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

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}

Related

how to pass dynamic data from parent to child in angular2+

As we know , in angular parent to child communication happened using #Input decorator but how to pass dynamic data from parent to child.for example 'name' property is defined in parent and it's value changes dynamically and updated value of 'name' we are going to using in child component so how to achieve it without services.
you can watch your input changes in the child component with ngOnChanges lifecycle
in your child component you should implements OnChanges
ngOnChanges(changes: SimpleChanges): void {
for (let propName in changes) {
let change = changes[propName];
if (propName === "YOUR_INPUT_NAME") {
this.YOUR_INPUT_NAME = (change.currentValue);
}
}
}
You can make the use of service to get the updated value in child when it gets changed in parent.
For that you need to declare the variable in the service of type observable.
your myService.service.ts would be having the following code:
#Injectable()
export class myService {
myInput:BehaviorSubject<boolean> = new BehaviorSubject(false)
}
now from your parent component change the value of this myInput by using:
myServie.myInput.next(true)
and subscribe this variable in your child component by using:
myService.myInput.subscribe((data)=>{console.log(data)})
In both the parent and child component add the line private mySerivce:MyService in the arguments of constructor.

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).

Two way data binding in angular not working

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.

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