首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何显示列表中的ng-template

如何显示列表中的ng-template
EN

Stack Overflow用户
提问于 2018-09-18 12:34:58
回答 4查看 3.7K关注 0票数 10

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

所以我有

代码语言:javascript
复制
shapes: ShapeComponent[] = []

其中包含LineComponent、CircleComponent等

我想要这样的东西

代码语言:javascript
复制
<div *ngFor="let shape of shapes">
    <!-- How to display the ng-template from for example LineComponent or Circle -->
</div>

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

EN

回答 4

Stack Overflow用户

发布于 2018-09-18 15:49:07

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

首先,我创建了一个ShapeComponent

代码语言:javascript
复制
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

代码语言:javascript
复制
@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

代码语言:javascript
复制
@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  {}

这两个组件都扩展了ShapeComponent,并根据自己的需要提供它。因此,每当有人尝试注入ShapeComponent时,他们都会得到一个LineComponentShapeComponent

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

代码语言:javascript
复制
@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>;
} 

您可以使用ContentChildren列出ShapeComponent。因为每个ShapeComponent都提供了自己,所以我们可以获得它们的列表并使用它们的template

最后,让我们在AppComponent中使用所有这些

代码语言:javascript
复制
<shape-holder>
  <circle>
    Custom Circle content
  </circle>
  <line>
    Custom Line content
  </line>
</shape-holder>

输出为

代码语言:javascript
复制
This is circle and its content: Custom Circle content
This is line and its content: Custom Line content
票数 4
EN

Stack Overflow用户

发布于 2018-09-18 13:59:44

有一个显示组件的解决方案,但它相当复杂,不是我的建议。

针对您的问题的"angular-style“解决方案是:

  1. 使用模型对象(而不是组件)列表,其中包含子组件所需的所有信息。在本例中我们称之为models
  2. 将形状的类型存储在每个模型的属性中(避免使用typeOf)。让我们调用if shape。如果在ngFor中对模型执行prefer.
  3. Iterate操作,并为每个模型创建组件,则还可以使用枚举。

HTML模板可能如下所示

代码语言:javascript
复制
<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
EN

Stack Overflow用户

发布于 2018-09-19 17:59:11

我找到了解决方案。实际上,我在github上发现了一篇很棒的文章

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

所以所有的功劳都归了比利·希弗斯。9~10成熟。

以下是解决方案

直线和圆的设置可以动态设置,下面只是直线和圆的一个例子

CircleComponent和HTML模板

代码语言:javascript
复制
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

代码语言:javascript
复制
<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模板

代码语言:javascript
复制
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

代码语言:javascript
复制
<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

代码语言:javascript
复制
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 :无

组件类型的枚举

代码语言:javascript
复制
export enum ShapeTypes {
    Line,
    Circle,
    Rectangle
}

The ShapeHolderComponent

代码语言:javascript
复制
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,在css中设置svg的宽度高度

代码语言:javascript
复制
<svg>
    <ng-container *ngFor="let shape of shapes; let i = index">
        <ng-container svg-dynamic [componentData]="shape">
        </ng-container>
    </ng-container>
</svg>

其中最重要的部分就是指令

代码语言:javascript
复制
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

代码语言:javascript
复制
<div style="text-align:center">
    <h1>
        Welcome to {{ title }}!
    </h1>
    <app-shapeholder></app-shapeholder>
</div>

The app.component

代码语言:javascript
复制
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

代码语言:javascript
复制
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 { }

以及我的应用程序的最终屏幕截图

我希望你发现这个答案很有用,并且可以在你自己的应用中使用它。其思想是创建动态模板视图

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52379006

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档