简体   繁体   English

如何呈现实现接口的组件?

[英]How can I render a component that implements an interface?

Let's say I got an interface (or an actual component) ListItemRenderer and a component MyRendererComponent that implements that interface (or extends the base component) 假设我有一个接口(或实际组件) ListItemRenderer和一个实现该接口的组件MyRendererComponent (或扩展基本组件)

@Component({
    selector: 'my-renderer',
    template: '<div>My renderer</div>'
})
export class MyRendererComponent implements ListItemRenderer {
    ...
}

I'd like to pass this concrete implementation to another component, eg 我想将这个具体的实现传递给另一个组件,例如

@Component({
    selector: 'my-list',
    template: `
        <ul [renderer]="renderer" [items]="items">
            <li *ngFor="let item of items">
                <!-- what goes here? -->
            </li>
        </ul>
    `
})
export class MyList {
    @Input() renderer: ListItemRenderer;
    @Input() items: any[];
    ...
}

Obviously, the parent component would have a public property renderer of type ListItemRenderer . 显然,父组件将具有ListItemRenderer类型的公共属性renderer The question is, how do I go about using that component in my <li> (see " what goes here? " above)? 问题是,如何在我的<li>使用该组件(参见上文中的“ 内容? ”)?

To add components dynamically using *ngFor you need something like the dcl-wrapper explained in https://stackoverflow.com/a/36325468/217408 ( DynamicComponentLoader is deprecated in favor of ViewContainerRef.createComponent() but I didn't try to introduce another name for the wrapper component.) 要使用*ngFor动态添加组件,需要像https://stackoverflow.com/a/36325468/217408中所述的dcl-wrapper (不推荐使用DynamicComponentLoader ,而不支持ViewContainerRef.createComponent()但我没有尝试介绍包装器组件的另一个名称。)

@Component({
  selector: '[dcl-wrapper]', // changed selector in order to be used with `<li>`
  template: `<div #target></div>`
})
export class DclWrapper {
  @ViewChild('target', {read: ViewContainerRef}) target;
  @Input() type;
  cmpRef:ComponentRef;
  private isViewInitialized:boolean = false;

  constructor(private resolver: ComponentResolver) {}

  updateComponent() {
    if(!this.isViewInitialized) {
      return;
    }
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }
   this.resolver.resolveComponent(this.type).then((factory:ComponentFactory<any>) => {
      this.cmpRef = this.target.createComponent(factory)
    });
  }

  ngOnChanges() {
    this.updateComponent();
  }

  ngAfterViewInit() {
    this.isViewInitialized = true;
    this.updateComponent();  
  }

  ngOnDestroy() {
    if(this.cmpRef) {
      this.cmpRef.destroy();
    }    
  }
}

and use it like 并使用它

@Component({
    selector: 'my-list',
    template: `
        <ul  [items]="items">
            <li *ngFor="let item of items" dcl-wrapper [type]="renderer" ></li>
        </ul>
    `
})
export class MyList {
    @Input() renderer: ListItemRenderer;
    @Input() items: any[];
    ...
}

The answer from Günter Zöchbauer is spot-on, here's a more complete code sample in case anybody else should encounter this problem (Angular2 RC1). GünterZöchbauer的答案很明显,这里有一个更完整的代码示例,以防任何人遇到这个问题(Angular2 RC1)。

app.component.ts app.component.ts

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

import { DynamicListComponent } from './dynamic-list.component';
import { TwoRendererComponent } from './two-renderer.component';
import { Renderer } from './renderer';

@Component({
    selector: 'app',
    template: `
        <h2>Dynamic List</h2>
        <dynamic-list [items]="items" [renderer]="renderer"></dynamic-list>
    `,
    directives: [
        DynamicListComponent
    ]
})
export class AppComponent {
    items: string[] = [ 'one', 'two', 'three' ];
    renderer: Renderer;
    constructor() {
        this.renderer = TwoRendererComponent;
    }
}

renderer.ts renderer.ts

export class Renderer {
}

dynamic-list.component.ts 动态list.component.ts

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

import { Renderer } from './renderer';
import { DclWrapperComponent } from './dcl-wrapper.component';

@Component({
    selector: 'dynamic-list',
    template: `
        <ul>
            <li *ngFor="let item of items" dcl-wrapper [type]="renderer">
            </li>
        </ul>
    `,
    directives: [
        DclWrapperComponent
    ]
})
export class DynamicListComponent {
    @Input() items: string[] = [];
    @Input() renderer: any;
}

one-renderer.component.ts 一renderer.component.ts

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

import { Renderer } from './renderer';

@Component({
    selector: 'one-renderer',
    template: '<div>ONE</div>'
})
export class OneRendererComponent implements Renderer {
}

two-renderer.component.ts 两renderer.component.ts

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

import { Renderer } from './renderer';

@Component({
    selector: 'two-renderer',
    template: '<div>TWO</div>'
})
export class TwoRendererComponent implements Renderer {
}

