繁体   English   中英

Angular-服务注入动态组件?

[英]Angular - Service injecting dynamic component?

我有一个工作代码,该代码通过服务将任何组件注入HTML:

ModalWindow.ts:

@Component({
  selector: 'modal-window'
  template: `
    <div class="modal-dialog" role="document">
        <div class="modal-content"><ng-content></ng-content></div>
    </div>
  `
})
export class ModalWindow {
}

Modalcontent.ts:

@Component({
  selector: 'modal-content'
  template: `
    I'm beeing opened as modal!
  `
})
export class ModalContent {
}

ModalService.ts:

/*1*/   @Injectable()
/*2*/   export class ModalService {
/*3*/     
/*4*/     constructor(private _appRef: ApplicationRef, private _cfr: ComponentFactoryResolver, private _injector: Injector) {
/*5*/     }
/*6*/     
/*7*/     open(content: any) {
/*8*/       const contentCmpFactory = this._cfr.resolveComponentFactory(content);
/*9*/       const windowCmpFactory = this._cfr.resolveComponentFactory(ModalWindow); 
/*10*/       
/*11*/       const contentCmpt = contentCmpFactory.create(this._injector);
/*12*/       const windowCmpt = windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);
/*13*/       
/*14*/       document.querySelector('body').appendChild(windowCmpt.location.nativeElement);
/*15*/       
/*16*/       this._appRef.attachView(contentCmpt.hostView);
/*17*/       this._appRef.attachView(windowCmpt.hostView);
/*18*/     }
/*19*/   }

应用程式:

@Component({
  selector: 'my-app',
  template: `
    <button (click)="open()">Open modal window</button>
  `,
})

结果(单击一个调用此服务方法的按钮时):

在此处输入图片说明

我已经知道contentCmpFactorywindowCmpFactory是什么( 第#8,9行

但是我不知道以后会发生什么。 关于第11行,第12行-文档说“创建一个新组件”。

问题:

1行#12: [[contentCmpt.location.nativeElement]]做什么? 文档说它的类型是projectableNodes?: any[][] -它们是什么意思?)

2行14号: [[windowCmpt.location.nativeElement]]做什么?

3行#16,#17:如果我已经执行appendChild ,为什么又需要它们? docs说:附加视图,以便对其进行脏检查。-这样吗?)。

朋克

答案:

1) Angular使用ComponentFactory并使用给定的元素注入器和可投影节点数组创建组件实例

windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);

1.1当角度将解决依赖关系时将使用元素注入器

const value = startView.root.injector.get(depDef.token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR);

这也是没有延迟加载的应用程序依赖项解析算法的简单说明。 使用延迟加载,看起来会更加复杂。

在此处输入图片说明

有关更多详细信息,请参见设计文档元素注入器与模块注入器

1.2可投影节点是节点元素,它们在组件模板的ng-content中被“投影”(包含)。

为了投射某些东西,我们的组件模板必须包含ng-content节点。

@Component({
  selector: 'modal-window',
  template: `
    <div class="modal-dialog">
      <div class="modal-content">
        <ng-content></ng-content> // <== place for projection
      </div>
    </div>
  ` 
})
export class ModalWindow {

我们可以在父组件模板中使用上述组件,如下所示:

<modal-window>
  <modal-content></modal-content>
  <div>Some other content</div>
</modal-window>

因此,我们的最终结果将如下所示:

<modal-window>
  <div class="modal-dialog">
    <div class="modal-content">
       <modal-content></modal-content> // our projectable nodes
       <div>Some other content</div>   // replaced ng-content
    </div>
  </div>
</modal-window>

因此,当我们传递可投影节点以创建方法时

windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement]]);

我们做与上述相同的事情。

我们正在获取创建的早期contentCmpt组件的host元素的引用( contentCmpt.location )。 这是modal-content元素。 然后,Angular将尽一切魔力将其投射到ng-content位置。

在上面的示例中,我添加了一个div

<modal-window>
  <modal-content></modal-content>
  <div>Some other content</div> <== here
</modal-window>

因此,实际代码应如下所示:

let div = document.createElement('div');
div.textContent = 'Some other content';
windowCmpFactory.create(this._injector, [[contentCmpt.location.nativeElement, div]]);

结论为什么projectableNodes是any [] []?

2)在下一行

document.querySelector('body').appendChild(windowCmpt.location.nativeElement);

我们正在引用在内存中创建的modal-window元素。 ComponentRef允许我们执行此操作,因为它在location getter中存储了对宿主元素的引用

export abstract class ComponentRef<C> {
  /**
   * Location of the Host Element of this Component Instance.
   */
  abstract get location(): ElementRef;

然后将它作为最后一个孩子插入document.body标记中。 所以我们在页面上看到它。

3)假设我们的ModalContent具有静态内容,还将执行一些交互操作。

@Component({
  selector: 'modal-content',
  template: `
    I'm beeing opened as modal! {{ counter }}
    <button (click)="counter = counter + 1">Increment</button>
  `
})
export class ModalContent {
  counter = 1;
}

如果我们删除

 this._appRef.attachView(contentCmpt.hostView);

那么我们的视图将不会在更改检测周期中进行更新,因为我们是通过ComponentFactory.create创建的,并且该视图不属于更改检测树中任何项目的一部分(与通过ViewContainerRef.createComponent创建的不同)。 Angular为此目的打开了API,我们可以轻松地将视图添加到根views https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L428 ,然后我们的组件将被更新在Application.tick期间https://github.com/angular/angular/blob/master/packages/core/src/application_ref.ts#L558

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM