问题描述
假设我有一个带有两个视图的 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();
}
}
在这个例子中,两个单独的组件加载到单独的汽车上。