Reputation: 1686
I have a component which displays a list of 'items' which are components created with a selector. I have a checkbox which i want, when clicked to update the 'state' of all child components.
Im really struggling to find the correct solution for doing this. Please see Plunkr for more info.
//our root app component
import {Component, EventEmitter} from 'angular2/core'
class Item {
name: boolean;
constructor(name: string) {
this.name = name;
}
}
@Component({
selector: 'my-item',
template: `
<div>
<label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
</div>
`
})
export class MyItemComponent {
state: boolean = false;
}
@Component({
selector: 'my-app',
template: `
<div style="border: 1px solid red;">
<label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
</div>
<div *ngFor="#item of items">
<my-item></my-item>
</div>
`,
directives: [MyItemComponent]
})
export class App {
state: boolean = true;
items: Item[] = [];
constructor() {
this.items.push(new Item("hello"));
this.items.push(new Item("test"));
}
}
Upvotes: 3
Views: 9647
Reputation: 202276
What @Günter said, it's completely true!
That said, I see some mistakes into your plunkr:
@Component({
selector: 'my-item',
template: `
<div>Hello</div>
`
}); // <------- Remove the ;
export class MyItemComponent {
}
And you missed the component in the directives
property:
@Component({
selector: 'my-app',
template: `
<div>
<label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
</div>
<div *ngFor="#item of items">
<my-item></my-item>
</div>
`,
directives: [] // <-------- Add the MyItemComponent component
})
export class App {
(...)
}
Edit
You can leverage the @ViewChildren
decorator to reference children directly.
@Component({
selector: 'my-app',
template: `
(...)
`,
directives: [MyItemComponent]
})
export class App {
(...)
@ViewChildren(MyItemComponent)
children:MyItemComponent[];
(...)
}
Then you can add a control to your checkbox to detect changes and update state of child components accordingly:
@Component({
selector: 'my-app',
template: `
<div style="border: 1px solid red;">
<label><input type="checkbox" [(ngModel)]="state"
[ngFormControl]="stateCtrl"/> {{state}}</label>
</div>
<div *ngFor="#item of items">
<my-item></my-item>
</div>
`,
directives: [MyItemComponent]
})
export class App {
(...)
constructor() {
this.items.push(new Item("hello"));
this.items.push(new Item("test"));
this.stateCtrl = new Control();
this.stateCtrl.valueChanges.subscribe(
data => {
this.children._results.forEach(child => {
child.state = data;
});
});
}
}
I updated your plunkr with this approach: https://plnkr.co/edit/nAA2VxZmWy0d4lljvPpU?p=preview.
See this link for more details: Why is that i can't inject the child component values into parent component?
Upvotes: 3
Reputation: 657751
Update
@Component({
selector: 'my-item',
inputs: ['state']; // added
template: `
<div>
<label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
</div>
`
})
export class MyItemComponent {
state: boolean = false;
}
and then use it like
<my-item [state]="state"></my-item>
original
Angular change detection doesn't detect changes in arrays.
This should make it work:
constructor() {
this.items.push(new Item("hello"));
this.items.push(new Item("test"));
this.items = this.items.slice();
}
This way a new array (a copy) is assigned to this.items
and therefore Angular will recognize it as change.
In MyItem
you need an input
@Component({
selector: 'my-item',
inputs: ['items']; // added
template: `
<div>
<label><input type="checkbox" [(ngModel)]="state"/> {{state}}</label>
</div>
`
})
export class MyItemComponent {
state: boolean = false;
items: Item[]; // added
}
then you build the connection with
<my-item [items]="items"></my-item>
To get code called in MyItemComponent
when items
change implement ngOnChanges()
See also https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html
export class MyItemComponent {
state: boolean = false;
items: Item[]; // added
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
console.log('ngOnChanges - myProp = ' + changes['items'].currentValue);
}
}
Upvotes: 6