Two way data binding in angular not working - javascript

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

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

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.

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.

Related

Why angular2 one-way binding between components works wrong with objects?

I have parent and child components.
I want to send form's values from parent to child component.
Child component can do everything with this data, but all changes are local and should not be returned back to parent component.
When i send a simple variable to child comp – everything works fine and changes doesn't returned back to parent.
But when i send form's values – all changes returns back to parent component...
Live
https://stackblitz.com/edit/angular-dvqyjr
Parent Component JS
export class AppComponent {
constructor (private formBuilder: FormBuilder){};
simpleVariable = 'Value';
form: FormGroup = this.formBuilder.group({
id: 1,
name: 'Name'
});
Parent Component HTML
<hello [simpleVariable]="simpleVariable" [form]="form.value"></hello>
Child Component JS
export class HelloComponent {
#Input() simpleVariable;
#Input() form;
}
Child Component HTML
<input [(ngModel)]="simpleVariable">
<input [(ngModel)]="form.name">
Question
So how can i send an object to child component and modify it without returning data to parent?
This is quite simple. The reason for this behavior is that the form.value is an object. Meaning that you're sharing a reference object with parent and child. Meaning that any change to this object will result in a change in both parent and child.
In order to make changes in child that won't affect your parent, create a deep copy of your object using Object.assign function and use it in your child component.
export class HelloComponent implements OnInit {
_from: {};
#Input() simpleVariable;
#Input() form;
ngOnInit() {
this._form = {};
Object.assign(this._from, this.form);
}
}
Forked and edited example
From the Angular documentation at Reactive Forms - Step 2: Associating the FormGroup model and view:
A form group tracks the status and changes for each of its controls, so if one of the controls changes, the parent control also emits a new status or value change.
You can try the following one with the form:
<form [formGroup]="form">
<input formControlName="name">
</form>
It all comes to Explaining Value vs. Reference in Javascript as well as Angular's Change Detection
Overall, #benshabatnoam already answered you'r question - in order to execute change detection - you need to change object reference, either by old fashioned way:
Object.assign(this._from, this.form);
Or ES6 way:
this._from = {...this.form}

Passing value to child component after it's added to the list on parent component - Angular

I'm working on small Angular project, and I want to pass value from parent to child after it is addedd to the list, so it goes like this:
new-component emits item to the list-component
and after it's addedd to the list, lets move it forward to view-component, so I can see recently addedd item in case I want to edit something--
And everything works fine until I want to see it on view-component (it's successfully added to the list etc.) here is my code:
on a new component item is emitted to list:
onSubmit() {
this._viewService.save(this.view)
.subscribe(
(data: View) => {
this.onSave.emit(data);
}
}
As list is parent list is listening for this emitter.
<view-new #addNew (onSave)="onAdd($event)"></view-new>
After it is emitted, its available in onAdd method, and until this part everything is ok:
onAdd(view: View) {
this.receivedView = view;
// And here I'm opening viewDisplay compnent which displaying info about recently addedd data
this.viewDisplay.show();
}
Also on my list which is parent component is also included display component, which needs to display info about recently addedd item:
<view-display #displayView [view]="receivedView"></view-display>
And when I open this component by calling this.viewDisplay.show();
#Input() view: View;
Which is part of display component is allwayas empty :
show() {
$('#' + this.id).modal('show');
console.log("added - selected:view", this.view);
}
this.view which should be filled onAdd method from parent and passed here is allways empty.. I dont know how..
Thanks guys
Cheers
you are updating field recievedView in a parent, which is bound to view field in a child. This binding is done by angular change detection mechanism. In your onAdd method you are trying to call viewDisplay.show() method synchroniously, before the change detection is done, and, because of this field is not updated yet. The simplest solution would be to assign the field of a child in place like
onAdd(view: View) {
this.receivedView = this.viewDisplay = view;
this.viewDisplay.show();
}
but there are surely better options which depend on your project
Angular needs to know that the value of view has changed. You usually do this by defining a setter. By defining a setter, changes to an input variable can be tracked. You also need to define a private property to save the value in.
private _view: any;
#Input()
get view(): any) {
return this._view;
}
set view(view: any) {
this._view= view;
// Do other stuff when "view" was updated
}
You can also handle the communiation via the service pattern.

Passing value of one component to another Angular

i have an input element in a component like so :
<input #inputDate
(blur)="realDate(inputDate.value); inputDate.value='' ">
and then i want to use (show) the value of the inputDate on another component, how can i do it?
im new to angular, any help will be appreciated.
If you want to send it to a parent component, bind it to a ngModel and use event emitter to send this value.
If you want to send it to child component, use #Input decorator. (parent-to-child)
However, as you want to send the value from html, you can use jquery or document.querySelector to get the value
This might help you
If you want to send your component to a parent component, you can use the #Output() decorator and an Event Emitter together to send the value to a parent component by doing something like:
#Output()
public input: EventEmitter<string> = new EventEmitter<string>();
<my-input (input)="onInputChange()"></my-input>
// Method to detect for changes on #myInput
public onMyInputChanges(): void {
// ...
input.emit(<#myInputValue>);
}
I'll leave it as an exercise for you to figure out how to detect changes to your input element above.
If you want to send your value to a child component, you can use the #Input() decorator and pass it along as such:
<input #myInput [(ngModel)]="inputValue">
public inputValue: string = '';
<child-component [value]="inputValue"></child-component>
Because you want to use the value directly from the HTML, consider using the #ViewChild decorator which would allow you to use your local reference #inputDate to get the value, like so:
#ViewChild('inputDate')
public myInput: HTMLInputElement;
And then you can access your value through myInput.value and either use it on your component or pass it around with the above methods.
You can also look into using Services which might help you reuse some of this logic across several components if that's your use case (e.g. emitting the value from a Subject and having multiple components listen to changes on your input).

How to use multiple copies of same child tag having Event Emitter in it at the same time.

I am having a child component which emits data through event emitter.The Emitting data is bind with ngModel in parent. And the emitting method in it is called from parent component.
I have created child component because i am having two same form. So i created a single form component and used it twice and binded with their data.
//Child Component Code
import {Component, EventEmitter, Input, Output} from 'angular2/core'
#Component({
selector: 'child-component',
template: `
<input [ngModel]="formObj.title" >
`
})
export class ChildComponent {
#Input() formObject: Object;
#Output() formObjectChange= new EventEmitter();
emitChangeforParent() {
this.formObjectChange.emit(newValue);
}
}
//Parent Component
#Component({
selector: 'parent-component',
template: `
<child-component[(formObject)]="doseObject1" #firstForm></child-component>
<child-component[(formObject)]="doseObject2" #secondForm></child-component>
<button (click)="save()">Save</button>
`
})
export class ParentComponent {
doseObject1 ={title:''};
doseObject2 ={title:''};
save(){
this.firstForm.emitChangeforParent();
this.secondForm.emitChangeforParent();
console.log(this.doseObject1); //Updated data by child is available.But this works when i used single tag.
But when i use multiple child tag it does not work
}
}
Problem is that whenever I use single form tag it works fine. The update done by child is reflected here in parent.
But when i use same tag tag twice, than on calling its child emitChangeForParent() method does not work.
I think you might be a little confused about how emitChangeForParent should work. This kinda of method would usually be called by the child, and used by the parent.
so the child component would have (change)="emitChangeForParent() , then on the parent you you would have
basically this means whenever the child component calls emit, it passes the data up, and the parent component can catch it by having a callback.
However, In this case I dont think you need to do that. This is most useful for value types like numbers. If you are passing in an refence type, like your json object doseObject1 ={title:''}, the parent object should still have an up to date reference to it.
I think all you need to do to make this work is have you child component template be "" with the banana in the box double binding syntax. Then the parent will still always have an accurate value of doseObject1.title and doseObject2.title
There is no need to emit the data. The double binding syntax returns the changes done in child component to parent component.

