【问题标题】:Angular 4 - How to display the template of the inheriting class componentAngular 4 - 如何显示继承类组件的模板
【发布时间】:2018-04-15 06:44:42
【问题描述】:

我正在尝试根据它们的类型显示项目(组件)的列表:

我有一个组件数组。 所有都继承自一个基类。 数组类型被定义为基类的类型。 我想用自己的模板而不是基本模板来显示数组(比如说项目列表)。

我试过了:

在 app.component.html 中:

<app-shape *ngFor="let shape of shapes"></app-shape>

在 app.component.ts 中:

shapes: ShapeComponent[] = [new CircleComponent(), new SquareComponent()];

我已经定义了 3 个组件:

export class ShapeComponent {
}

export class CircleComponent extends ShapeComponent{
}

export class SquareComponent extends ShapeComponent{
}

结果是我得到了一个形状列表。

Angular 支持这样的事情吗?

谢谢!

【问题讨论】:

    标签: html angular typescript


    【解决方案1】:

    声明式方法

    您可以使用ngComponentOutlet

    代码:

    shapes: ShapeComponent[] = [CircleComponent, SquareComponent];
    

    模板:

    <ng-container *ngFor="let shape of shapes">
        <ng-container *ngComponentOutlet="shape">
        </ng-container>
    </ng-container>
    

    ngComponentOutlet - 实例化单个 Component 类型并插入 将其主机视图转换为当前视图。 NgComponentOutlet 提供了一个 动态组件创建的声明式方法。

    NgComponentOutlet 需要一个组件类型,如果设置了一个假值 视图将被清除,任何现有组件都将被销毁。

    因此,模板中不需要硬代码。 *ngFor 将遍历代码中的组件类型数组

    更新

    不记得给AppModuleentryComponents添加动态渲染组件:

    @NgModule({
      imports:      [ BrowserModule, FormsModule ],
      declarations: [ AppComponent, HelloComponent, AComponent, BComponent ],
      entryComponents: [
        AComponent, BComponent 
      ],
      bootstrap:    [ AppComponent ]
    })
    export class AppModule { }
    

    StackBlitz Demo

    设置数据的命令式方法

    app-component.template:

    <ng-container #comps>
    
    </ng-container>
    

    通过ViewChild 装饰器访问#comps(ng-container) 视图并创建组件。 所以你不能初始化像b = new BComponent() 这样的组件。

    1. 首先需要解析组件工厂。
    2. 通过viewContainerRef'screateComponent方法初始化组件。它将reference返回给实例化的组件
    3. 通过引用,访问实例属性并根据需要更新任何数据

    app-component.ts:

     @ViewChild('comps', { read: ViewContainerRef }) comps: ViewContainerRef;
      constructor(private componentFactoryResolver: ComponentFactoryResolver) {
    
      }
    
    ngOnInit() {
        this.comps.clear();
        let aComponentFactory = this.componentFactoryResolver.resolveComponentFactory(this.compArr[0]);
        let aComponentRef = this.comps.createComponent(aComponentFactory);
        (<AComponent>aComponentRef.instance).name = 'A name';
    
        let bComponentFactory = this.componentFactoryResolver.resolveComponentFactory(this.compArr[1]);
    
        let bComponentRef = this.comps.createComponent(bComponentFactory);
        (<BComponent>bComponentRef.instance).name = 'B name';
      }
    

    StackBlitzh Demo

    【讨论】:

    • Doe 不起作用,我得到:“[Angular] 一个元素上不能有多个模板绑定。只使用一个名为 'template' 或前缀为 * 的属性”
    • 感谢@Yerkon,它成功了。虽然因为我有实例数组,而不是类型数组,但我只需要添加一个返回继承类型的方法 getType(ShapeComponent) 并将其设置为 ngComponentOutlet。此外,当我创建一个带参数的构造函数时,我遇到了一个异常,说不能实例化基类。我删除了它(很遗憾..),它就像一个魅力。
    • 对不起@Yerkon,我刚刚意识到这个解决方案存在问题,正在创建新实例而不是使用我的“形状”数组中的实例,这完全没有抓住重点。我丢失了所有数据:|。我错过了什么吗?
    • @Eibi,new instances are being created instead of using my instances 对。通过new Component() 创建组件并不意味着该组件是以角度创建的。如果要创建组件并将数据传递给它们。我建议您使用命令式方法angular.io/guide/dynamic-component-loader#resolving-components
    • 如何从继承类访问ViewContainerRef?由于装饰器不继承,我在这里有问题(无法读取未定义的属性“清除”):@ViewChild('licenceContent', { read: ViewContainerRef }) protected licenceContent: ViewContainerRef; 是基类中的声明,loadLicenceComponent() { this.licenceContent.clear();... 继承组件类中的错误。标记由继承的类共享 - 这是需要#licenceContent 的地方。从继承类的ngAfterViewInit 调用loadLicenceContent - 但没关系,因为问题在于装饰器。
    【解决方案2】:

    每个组件必须有单独的选择器。

    import {Component} from "@angular/core";
    
    @Component({
      selector: "app-shape" //maybe even is not needed here
    })
    class Shape {
    
    }
    
    @Component({
      selector: "app-rect"
    })
    class Rect extends Shape {
    
    }
    

    所以你可以做这样的事情。

    <div *ngFor="let shape of shapes">
      <app-rect *ngIf="//check if shape is rect"></app-rect>
      <app-circle *ngIf="//check if shape is circle"></app-circle>
    </div>
    

    【讨论】:

    • 我确实有单独的选择器。但我不想每次添加新类型时都添加代码。我基本上是在 WPF 中寻找 DataTemplate 的等价物,它知道选择继承的类 DataTemplate 而不使用显式执行它的额外代码。
    猜你喜欢
    • 2017-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多