首页 > 解决方案 > Angular 8:如何为同一组件的多个实例提供不同的服务实例

问题描述

我正在尝试在 Angular 8 中创建可重用的基于 d3 的仪表板组件。我想创建可以打包在模块中并在不修改组件代码(甚至构造函数参数)的情况下重用的组件(如条形图)。我还想允许 x 个显示不同数据的兄弟实例。

我有一个工作组件,我已将其 api 分解为简单显示配置的输入参数和所有组件数据/交互所需的服务接口。我将该服务接口实现为抽象基类,并且组件将该基类作为构造函数参数(用户/开发人员无法在我的场景中修改构造函数参数或任何组件代码)。

这给我留下了一个问题——如何在不修改组件构造函数参数的情况下为不同的组件实例提供服务基类的不同实现。

我也已经尝试为条形图创建一个抽象基类,然后创建仅通过获取派生 barchartservice 实例不同的派生条形图实例,但问题是模板和样式不是从组件基类继承的。

export class BarChartComponent implements AfterViewInit
{
    ...

    public constructor(public service: BarChartService) {}

    public ngAfterViewInit() {
        ...

        this.service.dataInit$.subscribe(() => {
            let data = this.service.barChartData;
            this.drawChart(data);
        });

        this.service.dataRefresh$.subscribe(() => {
            let data = this.service.barChartData;
            this.drawChart(data);
        });
    }

    private drawChart(data): void {
        ...
    }
}

@Injectable()
export abstract class BarChartService {

    abstract barChartData: any;

    abstract dataInit$: Observable<boolean>;
    abstract dataRefresh$: Observable<boolean>;

    abstract barChartSelect(string): void;
    abstract barChartXValue: (d: any) => any;
    abstract barChartYValue: (d: any) => any;
}

最终,我只想要可以显示不同数据的可重用多实例组件。任何见解将不胜感激。

为清楚起见,这是 barChartService 派生类正在执行的操作(审批服务参数包含后端访问逻辑和跨仪表板共享的交叉过滤器):

@Injectable()
export class ApprovalsBarChartService implements BarChartService {

    private init = new Subject<boolean>();
    dataInit$ = this.init.asObservable();

    private refresh = new Subject<boolean>();
    dataRefresh$ = this.refresh.asObservable();

    public get barChartData(): any {
        return this.service.approvalsGroupedByApprover.all();
    }

    public barChartXValue = function (d) { return d.key; };
    public barChartYValue = function (d) { return d.value; };

    public constructor(public service: ApprovalService) {   

        this.service.init$.subscribe(() => {
            this.init.next(true);
        });

        this.service.refresh$.subscribe(() => {
          this.refresh.next(true);
        });
    } 

    public barChartSelect(processor: string): void {
        this.service.approvalsIndexedByApprover.filter(processor);
        this.service.selectedProcessor = processor;
        this.service.selectItems = +this.service.xfilter.groupAll().reduceCount().value();
        this.service.refreshCharts();
    }
}

标签: angulartypescript

解决方案


BarChartService真的需要服务吗?在我看来,如果您不需要在不同组件之间重用相同的服务实例,那么您就不需要服务。

因此,您可以在组件的构造函数中实例化“服务”,而不是注入它。所以而不是:

public constructor(public service: BarChartService) {}

你可以:

public service: BarChartService;
public constructor() {
    this.service = new BarChartService();
}

请注意,由于该类是抽象的,您需要实例化具体的子类。

现在,如果您真的想将服务中的不同实例注入到不同的组件中(例如,其中一些服务将在某些组件之间共享),那么您可以为每个实例提供不同的注入令牌,并在组件的构造函数来选择您想要的实例:

声明两个注入令牌:

export const MyToken1 = new InjectionToken<BarChartService>('MyToken1');
export const MyToken2 = new InjectionToken<BarChartService>('MyToken2');

为每个令牌分配不同的服务实例:

@NgModule(...) 
export class ... {
    ...
    providers: [
        {provide: MyToken1, useValue: new MyServiceInstance(1, 2, 3)},
        {provide: MyToken2, useValue: new MyServiceInstance(4, 5, 6)},
    ]
}

然后在组件中,选择要注入的服务:

public constructor(@Inject(MyToken1) public service: BarChartService) {}

推荐阅读