2
votes

I am trying to implement two way data binding in angular components. Currently its in a parent child mode.

parent.component.html

<child [(title)]="title"></child>
<span style="color: red">This is parent component {{title}}</span>

parent.component.ts

title = 'app';

child.component.html

<span style="color: blue">This is child component {{title}}</span>

child.component.ts

@Input() title: any;
  @Output() pushTitle = new EventEmitter();

  constructor() { }

  ngOnInit() {
    this.title = 'new title';
    this.pushTitle.emit(this.title);
  }

The title should implement on the parent component as well, when I change it from the child. Also, I am not sure why the parent code keeps going in a loop for no reason. I have added text in html just to test if its updated in both the components, but its updating only in the child, and not in the parent. I am coming from the angularjs background, and two way data binding worked seamlessly in it. I am just confused what I am doing wrong(I know its a noob question).

Demo here: https://stackblitz.com/edit/angular-xttmxg

3
Simple way is update from @Output() pushTitle = new EventEmitter(); to @Output() titleChange = new EventEmitter(); - Stiger

3 Answers

2
votes

There is another way that you can achive the same.

@Input() title: any;
@Output() titleChange: EventEmitter<any> = new EventEmitter<any>();

changeValue() {
  this.title= !title;
  this.titleChange.emit(this.title);
}

Have a look at Angular documentation about two way binding

1
votes

Two way data binding only works for template - component interaction.

If you want to send title change to parent component, you should do something like this:

Parent template and component:

<child [title]="title" (pushTitle)="onTitleChange(value)"></child>
<span style="color: red">This is parent component {{title}}</span>

onTitleChange(value) {
    this.title = value;
}

Followup question:

Template:

 <input [(ngModel)]="inputModel">

Component:

inputModel: string;

Now, every time you type something into input field you will see changes in component model, OR when change inputModel value programmatically, you will see the change in HTML input.

1
votes

You are somehow creating an infinite update cycle using the 2-way-binding. This leads to the infinite loop and eventual stack overflow you noticed.


To fix this, preferably, you want to add some logic to the titleChange event (this is the banana-part of the banana-in-a-box syntax, i.e. the part in parens in [(title)] which is getting automatically translated into an event emitter named titleChange). For example, you might want to skip updating the title property of the parent component if its equal to the update emitted by the child component.

This means you should split up [(title)] into (titleChange)="titleChange($event)" and [title]="title". The first part lets you pass the updated title as $event and then process it in a function titleChanged (name is arbitrary in this case). The second part has the effect that the child component receives updates of the parent component's title property.

Another common pattern is to make title private (commonly with a prefixed underscore, e.g. _title) and then add a getter get title() { return this._title;} so that you can (1) encapsulate this property and (2) add some processing. In your case this is not needed, but it doesn't hurt either. ;-)


Here's a plunkr containing these changes.