How to pass data from parrent constructor to child component in angular 2

I have a angular 2 page where i need to show 2 different components using same array of data from external API. Parent is regular component, and child is shared among several other components using same functionality.
In parent component class i have output property declared:
public weatherList: WeatherForecast[];
#Output() public weatherListData: any;
Inside constructor of parent component, i populate weatherListData property with data from an external API
http.get(url)
.subscribe(result => {
this.weatherList= result.json() as WeatherForecast[];
this.weatherListData = this.weatherList;
});
and i'm using it inside parent template with success, something like: {{ weatherList.someValue }}
Also, inside parent component template, i have a call to a child component
<daily-temperature-chart [weatherListData]='weatherListData'></daily-temperature-chart>
In child component class i have declared property
#Input() weatherListData: any;
but, when i try to access weatherListData property in constructor, or init of child component, i get undefined result.
EDIT: I have played with console.log() and noticed that child component Constructor and OnInit() methods return before http.get() from parent component. Maybe this is problem, but i'm still new to angular and can't tell.
Can someone point me how to solve this?
You've a service call so you can't go for constructor or OnInit because component initialization is not dependent on your service call for this situation angular provides OnChanges whenever your input value is updated OnChanges fired.
ngOnChanges(changes: any){
console.log(changes);
console.log(this.weatherListData);
}
OnChanges passes as well an argument which informs about the current state and pervious state now you are able to use input values. If your components are bases on input and input is based on any other operation you can handle it in this block.

Categories

Resources