angularjs – Root中的Angular2动态组件注入

前端之家收集整理的这篇文章主要介绍了angularjs – Root中的Angular2动态组件注入前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我正在寻找将已知/已定义组件注入应用程序根目录并将@Input()选项投影到该组件上的最佳方法.

需求

这对于在应用程序主体中创建模态/工具提示内容是必要的,这样溢出:隐藏/等不会扭曲位置或完全切断它.

研究

我发现我可以得到ApplicationRef,然后hackily向上遍历并找到ViewContainerRef.

constructor(private applicationRef: ApplicationRef) {
}

getRootViewContainerRef(): ViewContainerRef {
  return this.applicationRef['_rootComponents'][0]['_hostElement'].vcRef;
}

一旦我有了,我就可以在ref上调用createComponent,如:

appendNextToLocation<T>(componentClass: Type<T>,location: ViewContainerRef): ComponentRef<T> {
  const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass);
  const parentInjector = location.parentInjector;
  return location.createComponent(componentFactory,location.length,parentInjector);
}

但现在我已经创建了组件,但没有完成我的输入属性.为了实现这一点,我必须手动遍历我的选项并在appendNextToLocation的实例的结果上设置它们,如:

const props = Object.getOwnPropertyNames(options);
for(const prop of props) {
  component.instance[prop] = options[prop];
}

现在我意识到你可以做一些DI来注入选项,但这使得它在尝试用作普通组件时无法重复使用.下面是什么看起来像参考:

let componentFactory = this.componentFactoryResolver.resolveComponentFactory(ComponentClass);
let parentInjector = location.parentInjector;

let providers = ReflectiveInjector.resolve([
  { provide: ComponentOptionsClass,useValue: options }
]);

childInjector = ReflectiveInjector.fromResolvedProviders(providers,parentInjector);

return location.createComponent(componentFactory,childInjector);

所有这一切,以上所有实际上都有效,但有时候感觉有点哈哈.我也关注设置输入属性的生命周期时间,因为它在创建之后发生.

值得注意的参考文献

> https://github.com/angular/angular/issues/9293
> https://github.com/angular/angular/issues/6446

在2.3.0中,引入了attachView,它允许您将更改检测附加到ApplicationRef,但是,您仍然需要手动将元素附加到根容器.这是因为使用Angular2,其运行环境的可能性可能是web worker,universal,nativescript等,所以我们需要明确告诉它我们想要将它添加到视图的位置/方式.

下面是一个示例服务,它允许您动态插入组件并自动投影组件的输入.

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

/**
 * Injection service is a helper to append components
 * dynamically to a known location in the DOM,most
 * noteably for dialogs/tooltips appending to body.
 * 
 * @export
 * @class InjectionService
 */
@Injectable()
export class InjectionService {
  private _container: ComponentRef<any>;

  constructor(
    private applicationRef: ApplicationRef,private componentFactoryResolver: ComponentFactoryResolver,private injector: Injector) {
  }

  /**
   * Gets the root view container to inject the component to.
   * 
   * @returns {ComponentRef<any>}
   * 
   * @memberOf InjectionService
   */
  getRootViewContainer(): ComponentRef<any> {
    if(this._container) return this._container;

    const rootComponents = this.applicationRef['_rootComponents'];
    if (rootComponents.length) return rootComponents[0];

    throw new Error('View Container not found! ngUpgrade needs to manually set this via setRootViewContainer.');
  }

  /**
   * Overrides the default root view container. This is useful for 
   * things like ngUpgrade that doesn't have a ApplicationRef root.
   * 
   * @param {any} container
   * 
   * @memberOf InjectionService
   */
  setRootViewContainer(container): void {
    this._container = container;
  }

  /**
   * Gets the html element for a component ref.
   * 
   * @param {ComponentRef<any>} componentRef
   * @returns {HTMLElement}
   * 
   * @memberOf InjectionService
   */
  getComponentRootNode(componentRef: ComponentRef<any>): HTMLElement {
    return (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
  }

  /**
   * Gets the root component container html element.
   * 
   * @returns {HTMLElement}
   * 
   * @memberOf InjectionService
   */
  getRootViewContainerNode(): HTMLElement {
    return this.getComponentRootNode(this.getRootViewContainer());
  }

  /**
   * Projects the inputs onto the component
   * 
   * @param {ComponentRef<any>} component
   * @param {*} options
   * @returns {ComponentRef<any>}
   * 
   * @memberOf InjectionService
   */
  projectComponentInputs(component: ComponentRef<any>,options: any): ComponentRef<any> {
    if(options) {
      const props = Object.getOwnPropertyNames(options);
      for(const prop of props) {
        component.instance[prop] = options[prop];
      }
    }

    return component;
  }

  /**
   * Appends a component to a adjacent location
   * 
   * @template T
   * @param {Type<T>} componentClass
   * @param {*} [options={}]
   * @param {Element} [location=this.getRootViewContainerNode()]
   * @returns {ComponentRef<any>}
   * 
   * @memberOf InjectionService
   */
  appendComponent<T>(
    componentClass: Type<T>,options: any = {},location: Element = this.getRootViewContainerNode()): ComponentRef<any> {

    let componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass);
    let componentRef = componentFactory.create(this.injector);
    let appRef: any = this.applicationRef;
    let componentRootNode = this.getComponentRootNode(componentRef);

    // project the options passed to the component instance
    this.projectComponentInputs(componentRef,options);

    appRef.attachView(componentRef.hostView);

    componentRef.onDestroy(() => {
      appRef.detachView(componentRef.hostView);
    });

    location.appendChild(componentRootNode);

    return componentRef;
  }
}

猜你在找的Angularjs相关文章