在 Angular 应用程序中在哪里编写 UI 逻辑?

问题描述

假设我有一个带有两个视图的 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 确实鼓励您按照 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();
    }
}

在这个例子中,两个单独的组件加载到单独的汽车上。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...