Angular2多态嵌套组件在单独的文件中会中断

时间:2016-02-26 01:08:30

标签: typescript nested polymorphism components angular

我得到错误:“组件视图上的意外指令值'未定义'”当我将多态组件放在单独的文件中时,但当它们都在同一个文件中时没有错误。 我已经研究了关于这个错误的所有SO问题无济于事,但是因为我有一个工作和非工作的场景,希望有一些ng2 /打字员大师可以帮助我把它排除在外:

我把问题归结为玩具场景,我有复合天体(星系,太阳系,星星)。任何天体都可以包含任何其他天体。我知道 - 有些人可能会说明星不能包含星系,但他们可能从未看过Dr. Who。 : - )

我的主要组件设置描述符并创建顶级组件,在本例中是一个星系:

import {Component} from 'angular2/core';
import {CelestialObject} from './CelestialObject';


@Component({
  selector: 'preview',
  directives: [CelestialObject],
  template: `
    <celestial-object [descriptor]="descriptor"></celestial-object>
  `
})

export class MainComponent {
  private descriptor;

  constructor() {
    this.descriptor = {
      type: 'galaxy',
      children: [
        { type: 'solarSystem', children: [{type: 'star', children: []}] },
        { type: 'star', children: []}
      ]
    };
  }
}

  • 工作:同一文件中的所有组件(从代码组织的角度来看是不受欢迎的)

CelestialObjects.ts:

import {Component, Input, DynamicComponentLoader, ElementRef, AfterViewInit} from 'angular2/core';

@Component({
  selector: 'celestial-object',
  template: `
    <span #loadSpecificCelestialObjectHere></span>
  `
})

export class CelestialObject implements AfterViewInit {
  @Input() descriptor: any;

  constructor(private dcl: DynamicComponentLoader, private elementRef: ElementRef) { /* */
  }

  ngAfterViewInit() {
    let objectType: any = null;

    switch (this.descriptor.type) {
      case 'solarSystem':
        objectType = SolarSystem;
        break;
      case 'galaxy':
        objectType = Galaxy;
        break;
      case 'star':
        objectType = Star;
        break;
    }

    this.dcl.loadIntoLocation(objectType, this.elementRef, 'loadSpecificCelestialObjectHere').then((comp) => {
      comp.instance.descriptor = this.descriptor;
    });
  }
}

//======================================
// Galaxy
//======================================
@Component({
  selector: 'galaxy',
  directives: [CelestialObject],
  template: `
    <p>Galaxy</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class Galaxy {
  @Input() descriptor: any;
}

//======================================
// SolarSystem
//======================================
@Component({
  selector: 'solar-system',
  directives: [CelestialObject],
  template: `
    <p>Solar system</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class SolarSystem {
  @Input() descriptor: any;
}

//======================================
// Star
//======================================
@Component({
  selector: 'star',
  directives: [CelestialObject],
  template: `
    <p>Star</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class Star {
  @Input() descriptor: any;
}

  • 无法正常工作:将galaxy组件放入单独的文件中。

CelestialObjectsMinusGalaxy.ts:

import {Component, Input, DynamicComponentLoader, ElementRef, AfterViewInit} from 'angular2/core';
import {Galaxy} from './Galaxy';

@Component({
  selector: 'celestial-object',
  template: `
    <span #loadSpecificCelestialObjectHere></span>
  `
})

export class CelestialObject implements AfterViewInit {
  @Input() descriptor: any;

  constructor(private dcl: DynamicComponentLoader, private elementRef: ElementRef) { /* */
  }

  ngAfterViewInit() {
    let objectType: any = null;

    switch (this.descriptor.type) {
      case 'solarSystem':
        objectType = SolarSystem;
        break;
      case 'galaxy':
        objectType = Galaxy;
        break;
      case 'star':
        objectType = Star;
        break;
    }

    this.dcl.loadIntoLocation(objectType, this.elementRef, 'loadSpecificCelestialObjectHere').then((comp) => {
      comp.instance.descriptor = this.descriptor;
    });
  }
}

//======================================
// SolarSystem
//======================================
@Component({
  selector: 'solar-system',
  directives: [CelestialObject],
  template: `
    <p>Solar system</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class SolarSystem {
  @Input() descriptor: any;
}

//======================================
// Star
//======================================
@Component({
  selector: 'star',
  directives: [CelestialObject],
  template: `
    <p>Star</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class Star {
  @Input() descriptor: any;
}

和Galaxy.ts。与以前相同的代码,只需拆分成单独的文件并导入CelestialObject:

import {Component, Input} from 'angular2/core';
import {CelestialObject} from './CelestialObject';

//======================================
// Galaxy
//======================================
@Component({
  selector: 'galaxy',
  directives: [CelestialObject],
  template: `
    <p>Galaxy</p>
    <celestial-object [descriptor]="obj" *ngFor="#obj of descriptor.children"></celestial-object>
  `
})

export class Galaxy {
  @Input() descriptor: any;
}

现在我收到了错误。我意识到这最终成为一个循环引用,但是没有办法将这些组件保存在自己的文件中,而不会在组件视图中出现“Unexpected directive value'undefined'错误?”

任何帮助非常感谢。这是一只要解开的熊。

2 个答案:

答案 0 :(得分:1)

您可以尝试删除类型开关并将其替换为事件发射器  在CelestialObject

export class CelestialObject implements AfterViewInit {
  @Input() descriptor: any;

  constructor(private dcl: DynamicComponentLoader, 
              private emitter: SharedEmitterService,
              private elementRef: ElementRef) { /* */
    this.emitter.subscribe(objectType => load(objectType));
  }

  load(objectType) {
    this.dcl.loadIntoLocation(objectType, this.elementRef, 'loadSpecificCelestialObjectHere').then((comp) => {
      comp.instance.descriptor = this.descriptor;
    });
  }
}

然后在您的其他课程中,您可以启动加载:

export class Galaxy {
  @Input() descriptor: any;
  constructor(private emitter: SharedEmitterService) { /* */ }
  ngOnInit() {
    this.emitter.emit(Galaxy);
  }
}

答案 1 :(得分:1)

问题是Galaxy导入CelestialObject和CelestialObject导入Galaxy。 Sasxa建议摆脱switch语句,从而使CelestialObject免于依赖Galaxy。

这导致了使用实际类型填充描述符的解决方案,而不是表示类型的字符串:

DELETE FROM TableA where MLS NOT IN (SELECT LIST_3 FROM TableB) LIMIT 100;

然后CelestialObject变为以下内容,不依赖于特定的天体:

import {Component} from 'angular2/core';
import {CelestialObject} from './CelestialObject';
import {Galaxy} from './Galaxy';
import {SolarSystem} from './SolarSystem';
import {Star} from './Star';


@Component({
  selector: 'preview',
  directives: [CelestialObject],
  template: `
    <celestial-object [descriptor]="descriptor"></celestial-object>
  `
})

export class MainComponent {
  private descriptor;

  constructor() {
    this.descriptor = {
      type: Galaxy,
      children: [
        { type: SolarSystem, children: [{type: Star, children: []}] },
        { type: Star, children: []}
      ]
    };
  }
}

感谢Sasxa提供关键的拼图。