Reputation: 8308
Child component has two data-bound input property, one of typo string
(inputVariable
) and another of type string[]
(inputArray
).
import {Component, Inject, Input} from 'angular2/core';
@Component(
{
selector: 'child-app',
template: `
{{inputVariable}}
<input type="button" (click)="onButtonOneClick()" value="changeFoo">
<ul>
<li *ngFor="#el of inputArray"> {{el}} </li>
</ul>
<input type="button" (click)="onButtonTwoClick()" value="ChangeLi">
`
})
export class ChildAppComponent {
@Input() inputVariable: string;
@Input() inputArray: string[];
onButtonOneClick() {
this.inputVariable = 'new string';
}
onButtonTwoClick() {
this.inputArray[0] = 'New element String';
}
}
Parent component has same properties and Initializes child components corresponding properties inside template ([inputArray]="inputArray" inputVariable="inputVariable"
)
import {Component} from 'angular2/core';
import {ChildAppComponent} from './childApp.component';
@Component({
selector: 'my-app',
template:
`
{{inputVariable}}
<input type="button" (click)="onButtonOneClick()" value="changeFoo">
<ul>
<li *ngFor="#el of inputArray"> {{el}} </li>
</ul>
<input type="button" (click)="onButtonTwoClick()" value="ChangeLi">
<hr>
<child-app [inputArray]="inputArray" inputVariable="inputVariable"> </child-app>
`,
directives: [ChildAppComponent]
})
export class AppComponent {
inputVariable: string = 'foo';
inputArray: string[] = ['one', 'two'];
onButtonOneClick() {
this.inputVariable = 'new string';
}
onButtonTwoClick() {
this.inputArray[0] = 'New element String';
}
}
Button clicks inside parent and child components changes values of corresponding property (buttonOne
-> inputVariable
& buttonTwo
-> inputArray
)
When click on second button (which changes string[]
property value) change happens both in parent and in child component
When click on first button (which changes string
property value) change only happens inside parent or child (respective to which component's button i clicked)
string
properties ?Upvotes: 1
Views: 2477
Reputation: 657018
For binding from child to parent you need to use @Output()
import {Component, Inject, Input} from 'angular2/core';
@Component({
selector: 'child-app',
template: `
{{inputVariable}}
<input type="button" (click)="onButtonOneClick()" value="changeFoo">
<ul>
<li *ngFor="#el of inputArray"> {{el}} </li>
</ul>
<input type="button" (click)="onButtonTwoClick()" value="ChangeLi">
`
})
export class ChildAppComponent {
@Input() inputVariable: string;
// v added
@Output() inputVariableChange:EventEmitter<string> = new EventEmitter<string>();
@Input() inputArray: string[];
// v added
@Output() inputArrayChange:EventEmitter<string[]> = new EventEmitter<string[]>();
onButtonOneClick() {
this.inputVariable = 'new string';
this.inputVariableChange.emit(this.inputVariable);
}
onButtonTwoClick() {
this.inputArray[0] = 'New element String';
this.inputArrayChange.emit(this.inputArray);
}
}
in parent use it like
{{inputVariable}}
<input type="button" (click)="onButtonOneClick()" value="changeFoo">
<ul>
<li *ngFor="#el of inputArray"> {{el}} </li>
</ul>
<input type="button" (click)="onButtonTwoClick()" value="ChangeLi">
<hr>
<!-- v added ( )
<child-app [(inputArray)]="inputArray" inputVariable="inputVariable"> </child-app>
The naming is relevant. The shortcut binding syntax [(xxx)]="yyy"
only works if the input and output are named @Input() xxx
and @Output() xxxChange
. otherwise the long form must be used.
[xxx]="zzz" (xxxChange)="zzz = $event"
Angular doesn't check for changes inside objects or arrays, it only checks if the object or array is a different object or array than before.
If only a property of an object was modified or only an element was added/removed/replaced, Angular won't notice - for example in an *ngFor
like
<ul>
<li *ngFor="#el of inputArray"> {{el}} </li>
</ul>
if you bind to a property instead of just the element item, then *ngFor
recognizes the change
<ul>
<li *ngFor="#el of inputArray"> {{el.someProp}} </li>
</ul>
A workaround is for example to create a new array
this.inputArray.slice();
or use the new (beta.2) trackBy
feature.
See also http://www.bennadel.com/blog/3020-understanding-object-identity-with-ngfor-loops-in-angular-2-beta-3.htm
creates a copy (new and different array) and this is recognized by Angular as change.
Upvotes: 1
Reputation: 202138
With input you can only make change by reference if you want that the parent component sees the update. That's the case for your array but not for you string property (and more generally properties with primitive types).
To have something working for every case, you need to leverage outputs and two-way binding:
@Component({
(...)
})
export class ChildAppComponent {
@Input() inputVariable: string;
@Output() inputVariableChange: EventEmitter<string> = new EventEmitter();
@Input() inputArray: string[];
onButtonOneClick() {
this.inputVariable = 'new string';
this.inputVariableChange.emit(this.inputVariable);
}
(...)
}
This can be used from the parent component this way:
<child-app [inputArray]="inputArray"
[(inputVariable)]="inputVariable"> </child-app>
See the [(...])
syntax. In this case, the inputVariable
will be updated transparently when updates occur in the child component for this property.
You can notice that Angular2 detects changes only when the reference of a binding changes not when corresponding content (object properties or elements in an array) is updated.
That's the default behavior but you can provide your own based on the DoCheck
interface. See this question for this use case:
Upvotes: 4