The way I thought @Input worked would directly give a big "No!" to this question. However, today I stumbled upon some strange behavior, or maybe I always thought the wrong way about @Input..
I've made a stackblitz to show the issue. This happens in the stackblitz for Angular 7.0.1, but in my local project, it also happens on Angular 6.1.2.
The stackblitz shows a simple parent component that has an object. The object is passed to a child component via @Input. Both the child and parent component have a function that changes the object. They also both show the object's value in the template.
I expected to see that when the parent changes the object, it will change it in the child. However, I did not expect that when the child changes the object, it also changes it for the parent. The stackblitz does show this behavior, however. I always thought you would need to explicitly emit an event via @Output to make a flow to the parent and change it there from the child component.
14 Answers
The answer is "no". In your example, the value that you pass to the @Input property is a reference to an object. If you had two-way binding, you could assign a new object to that property in the child component:
this.thing = { name: "world", nbm: 10 }; and the corresponding property in the parent component would be updated accordingly. That is not the case, as you can see in this stackblitz.
However, since the parent and child components have a reference to the same object, they can both modify one of its properties, and that change will be observed in the other component.
In order to implement two-way binding, you can add an @Output property with the same name followed by Change, and emit the event when the change occurs:
@Input() thing: any; @Output() thingChange = new EventEmitter(); setNewObject(){ this.thing = { name: "world", nmb: 10 }; this.thingChange.emit(this.thing); } The change will then be reflected to the parent component if the two-way binding syntax is used:
<child2 [(thing)]="thing"></child2> See this stackblitz for a demo.
If you want to prevent the child component from modifying the original object, you should bind the object properties instead of the object itself:
@Input() thingName: string; @Input() thingNmb: number; <child [thingName]="thing.name" [thingNmb]="thing.nmb"></child> 3Yes, Angular Change Detection flows from parent to child however what you are experiencing is not related to Object reference. Since the shared object in parent and child point to the same reference, it will update in both the component.
Whenever you make any change through child component, shared object will be changed in the parent component as well and Angular will detect changes and trigger the UI update.
However that is not same case with primitive data type. What you have thought is true for primitive data type. In case primitive data type changes moves from Parent to child not vice versa.
Here is the demo for the same -
2Two way data binding is the combination of both event binding and property binding banana like syntax - Where [(ngModel)] is the combination of [ngModel] and (ngModelChange) events
Here comes the magic with the word Change when a model name followed by the Change word angular will recognize that it will be two way data binding and makes it to work as like that
Coming to Child component @Input() is the kind off passing data to the child as property binding whereas @Output() is the kind off emitting data from the child to the parent as event binding - So to achieve two way binding you need both - check the example below
getModelList: any[]; @Output() modelListChange: EventEmitter<any[]> = new EventEmitter<any[]>(); @Input() get modelList(): any[] { return this.getModelList; } set modelList(value) { this.getModelList = value; this.modelListChange.emit(this.getModelList); } In the above code we have modelList property and modelListChange property with the combination of both the bindings so we can access the modelList property as [(modelList)]="value" and that is your two way binding property
Accessing from parent - <multi-dropdown [(modelList)]="value"></multi-dropdown> now your parent property will be updated based on two way data binding - Make sure you spell the Change correctly which is case sensitive too
Hope this will help you - Thanks Happy coding !!
4When change detection strategy is set to ChangeDetectionStrategy.Default, angular will often trigger change detections and yes, this will work.
Set it to ChangeDetectionStrategy.OnPush, and you will see different behavior.
This setting is configured in the Component annotation:
@Component({ selector: 'my-awesome-component', templateUrl: './my-awesome-component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class MyAwesomeComponent{ } edit: i just tried changing the ChangeDetectionStrategy to OnPush in your stackblitz, and the changes are still being reflected. I just read the docs again, and apparently the reason why this is always updated is because the click event on your button triggers change detection, and change detection is triggered from child to parent. So you could say that the change detection bubbles up to the root like an html event would.
3