【问题标题】:Where to write UI logic in an Angular app?在 Angular 应用程序中在哪里编写 UI 逻辑?
【发布时间】:2021-02-18 07:13:42
【问题描述】:

假设我有一个有两个视图的 Angular 应用程序。第一个视图显示某个对象的预览,比如一辆车,另一个视图显示该车的详细信息。汽车模型类似于:

export class Car {
    model: string;
    type: CarTypeEnum;
    ...;
}

假设我希望两个视图都向我显示一个代表汽车类型的图标。逻辑是:

switch(someCar.type) {
    case CarTypeEnum.HATCHBACK: return icons.hotHatch;
    case CarTypeEnum.SEDAN: return icons.sedan;
    case CarTypeEnum.SUV: return icons.suv;
    case CarTypeEnum.COUPE: return icons.coupe;
    case CarTypeEnum.VAN: return icons.van;
    case CarTypeEnum.WAGON: return icons.wagon;
}

这个根据汽车类型获取图标的逻辑应该去哪里?

  • 我应该在汽车模型类中创建一个属性吗?
export class Car {
    model: string;
    type: CarTypeEnum;
    ...;

    get typeIcon() { 
        // switch goes here
    }
}

这感觉有点对,因为我在两个单独的视图中使用它,但我也觉得我在污染模型。

  • 我应该将此代码添加到方法中并将其复制到两个视图组件类中吗?如果我必须在 10 个视图中使用此代码,我会在它们各自的组件中重复此逻辑 10 次吗?

  • 我是否应该创建一些包含具有此逻辑的方法的静态辅助类,将汽车作为参数,然后在每个组件类中调用它?

  • 我应该将逻辑直接添加到视图中吗?

<div *ngIf="car.type == carType.HATCHBACK" class="hatchback-icon-class"> ... </div>
<div *ngIf="car.type == carType.COUPE" class="coupe-icon-class"> ... </div>
...
  • 是否应该生成一个将汽车类型作为输入的 Angular 组件?这将是可重用的,但只是为了渲染这个图标有点过头了,不是吗?

【问题讨论】:

    标签: angular separation-of-concerns


    【解决方案1】:

    如果您要在整个应用程序中使用该图标,我建议您创建一个带有输入绑定的组件(正如您在上一段中所说的那样)。

    Angular 确实鼓励您按照 KISS 和 SOLID 原则制作易于测试和重用的演示组件。

    本文中的更多信息:https://indepth.dev/posts/1066/presentational-components-with-angular

    【讨论】:

      【解决方案2】:

      我的首选策略是使用服务。您可以将其创建为单例,以便在加载汽车时它可用于所有组件,或者您可以单独加载它,以便每个组件可以加载不同的汽车。

      这是一个示例。

      /services/car.service.ts

      从您的数据源加载汽车并为您的所有组件提供标准化接口的服务

      import { Injectable } from '@angular/core';
      import { BehaviorSubject, Observable } from 'rxjs';
      
      // If you want this to behave as a singleton, add {providedIn: 'root'} to @Injectable
      @Injectable()
      export class CarService {
          private _car = new BehaviorSubject<any>(null);
          private _carSnapshot;
          
          // Method called by your components to load a specific car
          load(carId: string): Promise<any> {
              return this.getCarInfoFromWherever(carId);
          }
      
          // Returns an observable of the currently loaded car
          car$(): Observable {
              return this._car.asObservable();
          }
      
          // Method to retrieve the car data from whatever datasource you're using
          getCarInfoFromWherever(carId: string): Promise<any> {
              return new Promise(async (resolve: any) => {
                  // Retrieve the car information from wherever it is such as a database.
                  const carInfo = await DbGetCarInfo(carId);
      
                  // Set an object for easy access to current vehicle
                  this._carSnapshot = carInfo;
      
                  // Update your observable
                  this._car.next(carInfo);
              });
          }
      
          // Example of abstraction to retrieve car attributes
          getType(): string {
            if (this._carSnapshot)
                return this._carSnapshot['type'];
      
            return null;
          }
      }
      

      /components/main/main.component.ts

      想要显示福特 Pinto 的组件

      import { Component } from '@angular/core';
      import { distinctUntilChanged } from 'rxjs/operators';
      
      @Component({
          selector: 'app-main',
          templateUrl: './main.component.html',
          styleUrls: ['./main.component.scss']
      })
      export class MainComponent {
          private _subscription;
      
          constructor(
              // Inject the CarService into our component
              public carSvc: CarService
          ) {
              // Tell CarService which car to load
              this.carSvc.load('FordPinto').then();
          }
      
          ngOnInit(): void {
              // Subscribe to the car service observable
              this._subscription = this.carSvc.car$()
                  .pipe(distinctUntilChanged())
                  .subscribe((car: any) => {
                      // The car has been loaded, changed, do something with the data.
                      console.log("Car Type:", this.carSvc.getType());
                  });
          }
      
          // Unsubscribe from the CarService observable
          ngOnDestroy(): void {
              this._subscription.unsubscribe();
          }
      }
      

      /components/dashboard/dashboard.component.ts

      想要显示 Ferrari Testarossa 的组件

      import { Component, OnInit } from '@angular/core';
      import { distinctUntilChanged } from 'rxjs/operators';
      
      @Component({
          selector: 'app-dashboard',
          // Here's an example of using the observable in a template
          template: `<div>{{carSvc.car$() | json}}`,
          styleUrls: ['./dashboard.component.scss']
      })
      export class DashboardComponent implements OnInit, O {
      
          constructor(
              public carSvc: CarService
          ) {
              this.carSvc.load('FerrariTestarossa').then();
          }
      
          ngOnInit(): void {
              this.carSvc.car$()
                  .pipe(distinctUntilChanged())
                  .subscribe((car: any) => {
                      // Do something with the car information
                  });
          }
      
          // Unsubscribe from the CarService observable
          ngOnDestroy(): void {
              this._subscription.unsubscribe();
          }
      }
      

      在本例中,两个独立的组件分别加载到不同的汽车上。

      【讨论】:

        猜你喜欢
        • 2011-07-24
        • 1970-01-01
        • 2015-11-03
        • 1970-01-01
        • 2020-07-10
        • 2021-04-20
        • 2016-09-09
        • 1970-01-01
        • 2010-12-14
        相关资源
        最近更新 更多