11

Angular 2 で動的コンポーネントを作成する際に、新しく作成したコンポーネントを DOM に追加するには、このプロセスが必要であることがわかりまし た。ViewContainerRef

そして、これらの動的に作成されたコンポーネントに渡す@Inputと、上記の2番目のリンクとここ@Outputに答えが見つかりました。

ただし、 などのさまざまな形状コンポーネントを返す関数を含む という名前のサービスを作成する場合、shape.serviceDOMの場所を指定せずにこのサービスがコンポーネントを作成する方法と、container-component がこの返されたコンポーネントを受け取る方法がわかりません (おそらくそのタイプはサービスから) になり、指定された DOM コンテナー コンポーネントに注入されます。@InputbgColorComponentRef

たとえば、サービスには次のメソッドが含まれます。

getCircle(bgColor:string): ComponentRef<Circle> {
    let circleFactory = componentFactoryResolver.resolveComponentFactory(CircleComponent);
    let circleCompRef = this.viewContainerRef.createComponent(circleFactory);
    circleCompRef.instance.bgColor = bgColor;

    return circleCompRef;
}

ここで最初の疑問が生じthis.viewContainerRefます。インポートする理由ViewContainerRefは、コンポーネントを動的に作成するためです。

2 番目の質問は、container-componentcomponentRefがサービスから特定の入力を受け取った後、DOM にどのように注入するかです。

更新: 上記の私の質問は十分に具体的ではなかったと思います。私は次のような状況にいます:

  1. 親コンポーネントはサービスを呼び出し、componentRef/s を取得します。
  2. componentRef/s を含むオブジェクトを他のデータと一緒に作成し、それらの作成されたオブジェクトを配列に格納します
  3. として子に渡します@Input
  4. そして、各子が componentRef をその DOM に注入し、オブジェクト内の残りのデータを別の方法で使用できるようにします。

つまり、コンポーネントを呼び出しているサービスには、それらの componentRef がどこに注入されるかがわからないということです。つまり、いつでもどこでも注入できる独立したコンポーネント オブジェクトが必要です。

私はすでに rumTimeCompiler ソリューションを数回読んでいますが、それが実際にどのように機能するかはよくわかりません。viewContainerRefを使ったコンポーネント作成に比べて手間がかかりすぎるようです。他に解決策が見つからない場合は、さらに掘り下げます...

4

3 に答える 3

9

私のような誰かが今日でもシンプルで明確な解決策を探している場合-ここにあります。@angular/cdk https://github.com/angular/components/tree/master/src/cdkから入手し 、簡単なサービスを作成しました。

import {
    Injectable,
    ApplicationRef,
    ComponentFactoryResolver,
    ComponentRef,
    Injector,
    EmbeddedViewRef
} from '@angular/core';

export type ComponentType<T> = new (...args: any[]) => T;

@Injectable({
    providedIn: 'root'
})
export class MyService {

    constructor(
        private _appRef: ApplicationRef,
        private _resolver: ComponentFactoryResolver,
        private _injector: Injector
    ) { }

    private _components: ComponentRef<any>[] = [];

    add<T>(
        component: ComponentType<T> | ComponentRef<T>,
        element?: Element | string
    ): ComponentRef<T> {
        const componentRef = component instanceof ComponentRef
            ? component
            : this._resolver.resolveComponentFactory(component).create(this._injector);
        this._appRef.attachView(componentRef.hostView);
        if (typeof element === 'string') {
            element = document.querySelector(element);
        }
        if (!element) {
            element = document.body;
        }
        element.appendChild(
            (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement
        );
        this._components.push(componentRef);
        return componentRef;
    }

    remove(dialog: number | ComponentRef<any>): boolean {
        let componentRef;
        if (typeof dialog === 'number' && this._components.length > dialog)  {
            componentRef = this._components.splice(dialog, 1)[0];
        }
        else {
            for (const cr of this._components) {
                if (cr === dialog) {
                    componentRef = cr;
                }
            }
        }
        if (componentRef) {
            this._remove(componentRef);
            return true;
        }
        return false;
    }

    private _remove(componentRef: ComponentRef<any>) {
        this._appRef.detachView(componentRef.hostView);
        componentRef.destroy();
    }

    clear() {
        while (this._components.length > 0) {
            this._remove(this._components.pop());
        }
    }

    getIndex(componentRef: ComponentRef<any>): number {
        return this._components.indexOf(componentRef);
    }

}

ComponentClass または ComponentRef をaddElement または任意の DOM 要素を指す任意の querySelector 文字列を 2 番目の引数として渡すことができます (または何も指定しない場合は、body に接続する必要があると想定されます)。

const cr = this._service.add(MyComponent); // will create MyComponent and attach it to document.body or
const cr = this._service.add(MyComponent, this.someElement); // to attach to Element stored in this.someElement or
const cr = this._service.add(MyComponent, 'body div.my-class > div.my-other-div'); // to search for that element and attach to it
const crIndex = this._service.getIndex(cr);
cr.instance.myInputProperty = 42;
cr.instance.myOutputEmitter.subscribe(
    () => {
        // do something then for example remove this component
        this._service.remove(cr);
    }
);
this._service.remove(crIndex); // remove by index or
this._service.remove(cr); // remove by reference or
this._service.clear(); // remove all dynamically created components

PS動的コンポーネントをentryComponents: []のに追加することを忘れないでください@NgModule

于 2019-11-08T11:58:58.747 に答える