当我将多态组件放在单独的文件中时,我收到错误:"组件视图上意外的指令值'未定义'",但当它们都在同一个文件中时没有错误。我已经研究了有关此错误的所有SO问题无济于事,但是由于我有一个工作和非工作场景,希望有一些ng2/打字稿大师可以帮助我解决这个问题:
我把这个问题归结为一个玩具场景,我有复合天体(星系、太阳系、恒星)。任何天体都可以包含任何其他天体。我知道——有些人可能会说恒星不能容纳星系,但他们可能从未看过《神秘博士》。:-)
我的主要组件设置描述符并创建顶级组件,在本例中是一个星系:
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;
}
- 不工作:将银河组件放入单独的文件中。
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;
}
还有银河网。与之前相同的代码,只需拆分成一个单独的文件并导入 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;
}
现在我收到错误。我意识到这最终是一个循环引用,但是有没有办法将这些组件保存在它们自己的文件中而不会收到"组件视图上的意外指令值'未定义'"错误?
任何帮助非常感谢。这个是一只需要解开的熊。
您可以尝试删除类型开关并将其替换为事件发射器 在您的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);
}
}
,CelestialObject导入Galaxy。Sasxa建议去掉switch语句,从而将CelestialObject从对Galaxy的依赖中解放出来。
这导致了用实际类型填充描述符的解决方案,而不是表示类型的字符串:
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: []}
]
};
}
}
然后,天体变为以下内容,不依赖于特定的天体:
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() {
this.dcl.loadIntoLocation(descriptor.type, this.elementRef, 'loadSpecificCelestialObjectHere').then((comp) => {
comp.instance.descriptor = this.descriptor;
});
}
}
感谢Sasxa提供关键的拼图。