在 Angular 应用程序中的何处编写 UI 逻辑?

Where to write UI logic in an Angular app?

假设我有一个有两个视图的 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
    }
}

鉴于我在两个单独的视图中使用它,这感觉有点正确,但它也感觉我在污染模型。

<div *ngIf="car.type == carType.HATCHBACK" class="hatchback-icon-class"> ... </div>
<div *ngIf="car.type == carType.COUPE" class="coupe-icon-class"> ... </div>
...

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

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

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

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

这是一个示例。

/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

想要显示法拉利 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();
    }
}

在此示例中,两个单独的组件加载到不同的汽车上。