I have an Item container and item pattern where the child items shoud be added and removed from its container. Adding is fine while remove does nothing. It seems that the angular2 *ngFor directive does not work when any of the child items removed.
import { NgFor} from 'angular2/common';
import { bootstrap } from 'angular2/platform/browser';
import { Component, View, Directive, OnDestroy, Input, enableProdMode } from 'angular2/core';
import { CORE_DIRECTIVES} from 'angular2/common';
@Component({selector: 'itemcontainer',})
@View({ template: `<ul (click)="$event.preventDefault()">
<li *ngFor="#it of items">Any Item</li>
</ul>
<div><ng-content></ng-content></div>`,
directives: [NgFor],
})
export class ItemContainer {
public items: Array<Item> = [];
public addItem(item: Item) {
this.items.push(item);
}
public removeItem(item: Item) {
var index = this.items.indexOf(item);
if (index === -1) {
return;
}
console.log(`Index about to remove: ${index} this.items length: ${this.items.length}`);
this.items.slice(index, 1);
console.log(`this.items length: ${this.items.length}`);
}
}
@Directive({ selector: 'item' })
export class Item implements OnDestroy {
@Input() public heading: string;
constructor(public itemcontainer: ItemContainer) {
this.itemcontainer.addItem(this);
}
ngOnDestroy() {
this.itemcontainer.removeItem(this);
}
}
@Component({
selector: 'my-app'
})
@View({
template: `<div (click)="$event.preventDefault()">
<button type="button" (click)="addItem()">Add item</button>
<button type="button" (click)="removeItem()">Remove item</button>
<itemcontainer>
<item *ngFor="#containerItem of containerItems" [heading]="containerItem.title">Content </item>
</itemcontainer>
</div>`,
directives: [CORE_DIRECTIVES, Item, ItemContainer],
})
class Tester {
private counter: number = 2;
public containerItems: Array<any> = [
{ title: 'Item1' },
{ title: 'Item2' },
];
addItem() {
this.containerItems.push({ title: `Item ${this.counter}` });
}
removeItem() {
if (this.containerItems.length > 0) {
this.containerItems.splice(this.containerItems.length - 1, 1);
}
}
}
enableProdMode();
bootstrap(Tester);
Here is the DOM look like after two new items added and removed:
<itemcontainer>
<ul>
<li>Any Item</li>
<li>Any Item</li>
<li>Any Item</li>
<li>Any Item</li>
</ul>
<div>
<item>Content </item>
<item>Content </item>
</div>
</itemcontainer>
The issue is the li part does not removed. Any idea?
(I tested it with angular 2.0.0-beta.3 and 2.)
You need to use splice()
not slice()
. There are no issues with Angular change detection here.
this.items.splice(index, 1);
NgFor
will loop over your array items, and it will detect when something is added or removed just fine.
Plunker
Also, you can remove this stuff:
import { NgFor} from 'angular2/common';
import { CORE_DIRECTIVES} from 'angular2/common';
directives: [NgFor],
Also, you have circular references in your data. Change your code to the following
<li *ngFor="#it of items">Any Item {{it | json}}</li>
and note the error in the console:
EXCEPTION: TypeError: Converting circular structure to JSON in [Any Item {{it | json}} in ItemContainer
In fact, Angular2 only detects changes when the instance changes. I mean the instance of the array not changes internally on elements.
You could use this (see the second slice
call):
public removeItem(item: Item) {
var index = this.items.indexOf(item);
if (index === -1) {
return;
}
console.log(`Index about to remove: ${index} this.items length: ${this.items.length}`);
this.items.slice(index, 1);
console.log(`this.items length: ${this.items.length}`);
this.items = this.items.slice();
}
This answers could also help you:
Yes, it is change detection indeed. This what worked for me:
this.items = [
...this.items.slice(0, index),
...this.items.slice(index + 1, this.items.length)
];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With