更新
我已经尝试过放入EmitterEvent,它可以解决我的问题,然后在所有点击事件上执行。
@Component({
selector: 'bol-form',
template: `
<table class="styledTable" id="CommoditySection">
<thead><th>Commodities</th></thead>
<tr><td>
<div id="commodities" (click) = "removeHandlingUnits($event)">
<h5><a id="addHandlingUnits" (click)="addHandlingUnits(handlingUnitsCount, 0)">Add More Handling Units</a></h5>
<dynamic-handling-component
[componentHandlingData]="componentHandlingData"
[componentHandlingData]="componentHandlingData"
[removeComponent]="removeComponent">
</dynamic-handling-component>
</div>
</td></tr>
</table>
`
})
export class App implements AfterContentInit {
private handlingUnitsCount: number;
private componentHandlingData = null;
private componentCommoditiesData = null;
private removeCommoditiesComponent = null;
private removeComponent = null;
constructor() {
this.handlingUnitsCount = 0;
}
addHandlingUnits(unita, unitb) {
if (unita == undefined ) {
unita = this.handlingUnitsCount;
unitb = 0;
}
this.sleep(unita * 50 + 10).then(() => {
this.componentHandlingData = {
component: HandlingComponent,
inputs: {
handlingUnitsCount: unita,
}
};
this.handlingUnitsCount += 1;
});
}
removeHandlingUnits(id) {
if (id == 0) {
this.sleep(10).then(() => {
this.removeComponent = {
toDelete: id,
allDelete: true,
};
});
this.handlingUnitsCount = this.handlingUnitsCount - 1;
this.componentHandlingData = null;
this.componentCommoditiesData = null;
} else {
this.sleep(10).then(() => {
this.removeComponent = {
toDelete: id,
allDelete: false,
};
});
this.handlingUnitsCount = 0;
this.componentHandlingData = null;
this.componentCommoditiesData = null;
}
}
ngAfterContentInit() {
this.addHandlingUnits(0,0);
}
sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
}
@Component({
selector: 'handling',
template: `
<div id="commod{{handlingUnitsCount}}" class = "commHeaderHandlingType">
<label>Select type of Handling units </label><br class="mobileonly">
<select id = "handingUnit{{handlingUnitsCount}}" class="form-control" name = "bdhdform{{handlingUnitsCount}}">
<option value=" ">(--)</option>
<option value="BAG">BAG</option>
<option value="BAL">BALE</option>
<option value="BBL">BARREL</option>
<option value="BSK">BASKET</option>
<option value="BIN">BIN</option>
<option value="BOT">BOTTLE</option>
<option value="BOX">BOX</option>
<option value="BXT">BUCKET</option>
<option value="BDL">BUNDLE</option>
<option value="CAG">CAGE</option>
<option value="CAN">CAN</option>
<option value="CTN">CARTON</option>
<option value="CAS">CASE</option>
<option value="CNT">CONTAINER</option>
<option value="COR">CORES</option>
<option value="CRT">CRATE</option>
<option value="CYL">CYLINDER</option>
<option value="DRM">DRUM</option>
<option value="DBK">DRY BULK</option>
<option value="ENV">ENVELOPE</option>
<option value="FKL">FORKLIFT</option>
<option value="HRB">HNGERS/RCKS BXS</option>
<option value="IBC">INTERMEDIATE BC</option>
<option value="JAR">JAR</option>
<option value="JUG">JUG</option>
<option value="KEG">KEG</option>
<option value="KIT">KIT</option>
<option value="LBK">LIQUID BULK</option>
<option value="PKG">PACKAGE</option>
<option value="PCK">PACKED</option>
<option value="PAL">PAIL</option>
<option value="PLT">PALLET</option>
<option value="PCS">PIECES</option>
<option value="RCK">RACK</option>
<option value="REL">REEL</option>
<option value="ROL">ROLL</option>
<option value="SWP">SHRINK WRAP PLT</option>
<option value="SWS">SHRINK WRAP SKD</option>
<option value="SKD">SKID</option>
<option value="SLP">SLIP SHEET</option>
<option value="TNK">TANKS</option>
<option value="TOT">TOTE</option>
<option value="TRL">TRAILER LOAD</option>
<option value="TRY">TRAY</option>
<option value="TUB">TUB</option>
<option value="TBE">TUBE</option>
</select>
<label>Number of Units</label>
<input name = "bdhdunit{{handlingUnitsCount}}" type="text" size="5" maxlength="5">
<button id="removeHandlingUnits{{handlingUnitsCount}}" (click)="removeHandlingUnits(handlingUnitsCount)">X</button>
<br>
<div class = "seperatorDiv">------------------------------------</div>
<br>
</div>
`
})
export default class HandlingComponent implements AfterContentInit {
private handlingUnitsType:string;
private handlingUnitsCount: number;
private handlingUnitsRequested: number;
private commoditiesCount: number;
private componentHandlingData = null;
private componentCommoditiesData = null;
private removeComponent = null;
@Output() click: EventEmitter<number> = new EventEmitter<any>();
constructor(private injector: Injector) {
this.handlingUnitsCount = this.injector.get('handlingUnitsCount');
this.handlingUnitsRequested = 0;
this.handlingUnitsType = " ";
this.commoditiesCount = 0;
}
ngAfterContentInit() {
//this.addCommodities(this.handlingUnitsCount, unitb);
}
addCommodities(handlingUnit, id) {
this.sleep(20).then(() => {
this.componentCommoditiesData = {
component: CommoditiesComponent,
inputs: {
commoditiesCount: this.commoditiesCount,
handlingUnitsCount: this.handlingUnitsCount,
}
};
this.commoditiesCount += 1;
});
return false;
}
removeHandlingUnits(id) {
this.click.emit(this.handlingUnitsCount);
return false;
}
sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
}
let componentList = [];
@Component({
selector: 'dynamic-handling-component',
entryComponents: [ HandlingComponent ], // Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicHandlingComponentContainer></div>
`,
})
export default class DynamicHandlingComponent {
@ViewChild('dynamicHandlingComponentContainer', { read: ViewContainerRef }) dynamicHandlingComponentContainer: ViewContainerRef;
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
@Input() set componentHandlingData (data: {component: any, inputs: any }) {
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.dynamicHandlingComponentContainer.injector);
// 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.dynamicHandlingComponentContainer.insert(component.hostView, inputProviders[0].useValue);
componentList[ inputProviders[0].useValue ] = component;
// We can destroy the old component is we like by calling destroy
//if (this.currentComponent) {
// this.currentComponent.destroy();
//}
}
@Input() set removeComponent(data: { toDelete: any, allDelete: boolean }) {
if (!data) {
return;
}
if ( data.allDelete == true ) {
for( var item = componentList.length - 1; item >= 0; item--) {
if ( componentList[item] != "REMOVED" ) {
if ( ( componentList[item] != null ) && (componentList[item] != 1)) {
componentList[item].destroy();
componentList[item] = "REMOVED";
} else {
componentList[item] = "REMOVED";
}
}
}
} else {
if ( ( componentList[ data.toDelete ] != null ) && ( componentList[item] != "REMOVED" ) && ( componentList[item] != 1 ) ) {
componentList[ data.toDelete ].destroy();
componentList[ data.toDelete ] = "REMOVED";
}
}
}
constructor(private resolver: ComponentFactoryResolver) {
}
}
我遇到动态组件问题。在删除组件时,我收到detectChanges
错误。我尝试在detach()
方法之前和之后添加remove()
。
以下是我的问题https://embed.plnkr.co/4yB7ZBCyFp2don9l8N3n/
的完整工作示例我遇到的主要问题是如何摆脱该错误,以便在删除组件后不会中断。
我是angular2的新手,所以这可能不是实现这一目标的最佳方法。我正在寻找,似乎使用发射器作为子组件的按钮是正确的方法。我只是从主要组件调用remove()
时遇到了麻烦。
remove()
似乎可以在主要组件中运行,但是当在另一个组件内部时,它似乎已将更改发送到父动态组件。
这是抛出的错误
ng:///AppModule/CommoditiesComponent.ngfactory.js:88错误错误: ViewDestroyedError:尝试使用已销毁的视图:detectChanges at viewDestroyedError(unpkg.com/@angular/core/bundles/core.umd.js:9579) at Object.debugUpdateRenderer [as updateRenderer](unpkg.com/@angular/core/bundles/core.umd.js:14413) 在checkAndUpdateView(unpkg.com/@angular/core/bundles/core.umd.js:13552) 在callViewAction(unpkg.com/@angular/core/bundles/core.umd.js:13897) 在execComponentViewsAction(unpkg.com/@angular/core/bundles/core.umd.js:13829) 在checkAndUpdateView(unpkg.com/@angular/core/bundles/core.umd.js:13553) 在callViewAction(unpkg.com/@angular/core/bundles/core.umd.js:13897) at execEmbeddedViewsAction(unpkg.com/@angular/core/bundles/core.umd.js:13855) 在checkAndUpdateView(unpkg.com/@angular/core/bundles/core.umd.js:13548) 在callViewAction(unpkg.com/@angular/core/bundles/core.umd.js:13897)
以下是我的代码:
@Component({
selector: 'bol-form',
template: `
<table class="styledTable" id="CommoditySection"
(handlingDeleteEvent) =
"removeHandlingUnits(handlingUnitsCount)">
<thead><th>Commodities</th></thead>
<tr><td>
<div id="commodities">
<h5><a id="addHandlingUnits"
(click)="addHandlingUnits(handlingUnitsCount, 0)">Add More Handling Units</a></h5>
<dynamic-handling-component
[componentHandlingData]="componentHandlingData"
[removeComponent]="removeComponent">
</dynamic-handling-component>
</div>
</td></tr>
</table>
`
})
export class App implements AfterContentInit {
private handlingUnitsCount: number;
private componentHandlingData = null;
private componentCommoditiesData = null;
private removeCommoditiesComponent = null;
private removeComponent = null;
constructor() {
this.handlingUnitsCount = 0;
}
addHandlingUnits(unita, unitb) {
if (unita == undefined ) {
unita = this.handlingUnitsCount;
unitb = 0;
}
this.sleep(unita * 50 + 10).then(() => {
this.componentHandlingData = {
component: HandlingComponent,
inputs: {
handlingUnitsCount: unita,
}
};
this.handlingUnitsCount += 1;
});
}
removeHandlingUnits(id) {
if (id == 0) {
this.sleep(10).then(() => {
this.removeComponent = {
toDelete: id,
allDelete: true,
};
});
this.handlingUnitsCount = this.handlingUnitsCount - 1;
this.componentHandlingData = null;
this.componentCommoditiesData = null;
} else {
this.sleep(10).then(() => {
this.removeComponent = {
toDelete: id,
allDelete: false,
};
});
this.handlingUnitsCount = 0;
this.componentHandlingData = null;
this.componentCommoditiesData = null;
}
}
ngAfterContentInit() {
this.addHandlingUnits(0,0);
}
sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
}
@Component({
selector: 'handling',
template: `
<div id="commod{{handlingUnitsCount}}" class = "commHeaderHandlingType">
<label>Select type of Handling units </label><br class="mobileonly">
<select id = "handingUnit{{handlingUnitsCount}}" class="form-control" name = "bdhdform{{handlingUnitsCount}}">
<option value=" ">(--)</option>
<option value="BAG">BAG</option>
<option value="BAL">BALE</option>
<option value="BBL">BARREL</option>
<option value="BSK">BASKET</option>
<option value="BIN">BIN</option>
<option value="BOT">BOTTLE</option>
<option value="BOX">BOX</option>
<option value="BXT">BUCKET</option>
<option value="BDL">BUNDLE</option>
<option value="CAG">CAGE</option>
<option value="CAN">CAN</option>
<option value="CTN">CARTON</option>
<option value="CAS">CASE</option>
<option value="CNT">CONTAINER</option>
<option value="COR">CORES</option>
<option value="CRT">CRATE</option>
<option value="CYL">CYLINDER</option>
<option value="DRM">DRUM</option>
<option value="DBK">DRY BULK</option>
<option value="ENV">ENVELOPE</option>
<option value="FKL">FORKLIFT</option>
<option value="HRB">HNGERS/RCKS BXS</option>
<option value="IBC">INTERMEDIATE BC</option>
<option value="JAR">JAR</option>
<option value="JUG">JUG</option>
<option value="KEG">KEG</option>
<option value="KIT">KIT</option>
<option value="LBK">LIQUID BULK</option>
<option value="PKG">PACKAGE</option>
<option value="PCK">PACKED</option>
<option value="PAL">PAIL</option>
<option value="PLT">PALLET</option>
<option value="PCS">PIECES</option>
<option value="RCK">RACK</option>
<option value="REL">REEL</option>
<option value="ROL">ROLL</option>
<option value="SWP">SHRINK WRAP PLT</option>
<option value="SWS">SHRINK WRAP SKD</option>
<option value="SKD">SKID</option>
<option value="SLP">SLIP SHEET</option>
<option value="TNK">TANKS</option>
<option value="TOT">TOTE</option>
<option value="TRL">TRAILER LOAD</option>
<option value="TRY">TRAY</option>
<option value="TUB">TUB</option>
<option value="TBE">TUBE</option>
</select>
<label>Number of Units</label>
<input name = "bdhdunit{{handlingUnitsCount}}" type="text" size="5" maxlength="5">
<button id="removeHandlingUnits{{handlingUnitsCount}}" (click)="removeHandlingUnits(handlingUnitsCount)">X</button>
<br>
<dynamic-handling-component
[removeComponent]="removeComponent">
</dynamic-handling-component>
<dynamic-commodities-component
[componentCommoditiesData]="componentCommoditiesData"
[removeCommoditiesComponent]="removeCommoditiesComponent">
</dynamic-commodities-component>
<a id="addCommodities{{commoditiesCount}}" (click)="addCommodities( handlingUnitsCount, commoditiesCount )" href="">Add More Commodities to Handling Unit</a>
<div class = "seperatorDiv">------------------------------------</div>
<br>
</div>
`
})
export default class HandlingComponent implements AfterContentInit {
private handlingUnitsType:string;
private handlingUnitsCount: number;
private handlingUnitsRequested: number;
private commoditiesCount: number;
private componentHandlingData = null;
private componentCommoditiesData = null;
private removeComponent = null;
@Output() handlingDeleteEvent = new EventEmitter<any>();
constructor(private injector: Injector) {
this.handlingUnitsCount = this.injector.get('handlingUnitsCount');
this.handlingUnitsRequested = 0;
this.handlingUnitsType = " ";
this.commoditiesCount = 0;
}
ngAfterContentInit() {
//this.addCommodities(this.handlingUnitsCount, unitb);
}
addCommodities(handlingUnit, id) {
this.sleep(20).then(() => {
this.componentCommoditiesData = {
component: CommoditiesComponent,
inputs: {
commoditiesCount: this.commoditiesCount,
handlingUnitsCount: this.handlingUnitsCount,
}
};
this.commoditiesCount += 1;
});
return false;
}
removeHandlingUnits(id) {
this.sleep(10).then(() => {
this.removeComponent = {
toDelete: id,
allDelete: false,
};
});
return false;
}
sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
}
@Component({
selector: 'commodities',
template: `
<div id = "commoditityLine{{handlingUnitsCount}}_{{commoditiesCount}}" class="overflowdiv">
<table class="bolCommTable">
<tr>
<td id = "commHeaderWeight">Weight</td>
<td id = "commHeaderClass">Class</td>
<td></td>
<!-- <td id = "commHeaderRemove">Remove</td> -->
</tr>
<tr>
<td id = "commHeaderWeight">
<input name = "bdwgt{{handlingUnitsCount}}_{{commoditiesCount}}" type="text" size="5" maxlength="7" >
</td>
<td id = "commHeaderClass">
<select name = "bdclss{{handlingUnitsCount}}_{{commoditiesCount}}" type="text" width="49px" style="width:49px;">
<option value="0">(--)</option>
<option value="50">50</option>
<option value="55">55</option>
<option value="60">60</option>
<option value="65">65</option>
<option value="70">70</option>
<option value="77.5">77.5</option>
<option value="85">85</option>
<option value="92.5">92.5</option>
<option value="100">100</option>
<option value="110">110</option>
<option value="125">125</option>
<option value="150">150</option>
<option value="175">175</option>
<option value="200">200</option>
<option value="250">250</option>
<option value="300">300</option>
<option value="400">400</option>
<option value="500">500</option>
</select>
</td>
<td>
<button (click)="removeCommodities(commoditiesCount)">X</button>
</td>
</tr>
<dynamic-commodities-component
[componentCommoditiesData]="componentCommoditiesData"
[removeCommoditiesComponent]="removeCommoditiesComponent">
</dynamic-commodities-component>
</table>
</div>
`
})
export default class CommoditiesComponent {
private commoditiesCount: number;
private handlingUnitsCount: number;
private componentCommoditiesData = null;
private removeCommoditiesComponent = null;
private bdwgt: string;
private bdclss: string;
constructor(private injector: Injector) {
this.commoditiesCount = this.injector.get('commoditiesCount');
this.handlingUnitsCount = this.injector.get('handlingUnitsCount');
}
addCommodities( unita, unitb ) {
var commoditiesCount = unitb;
var handlingUnitsCount = unita;
var bdwgt = this.bdwgt;
var bdclss = this.bdclss;
//this.sleep(unitb * 30 + (unita * 40)).then(() => {
this.componentCommoditiesData = {
component: CommoditiesComponent,
inputs: {
commoditiesCount: commoditiesCount,
handlingUnitsCount: handlingUnitsCount,
}
};
this.commoditiesCount += 1;
//});
return false;
}
removeCommodities(id) {
this.sleep(10).then(() => {
this.removeCommoditiesComponent = {
toDelete: id,
allDelete: false,
};
});
this.componentCommoditiesData = null;
this.commoditiesCount = this.commoditiesCount - 1;
return false;
}
sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
}
let componentList = [];
@Component({
selector: 'dynamic-handling-component',
entryComponents: [ HandlingComponent ], // Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicHandlingComponentContainer></div>
`,
})
export default class DynamicHandlingComponent {
@ViewChild('dynamicHandlingComponentContainer', { read: ViewContainerRef }) dynamicHandlingComponentContainer: ViewContainerRef;
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
@Input() set componentHandlingData (data: {component: any, inputs: any }) {
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.dynamicHandlingComponentContainer.injector);
// 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.dynamicHandlingComponentContainer.insert(component.hostView, inputProviders[0].useValue);
componentList[ inputProviders[0].useValue ] = component;
// We can destroy the old component is we like by calling destroy
//if (this.currentComponent) {
// this.currentComponent.destroy();
//}
}
@Input() set removeComponent(data: { toDelete: any, allDelete: boolean }) {
if (!data) {
return;
}
if ( data.allDelete == true ) {
for( var item = componentList.length - 1; item >= 0; item--) {
if ( componentList[item] != "REMOVED" ) {
if ( ( componentList[item] != null ) && (componentList[item] != 1)) {
componentList[item].destroy();
componentList[item] = "REMOVED";
} else {
componentList[item] = "REMOVED";
}
}
}
} else {
if ( ( componentList[ data.toDelete ] != null ) && ( componentList[item] != "REMOVED" ) && ( componentList[item] != 1 ) ) {
componentList[ data.toDelete ].destroy();
componentList[ data.toDelete ] = "REMOVED";
}
}
}
constructor(private resolver: ComponentFactoryResolver) {
}
}
let componentList = [];
var index = 0;
@Component({
selector: 'dynamic-commodities-component',
entryComponents: [CommoditiesComponent ], // Reference to the components must be here in order to dynamically create them
template: `
<div #dynamicCommoditiesComponentContainer></div>
`,
})
export default class DynamicCommoditiesComponent {
@ViewChild('dynamicCommoditiesComponentContainer', { read: ViewContainerRef }) dynamicCommoditiesComponentContainer: ViewContainerRef;
// component: Class for the component you want to create
// inputs: An object with key/value pairs mapped to input name/input value
@Input() set componentCommoditiesData (data: {component: any, inputs: any }) {
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.dynamicCommoditiesComponentContainer.injector);
// 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.dynamicCommoditiesComponentContainer.insert(component.hostView, inputProviders[0].useValue);
componentList[index] = component;
index = index + 1;
}
@Input() set removeCommoditiesComponent(data: { toDelete: any, allDelete: boolean }) {
if (!data) {
return;
}
if ( data.allDelete == true ) {
for( var item = componentList.length - 1; item >= 0; item--) {
if ( componentList[item] != "REMOVED" ) {
if ( ( componentList[item] != null ) && (componentList[item] != 1)) {
componentList[item].destroy();
componentList[item] = "REMOVED";
} else {
componentList[item] = "REMOVED";
}
}
}
} else {
if ( ( componentList[ data.toDelete ] != null ) && ( componentList[ data.toDelete ] != "REMOVED" ) && ( componentList[ data.toDelete ] != 1 ) ) {
componentList[ data.toDelete ].destroy();
componentList[ data.toDelete ] = "REMOVED";
}
}
}
constructor(private resolver: ComponentFactoryResolver) {
}
}