Angular 2中的动态组件

时间:2017-06-12 10:29:24

标签: javascript angular primeng

我正在尝试实现Prime NG的标签视图组件。但我的标签本质上是动态的,即。 因此,当加载容器时,它会在组件内部发送多个AJAX数据请求。(也许组件被多次初始化?) 另一件事,在其中一个组件中,移动鼠标会在控制台上出现数千个错误。

ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed

ERROR CONTEXT [object Object]

不确定原因。在另一个地方使用相同的组件,没有问题。

即使我删除了组件的动态特性并只放置了4个静态标签,一切都运行良好。(现在相同的4个组件来自服务器)。

Html模板:

<div class="col-md-12 padding0">
  <div class="tabViewWrapper">
    <p-tabView (onChange)="handleChange($event)">
      <p-tabPanel header="{{tab.tabName}}" *ngFor="let tab of tabs" >
        <dynamic-component [componentData]="componentData"></dynamic-component>
      </p-tabPanel>
    </p-tabView>
  <div>
</div>

组件:

@Component({
  selector: 'tab-view',
  templateUrl: './tab-view.component.html',
  styleUrls: ['./tab-view.component.scss'],
  encapsulation: ViewEncapsulation.None,
  entryComponents: [GenericDataTableComponent, SingleEditCategoryExplorerComponent, AssetsDataTableComponent]
})
export class TabViewComponent implements OnInit {
  private ngUnsubscribe: Subject<void> = new Subject<void>();
  private componentData = null;
  private tabs: Array<any>;
  private index:number;
  private disabledTabs:Array<any>;
  private disabledTabsWhenMetaDataClicked:Array<any>;

  versionConfig = {
    url: AppSettingProperties.DATA_TABLE_VALUES.VERSIONS_URL,
    dateLocale: AppSettingProperties.DATA_TABLE_VALUES.LOCALE,
    header: AppSettingProperties.DATA_TABLE_VALUES.VERSIONS_HEADER
  };

  relatedConfig = {
    url: AppSettingProperties.BASEURL + AppSettingProperties.DATA_TABLE_VALUES.RELATED_ENDPOINT,
    header: AppSettingProperties.DATA_TABLE_VALUES.RELATED_HEADER
  };

  constructor(private assetDataLoadedService: AssetDataLoadedService, private assetDetailsService: AssetDetailsService, private assetDetailDataModel:AssetDetailDataModel) { }

  @ViewChildren(DynamicContainerComponent) dynamicContainers: QueryList<DynamicContainerComponent>;

  ngOnInit() {
    this.disabledTabs = [];

    //Set items to be disabled when Metadata button is clicked
    this.disabledTabsWhenMetaDataClicked = [AppSettingProperties.TAB_RELATEDITEMS, AppSettingProperties.TAB_VERSIONS];

    //Disable the tabs as per the condistions
    this.disableTabsAsPerRequirement();

    //Assigning tabs
    this.tabs = this.assetDetailsService.systemTabs;

  }
  getInitialSelected(tab){
    return this.selectedTab == this.tabs.indexOf(tab);
  }

  get selectedTab():number{
    return this.index;
  }

  set selectedTab(val:number){
    this.index = val;
    var defaultTab = this.tabs[this.index]['tabName'];

    if(!this.assetDetailDataModel.catalogId){
      this.assetDataLoadedService.assetDetailPublisher.subscribe(data=>{
        this.loadComponentByTab(defaultTab);
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
      });
    }
    else{
      this.loadComponentByTab(defaultTab);
    }
  }

  handleChange(e) {
    let tabName: string = e.originalEvent.currentTarget.innerText;
    this.selectedTab = e.index;
    //this.loadComponentByTab(tabName);    
  }

  loadComponentByTab(tabName:string){
    switch (tabName) {
      case AppSettingProperties.TAB_METADATA:
        this.componentData = { component: AssetsDataTableComponent, inputs: {} }
        break;
      case AppSettingProperties.TAB_CATEGORY:
        let categoryConfig: object = {"catalog_id":this.assetDetailDataModel.catalogId,"item_id":this.assetDetailDataModel.assetId};
        console.log(categoryConfig);
        this.componentData = { component: SingleEditCategoryExplorerComponent, inputs: { tabConfig: categoryConfig } }
        break;
      case AppSettingProperties.TAB_RELATEDITEMS:
        this.componentData = { component: GenericDataTableComponent, inputs: { tabConfig: this.relatedConfig } }
        break;
      case AppSettingProperties.TAB_VERSIONS:
        this.componentData = { component: GenericDataTableComponent, inputs: { tabConfig: this.versionConfig } }
        break;
    }
  }
}

动态组件:

import { Component, Input, ViewContainerRef, ViewChild, ReflectiveInjector, ComponentFactoryResolver } from '@angular/core';

@Component({
  selector: 'dynamic-component',

  template: `<div #dynamicComponentContainer></div>`,
})
export class DynamicComponent {
  private currentComponent = null;

  @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef;

  constructor(private resolver: ComponentFactoryResolver) { }

  // component: Class for the component you want to create
  // inputs: An object with key/value pairs mapped to input name/input value
  @Input() set componentData(data: { component: any, inputs: any }) {
    console.log("Building Component Start");
    if (!data) {
      return;
    }

    // Inputs need to be in the following format to be resolved properly
    let inputProviders = Object.keys(data.inputs).map((inputName) => { return { provide: inputName, useValue: data.inputs[inputName] }; });
    let resolvedInputs = ReflectiveInjector.resolve(inputProviders);

    // We create an injector out of the data we want to pass down and this components injector
    let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.dynamicComponentContainer.parentInjector);

    // We create a factory out of the component we want to create
    let factory = this.resolver.resolveComponentFactory(data.component);

    // We create the component using the factory and the injector
    let component = factory.create(injector);

    // We insert the component into the dom container
    this.dynamicComponentContainer.insert(component.hostView);

    // We can destroy the old component is we like by calling destroy
    if (this.currentComponent) {
      this.currentComponent.destroy();
    }

    this.currentComponent = component;
    console.log("Building Component Finish");
  }  
}

另一件事是动态组件中的控制台启动显示8次。 控制台完成时显示4-5次。

似乎真的很奇怪。

2 个答案:

答案 0 :(得分:0)

正如@echonax写的评论。

这是因为你试图迭代不是数组的东西 最有可能是this.tabs

您可以尝试在div而不是{{tabs|json}}

中写出*ngFor

答案 1 :(得分:0)

由于您的响应需要一些时间来加载DOM,因此将tabs变量作为未定义的数组。

要解决此问题,请将变量初始化为空数组,如下所示

tabs:Array<any> = []

或在构造函数中作为

constructor(){
   this.tabs = [];
}