dcl-wrapper.component.ts DCL-wrapper.component.ts

import {
    Component,
    ViewChild,
    ViewContainerRef,
    ComponentRef,
    ComponentResolver,
    ComponentFactory,
    Input
} from '@angular/core';

import { Renderer } from './renderer';

@Component({
    selector: '[dcl-wrapper]',
    template: `<div #target></div>`
})
export class DclWrapperComponent {
    @ViewChild('target', { read: ViewContainerRef }) target;
    @Input() type: any;
    @Input() input: string;
    cmpRef: ComponentRef<Renderer>;
    private isViewInitialized: boolean = false;

    constructor(private resolver: ComponentResolver) { }

    updateComponent() {
        if (!this.isViewInitialized) {
            return;
        }
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
        this.resolver.resolveComponent(this.type).then((factory: ComponentFactory<any>) => {
            this.cmpRef = this.target.createComponent(factory)
        });
    }
    ngOnChanges() {
        this.updateComponent();
    }
    ngAfterViewInit() {
        this.isViewInitialized = true;
        this.updateComponent();
    }
    ngOnDestroy() {
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
    }
}

Here's what the resulting DOM looks like: 以下是生成的DOM的样子:

<app>
    <h2>Dynamic List</h2>
    <dynamic-list>
        <ul>
            <!--template bindings={}-->
            <li dcl-wrapper="">
                <div></div>
                <two-renderer _ngcontent-gce-4="">
                    <div>TWO</div>
                </two-renderer>
            </li>
            ...
        </ul>
    </dynamic-list>
</app>

If you need to pass a parameter, you'll have to pass it to DclWrapperComponent like so: 如果需要传递参数,则必须将其传递给DclWrapperComponent如下所示:

dcl-wrapper.component.ts DCL-wrapper.component.ts

export class DclWrapperComponent {
    @ViewChild('target', { read: ViewContainerRef }) target;
    @Input() type: any;
    @Input() input: string;  // <-- the parameter
    cmpRef: ComponentRef<Renderer>;
    private isViewInitialized: boolean = false;

    constructor(private resolver: ComponentResolver) { }

    updateComponent() {
        if (!this.isViewInitialized) {
            return;
        }
        if (this.cmpRef) {
            this.cmpRef.destroy();
        }
        this.resolver.resolveComponent(this.type).then((factory: ComponentFactory<any>) => {
            this.cmpRef = this.target.createComponent(factory);
            this.cmpRef.instance.input = this.input;  // <-- pass value to the newly created component
        });
    }
    ...

Here's an example implementation: 这是一个示例实现:

to-uppercase-renderer.component.ts 到大写,renderer.component.ts

import { Component, Input, OnInit } from '@angular/core';

import { Renderer } from './renderer';

@Component({
    selector: 'to-uppercase-renderer',
    template: '<div>{{output}}</div>'
})
export class ToUppercaseRendererComponent implements Renderer, OnInit {
    @Input() input: string;
    output: string;
    ngOnInit() {
        this.output = this.input.toUpperCase();
    }
}

Of course, the parameter should be declared on the base component as well, 当然,参数也应该在基础组件上声明,

renderer.ts renderer.ts

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

export abstract class Renderer {
    @Input() input: string;
}

You can then pass this parameter in your template as follows: 然后,您可以在模板中传递此参数,如下所示:

dynamic-list.component.ts 动态list.component.ts

...
@Component({
    selector: 'dynamic-list',
    template: `
        <ul>
            <li *ngFor="let item of items" dcl-wrapper [type]="renderer" [input]="item">
            </li>
        </ul>
    `,
    directives: [
        DclWrapperComponent
    ]
})
...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 您可以在实现另一个接口的组件的接口中键入字段吗? - Can you type a field in an interface to a Component that implements another interface? 我不明白为什么 Angular 组件 class 添加实现 onInit 接口 - I can't figure out why Angular component class add implements onInit interface 如何在 Angular 组件中呈现带有上下文的模板? - How can I render template with context in an Angular component? 如何从实现 ControlValueAccessor 的组件获取对 FormControl 的引用? - How do I get reference to the FormControl from a component that implements ControlValueAccessor? 如何将Angular2组件渲染为字符串? - How can I render an Angular2 component to a string? 如何使用Angular数据绑定来渲染组件? - How can I use Angular data binding to render component? 我可以在 html 组件中使用接口,但不能在 typescript 中使用 - I can use an interface in the html component but not in the typescript 如何将 ngx-quill 编辑器内容从我的数据库渲染到视图组件? - How can i render ngx-quill editor content from my database to a view component? 如何使用@ContentChildren并仅呈现特定的子组件而不是所有子组件? - How can I use @ContentChildren and only render a specific child component instead of all the children? 如何在我的角度组件中动态呈现带有样式和* ngFor的HTML选择框? - How can I dynamically render HTML select box with styles and *ngFor in my angular component?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM