如何从列表显示ng-templates

时间:2018-09-18 04:34:58

标签: angular ng-template

我有一个AppComponent,其中包含ShapeComponents的列表。我实现了一些扩展ShapeComponent的组件,例如LineComponentCircleComponentRectangleComponent。他们每个人都有自己的ng-template#shapeTemplate。 在我的app.component.html中,我要遍历ShapeComponents的列表并显示每个ng-template(来自LineComponentCircleComponent等)。

所以我有

shapes: ShapeComponent[] = []

其中包含LineComponent,CircleComponent等

我想要这样的东西

<div *ngFor="let shape of shapes">
    <!-- How to display the ng-template from for example LineComponent or Circle -->
</div>

我认为使用@ViewChildren或@ContentChildren会很有用,但不知道如何处理

4 个答案:

答案 0 :(得分:1)

有一种显示组件的解决方案,但这很复杂,不是我的建议。

针对您的问题的“角度样式”解决方案是这样的:

  1. 使用一系列模型对象(而非组件),其中包含子组件所需的所有信息。在此示例中,我们将其称为models
  2. 在每个模型的属性中存储形状的类型(避免使用typeOf)。让我们打电话给shape。如果愿意,还可以使用枚举。
  3. 迭代ngFor中的模型,并为每个模型创建一个组件。

HTML模板可能看起来像这样

<div *ngFor="let model of models">

    <!-- display the ng-template from for example LineComponent or Circle -->
    <line   [model]="model" *ngIf="model.shape === 'Line'"></line>
    <circle [model]="model" *ngIf="model.shape === 'Circle'"></circle>

</div>

请参阅完整的有效示例on stackblitz

答案 1 :(得分:1)

最近我做了类似的事情。这是最后的stackblitz

首先,我创建一个ShapeComponent

file1=$(cat dir.txt)
echo $file1

它的模板有一个test-dir ,以便我们可以引用它,还有import { Component, Input, ViewChild, TemplateRef } from '@angular/core'; @Component({ selector: 'shape', template: `<ng-template><ng-content></ng-content></ng-template>`, }) export class ShapeComponent { @ViewChild(TemplateRef) template: TemplateRef<any>; } ,以便该组件的使用者可以在其中投影其内容。

借助ng-template,您可以获得ng-content的引用以及由于@ViewChild(TemplateRef)而在其中的引用。

让我们创建一个ng-template

ng-content

LineComponent

@Component({
  selector: 'line',
  template: `<ng-template>
    This is line and its content: <ng-content></ng-content>
  </ng-template>`,
  providers: [{
    provide: ShapeComponent,
    useExisting: forwardRef(() => LineComponent)
  }]
})
export class LineComponent extends ShapeComponent  {}

两个组件都扩展CircleComponent并根据自己提供。这样,只要有人尝试注入@Component({ selector: 'circle', template: `<ng-template> This is circle and its content: <ng-content></ng-content> </ng-template>`, providers: [{ provide: ShapeComponent, useExisting: forwardRef(() => CircleComponent) }] }) export class CircleComponent extends ShapeComponent {} ,他们都会得到ShapeComponentShapeComponent

最后,让我们创建一个LineComponent,它将所有这些粘合在一起

ShapeComponent

您可以将ShapeHolderComponent@Component({ selector: 'shape-holder', template: ` <div *ngFor="let child of children"> <ng-container *ngTemplateOutlet="child.template"></ng-container> </div> `, }) export class ShapeHolderComponent { @ContentChildren(ShapeComponent) children: QueryList<ShapeComponent>; } 一起列出。由于每个ShapeComponent都提供了自己的名称,因此我们可以获得它们的列表并使用它们的ContentChildren

最后,让我们在ShapeComponent

中使用所有这些
template

输出为

AppComponent

答案 2 :(得分:0)

在这种情况下,最好也是官方建议的方法是使用Dynamic Forms

在文档中,您会找到一些有用的提示

答案 3 :(得分:0)

我找到了解决方案。实际上,我在github上找到了一个很棒的帖子

https://github.com/shivs25/angular5-canvas-drawer。我采用了这种解决方案来实现自己的解决方案。

因此所有功劳归Billy Shivers所有。干得好。

这是解决方案

直线和圆的设置可以动态设置,下面仅是直线和圆的示例

CircleComponent和HTML模板

import { Component } from '@angular/core';
import { ShapeComponent } from '../shape/shape.component';

@Component({
    selector: 'app-circle',
    templateUrl: './circle.component.html',
    styleUrls: ['./circle.component.css']
})
export class CircleComponent extends ShapeComponent {

    constructor() {
        super('circle');
    }
}

html

<ng-template #elementTemplate>
    <svg:circle [attr.cx]="50" [attr.cy]="50" [attr.r]="40" stroke="black" stroke-width="3" fill="red" />
</ng-template>>

LineComponent和HTML模板

import { Component } from '@angular/core';
import { ShapeComponent } from '../shape/shape.component';

@Component({
    selector: 'app-line',
    templateUrl: './line.component.html',
    styleUrls: ['./line.component.css']
})
export class LineComponent extends ShapeComponent {

