有没有办法用客户模块中的组件替换Angular组件,就像在AngularJS中使用装饰器一样?

0aydgbwb  于 2023-04-10  发布在  Angular
关注(0)|答案(2)|浏览(136)

我正在为广泛的客户开发应用程序。不同的客户往往有不同的需求,在他们的UI定制。因此,我们想用客户特定的组件替换这些组件。不幸的是,这似乎是不可能的。有人可以帮助吗?
我们希望的情况:

  • 模块“基础”
  • 组件“调度程序”-显示模板中的几个组件
  • 组件'SchedulerEvent'(标记'scheduler-event')和一些基本数据
  • 模块“客户”
  • 具有客户特定数据的组件“CustomerSchedulerEvent”(标记“scheduler-event”)

在这种情况下,我们希望显示CustomerSchedulerEvent,而不是正常的SchedulerEvent。尽管代码以这种方式正确编译,但仍显示SchedulerEvent。
在旧的AngularJS代码中,有一个装饰器的概念,它可以替换整个指令/组件,这里描述:https://docs.angularjs.org/guide/decorators#directive-decorator-example。
有没有可能在现代Angular中也实现这种行为?!

ui7jx7zq

ui7jx7zq1#

虽然很麻烦,但至少有一个解决方案:
1.确保你有一个组件主机指令。我们稍后会用到它。

@Directive({
    selector: '[componentHost]',
})
export class ComponentHostDirective {
    constructor(readonly $viewContainerRef: ViewContainerRef) { }
}

1.创建返回要呈现的组件类型的服务:

import { Injectable, Type } from '@angular/core';
[...]
@Injectable()
export class TemplateComponentService extends TemplateComponentBaseService {
    getTemplate(): Type<LaneSubscriberSchedulingEventInformationTemplateBaseComponent> {
        // console.log('Our EventInformationTemplateService...');
        return LaneSubscriberSchedulingEventInformationTemplateComponent;
    }
}

在基本模块中注册如下:

@NgModule({
    ...
    providers: [
        EventInformationTemplateService,
        { provide: EventInformationTemplateBaseService, useExisting: EventInformationTemplateService }
    ]
})
export class BaseModule {
}

1.您可以替换的组件应如下所示:

import { AfterViewInit, Component, ComponentFactoryResolver, ElementRef, Type, ViewChild } from "@angular/core";
import { ComponentHostDirective } from "src/common/directives/component-host.directive";
import { AggregateServiceFactory } from "src/common/services/aggregates";
import { LaneSubscriberSchedulingEventInformationTemplateBaseComponent } from "src/production-planning/components/lane-subscriber-scheduling/event-information-template/event-information-template-base.component";
import { EventInformationTemplateBaseService } from "src/production-planning/components/lane-subscriber-scheduling/event-information-template/event-information-template-base.service";
import { moduleName } from "src/production-planning/production-planning.states";

@Component({
    selector: 'app-template',
    templateUrl: './template.component.html',

})
export class TemplateComponent extends TemplateBaseComponent implements AfterViewInit {
    componentType: Type<LaneSubscriberSchedulingEventInformationTemplateBaseComponent>;
    customComponent: boolean;

    @ViewChild(ComponentHostDirective, { static: true }) private _componentHost: ComponentHostDirective;

    constructor(
        $element: ElementRef,
        private readonly $componentFactoryResolver: ComponentFactoryResolver,
        private readonly _templateComponentService: TemplateComponentBaseService) {

        this.componentType = this._templateComponentService.getComponent();
        this.customComponent = !isNull(this.componentType) && this.componentType !== TemplateComponent;

        // console.group('TemplateComponentService.getComponent()');
        // console.log('Component type', this.componentType);
        // console.log('Component custom?', this.customComponent);
        // console.groupEnd();
    }

    // Component lifecycle events
    ngAfterViewInit(): void {
        if (this.customComponent === true) {
            const componentFactory = this.$componentFactoryResolver.resolveComponentFactory(this.componentType);
            this._componentHost.$viewContainerRef.clear();
            const componentRef = this._componentHost.$viewContainerRef.createComponent<LaneSubscriberSchedulingEventInformationTemplateBaseComponent>(componentFactory);
            componentRef.instance.event = this.event;
        }
    }
}

其模板文件如下:

<ng-container *ngIf="customComponent != true">
    <!-- TODO Display more information -->
    <strong>{{ event.title }}</strong>
</ng-container>
<ng-template componentHost></ng-template>

1.创建另一个类似上面的服务,并在应用的另一个模块中以相同的方式注册它。
正如你所看到的,我们使用 *ngIf隐藏原始组件内容,并使用ng-template上的组件宿主来在服务返回另一种类型而不是当前类型时在其位置呈现替换组件。选择这种奇怪路径的原因是标记将直接Map到我们的基本模板组件,并且不可替换。
这种情况的一个缺点是,组件主机指令,是一个ViewChild,只有在视图初始化后才可用,这是相当晚的。对于非常复杂的情况,这种解决方案可能因此导致一些不必要的计时问题,例如...
希望大家能提供更好的解决方案。

0md85ypi

0md85ypi2#

如果你是一个组件的作者,你想让用户只替换它,@DotBert的第一个答案可以用https://github.com/gund/ng-dynamic-component来改进

相关问题