Как отобразить ng-шаблоны из списка - PullRequest
0 голосов
/ 18 сентября 2018

У меня есть AppComponent, который содержит список ShapeComponents.Я реализовал некоторые компоненты, которые расширяют ShapeComponent как LineComponent, CircleComponent, RectangleComponent.У каждого из них есть свой ng-template с #shapeTemplate.В моем app.component.html я хочу перебрать список ShapeComponents и отобразить каждый ng-template (из LineComponent, CircleComponent и т. Д.).

, поэтому у меня есть

shapes: ShapeComponent[] = []

, который содержит LineComponent, CircleComponent и т.д.1024 *

Ответы [ 4 ]

0 голосов
/ 19 сентября 2018

Я нашел решение. На самом деле я нашел отличный пост на github

https://github.com/shivs25/angular5-canvas-drawer. Я использовал это решение для реализации своего собственного.

Так что все кредиты достаются Билли Шиверсу. Молодец.

Вот решение

Настройки для линии и окружности могут быть установлены динамически, ниже приведен только пример линии и окружности

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
}

Компонент ShapeHolder

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

<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>

Приложение appponpon

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

Надеюсь, вы найдете этот ответ полезным и сможете использовать его в своем приложении. Идея заключается в создании динамических шаблонов просмотров

0 голосов
/ 18 сентября 2018

В этом случае лучший и официально рекомендованный подход - использовать Динамические формы .

В документации вы найдете несколько полезных советов

0 голосов
/ 18 сентября 2018

Я недавно сделал нечто подобное.Вот окончательный stackblitz

Сначала я создаю ShapeComponent

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  {}

Оба компонента расширяют ShapeComponent и предоставляют его в соответствии с самими собой.Так что, когда кто-нибудь попытается ввести ShapeComponent, он получит 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 s.

Наконец, давайте использовать все это в AppComponent

<shape-holder>
  <circle>
    Custom Circle content
  </circle>
  <line>
    Custom Line content
  </line>
</shape-holder>

Выход

This is circle and its content: Custom Circle content
This is line and its content: Custom Line content
0 голосов
/ 18 сентября 2018

Существует решение для отображения компонентов, но оно довольно сложное, и я не рекомендую.

Решение в "угловом стиле" для вашей проблемы таково:

  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>

См. полный рабочий пример на стеке .

...