【问题标题】:Angular RC 2 - Dynamically Render Components from a json fileAngular RC 2 - 从 json 文件动态渲染组件
【发布时间】:2016-05-21 21:12:22
【问题描述】:

DynamicComponentLoader 已被弃用。我正在尝试构建一个父组件,它根据一组子组件在父组件内呈现子组件,这意味着每个用户都有一个设置文件,其中包含需要在父组件中的子组件。现在父母看起来像这样:

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

import { Child1Component } from '../children/child1.component'
import { Child2Component } from '../children/child2.component'
import { Child3Component } from '../children/child3.component'

import { ParentService } from './parent.service'

@Component({
  selector: 'parent',
  directives: [Child1Component, Child2Component, Child3Component],
  providers: [ParentService],
  template: `
    <style>
      .parent{
        background-image: linear-gradient(141deg, #8ecb45 0%, #97cd76 71%, #96d885 100%);
        width: 100%;
        height: 200px;
      }
    </style>
    <div class="parent"></div>
  `
})
export class ParentComponent {
  constructor(private parentService:ParentService, private children: any, viewContainer: ViewContainerRef, private componentResolver: ComponentResolver) {

    this.children = parentService.getChildren();

    for(var i = 0; i < children.length; i++) {
      this.componentResolver.resolveComponent(children[i].component)
        .then(componentFactory => {
          const ctxInjector = viewContainer.injector;
          return viewContainer.createComponent(componentFactory, 0, ctxInjector);
        })
    }

  }
}

现在,在 ParentComponent 构造函数中调用服务似乎引起了一些问题。但在我开始遍历 componentResolver 之前,我能够在父节点下附加一个组件,但不能在其中。

有谁知道构建动态父组件的更好方法?这是为了管理布局的仪表板类型。大多数示例都明确说明将加载多少组件。这对我不起作用。我已经看过一些关于如何完成这项工作的帖子,但到目前为止,我还没有看到任何在带有配置文件的 RC 上运行的东西。

这最接近我想要做的:http://plnkr.co/edit/jAmMZKz2VqponmFtPpes?p=preview 但它使用 dcl...

目前我在启动应用程序时遇到了一个我没有发现的错误:TypeError: Cannot read property 'query' of null

感谢您的帮助!

如果您想下载并尝试一下,请点击以下链接:

https://github.com/weswhite/ng2-layout

编辑/更新:这可能与孩子有关:我注射的任何东西。我不认为我这样做是正确的,仍在弄清楚那一点......

【问题讨论】:

  • 只有viewContainerRef.createComponent()添加的组件不需要在directives: [...]中列出。 stackoverflow.com/questions/36325212/… 是我对类似问题的回答。
  • 应该传入什么private children: any,
  • 我意识到现在私有的 any 是不正确的。您是否知道如何注入一些表示组件数组的对象?现在我正在尝试做一些更像是创建一个子类并在组件上有一个 children: Child[] 属性的事情。还没搞清楚呢
  • 不确定children: Child[] 应该通过什么。数组及其内容应该从哪里来?
  • parentService.getChildren();这将返回主组件中的组件列表

标签: angularjs angular angular2-components


【解决方案1】:

您可以使用带有子 ViewContainerRef 的 ComponentResolver 来动态创建组件并将它们加载到父组件中。

@Component({
  selector: 'parent',
  providers: [ParentService],
  template: `
    <style>
      .parent{
        background-image: linear-gradient(141deg, #8ecb45 0%, #97cd76 71%, #96d885 100%);
        width: 100%;
        height: 200px;
      }
    </style>
    <div #content></div>
   `
  })

export class ParentComponent {
  @ViewChild('content', { read: ViewContainerRef }) contentContainer: ViewContainerRef;

  children: Component[];

  constructor(
    private parentService:ParentService,
    private resolver: ComponentResolver) {
    this.children = parentService.getChildren();
  }

  ngOnInit() {
    this.children.forEach( (component, index) => this.loadComponent(component, index));
  }

  private loadComponent(component: Component, index: number) {
    return this
      .resolver
      .resolveComponent(component)
      .then( factory => 
        this.contentContainer.createComponent(factory, index, this.contentContainer.injector))
  }
}

我还升级了提供的 plunker 以使用最新的 @angular 和 ComponentResolver 而不是 DynamicComponentLoader: 笨蛋:http://plnkr.co/edit/Z6bXwBcwAnf4DSpNlU8H?p=preview

将 DCL 与 loadNextTolocation 一起使用:http://plnkr.co/edit/NYgJzz9UjrtGsNnO5WXS?p=info

P.S: 你得到 TypeError: Cannot read property 'query' of null, 因为你注入 children 的方式,你必须指定一个可注入类型。

另见Angular 2 dynamic tabs with user-click chosen components

【讨论】:

  • 看起来很棒!我走了一点不同的路线,但这也行!我喜欢你在不添加包装组件的情况下遵循我的初衷。
  • 是否可以将其更新到不再使用 ComponentResolver 的 RC5?我自己试了一下,我得到一个“没有找到...的组件工厂:控制台中的错误。
【解决方案2】:

我很确定你在问题中提到的错误信息是由这个参数引起的

private children: any,

Angular 无法在没有提供具体类型且没有 @Inject(...) 注释的情况下注入依赖项。

更新

能够以声明方式添加动态组件的包装类的完整示例

@Component({
  selector: 'dcl-wrapper',
  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();
    }    
  }
}

来自https://stackoverflow.com/a/36325468/217408

Plunker example RC.1

Plunker example beta.17

【讨论】:

  • 这最终工作得很好,但我选择了 teleaziz 的答案,因为它更符合我的想法。感谢您帮助我找到解决方案!
猜你喜欢
  • 2023-03-14
  • 2018-02-06
  • 1970-01-01
  • 2018-07-02
  • 2018-12-29
  • 2021-06-14
  • 2019-08-18
  • 1970-01-01
  • 2017-05-13
相关资源
最近更新 更多