    constructor() {
        super('line');
        console.log('linecomponent:constructor');
    }

}

html

<ng-template #elementTemplate>
    <svg:line [attr.x1]="100" [attr.y1]="100" [attr.x2]="200" [attr.y2]="200" style="stroke:#006600; stroke-width:1px" />
</ng-template>>

ShapeComponent和HTML

import { Component, OnInit, ViewChild, TemplateRef, AfterViewInit } from '@angular/core';

@Component({
    selector: 'app-shape',
    templateUrl: './shape.component.html',
    styleUrls: ['./shape.component.css']
})
export class ShapeComponent implements OnInit, AfterViewInit {
    shapeType: string;
    visible: boolean = true;

    id: string = 'unknown';

    @ViewChild('elementTemplate')
    elementTemplate: TemplateRef<any>;

    constructor(shapeType: string) {
        console.log('shapecomponent constructor :', shapeType);
        this.shapeType = shapeType;
    }

    setid(value: string): void {
        this.id = value;
    }

    ngOnInit() {
        console.log('ShapeComponent ngOnInit()');
    }

    ngAfterViewInit(): void {
        console.log('!!!!!!!!! ShapeComponent ngAfterViewInit: ', this.elementTemplate);
    }

}

html:无

组件类型的枚举

export enum ShapeTypes {
    Line,
    Circle,
    Rectangle
}

ShapeHolderComponent

import { Component, OnInit, ViewChild, TemplateRef, AfterViewInit } from '@angular/core';

import { ShapeComponent } from '../shape/shape.component';
import { LineComponent } from '../line/line.component';
import { CircleComponent } from '../circle/circle.component';
import { ShapeTypes } from '../model/shape-types';

@Component({
    selector: 'app-shapeholder',
    templateUrl: './shapeholder.component.html',
    styleUrls: ['./shapeholder.component.css']
})
export class ShapeholderComponent implements OnInit, AfterViewInit {

    @ViewChild('elementTemplate')
    elementTemplate: TemplateRef<any>;

    shapes: ShapeTypes[];

    constructor() {
        this.shapes = [];
        this.shapes.push(ShapeTypes.Line);
        this.shapes.push(ShapeTypes.Circle);
        console.log('shapeholder shapes :', this.shapes);
    }

    ngOnInit() {
        console.log('ShapeHolderComponent : ngOnInit()');
    }

    ngAfterViewInit(): void {
        console.log('!!!!!!!!! ShapeHolder ngAfterViewInit: ', this.elementTemplate);
    }

}

html,为svg在CSS中设置宽度的高度

<svg>
    <ng-container *ngFor="let shape of shapes; let i = index">
        <ng-container svg-dynamic [componentData]="shape">
        </ng-container>
    </ng-container>
</svg>

其中最重要的部分是指令

import { Directive, Input, ViewContainerRef, Injector, ComponentFactoryResolver } from '@angular/core';
import { ShapeComponent } from './shape/shape.component';
import { LineComponent } from './line/line.component';
import { CircleComponent } from './circle/circle.component';
import { ShapeTypes } from './model/shape-types';

@Directive({
    selector: '[svg-dynamic]'
})
export class SvgDynamicDirective {

    constructor(private _viewContainerRef: ViewContainerRef, private _resolver: ComponentFactoryResolver) {

    }

    @Input() set componentData(data: ShapeTypes) {
        console.log('set componentdata : ', data);

        let injector = Injector.create([], this._viewContainerRef.parentInjector);
        console.log('injector:', injector);
        let factory = this._resolver.resolveComponentFactory(this.buildComponent(data));
        console.log('factory:', factory);
        let component = factory.create(injector);
        console.log('component:', component);
        let c: ShapeComponent = <ShapeComponent>component.instance;

        console.log('viewContainerRef:', this._viewContainerRef);
        console.log('elementTemplate:', c.elementTemplate);
        this._viewContainerRef.clear();
        this._viewContainerRef.createEmbeddedView(c.elementTemplate);
    }

    private buildComponent(data: ShapeTypes): any {
        switch (data) {
            case ShapeTypes.Line:
                return LineComponent;
            case ShapeTypes.Circle:
                return CircleComponent;
        }
        return null;
    }

}

和app.component html

<div style="text-align:center">
    <h1>
        Welcome to {{ title }}!
    </h1>
    <app-shapeholder></app-shapeholder>
</div>

app.component

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'demo1';
}

还有app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { ShapeComponent } from './shape/shape.component';
import { LineComponent } from './line/line.component';
import { ShapeholderComponent } from './shapeholder/shapeholder.component';
import { SvgDynamicDirective } from './svg-dynamic.directive';
import { CircleComponent } from './circle/circle.component';

@NgModule({
    entryComponents: [
        LineComponent,
        ShapeComponent,
        CircleComponent
    ],
    declarations: [
        AppComponent,
        LineComponent,
        ShapeComponent,
        CircleComponent,
        ShapeholderComponent,
        SvgDynamicDirective,
    ],
    imports: [
        BrowserModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

还有我的应用的最终屏幕截图

enter image description here

我希望这个答案有用,并且可以在您自己的应用中使用。这个想法是创建动态模板视图