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

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.

Related

How can I access a property of a step component from the parent page of a Vue stepper?

Not very experienced with Vue, and am struggling to fix an issue with an existing component made by a previous developer.
On the parent page, the stepper component is declared like this, with these as the names of the sub-components that make up the individual steps:
#Component({
name: 'switch-stepper',
components: {
Stepper,
StepIndicator,
FirstStep,
SecondStep,
ThirdStep
}
})
There's a property in ThirdStep that needs to be changed, either in the parent page or in FirstStep. The property in question is public, and declared like this:
#Prop({ default: true })
public myBooleanProperty: boolean;
However, inside the parent page, if I try this:
ThirdStep.myBooleanProperty
It is not recognised by the intellisense and is undefined. I've tried also creating a public method on ThirdStep that I can call to use ForceUpdate but public methods likewise seem to be inaccessible.
I've also tried setting the public property via a function in the parent page when the step is created:
<third-step :page="page"
:step="steps[4]"
:myBooleanProperty="setMyBooleanProperty()"
v-show="currentStep === steps[4]">
</third-step>
But as far as I can tell this is only called once when the step is created and never accessed again.
What can I do to set the property of this child step via other components in the stepper?
There's a property in ThirdStep that needs to be changed, either in the parent page or in FirstStep. The property in question is public,
and declared like this:
If a property needs to be changed, it should be changed in the component which returns the property in the data property. By this, I mean, the component that has
data() {
return {
myBooleanProperty: false // or true. This is the local data that will be initialised and passed as props to the ThirdStep component
}
}
If this is in the parent page, change ThirdStep.myBooleanProperty to this.myBooleanProperty = *enter value*. The change of the value of myBooleanProperty can be done in a method, watcher, computed property etc. The reason ThirdStep.myBooleanProperty is not working in the parent component, is that each vue component is a Vue instance and ThirdStep cannot have access to an instance property in the parent component.
ParentComponent.vue
#Component({
name: 'switch-stepper',
components: {
Stepper,
StepIndicator,
FirstStep,
SecondStep,
ThirdStep
}
})
<third-step>
...
:myBooleanProperty="myBooleanProperty" // my boolean property is passed as props from the parent component, I assume
....>
</third-step>
When myBooleanProperty is changed where it is initialised (parent component, I assume), this will cause the value of the myBooleanProperty props, passed into the ThirdStep component, to change and there will be a re-render of the parts of the ThirdStep component that use myBooleanProperty props.

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}

Angular parent component "scoping" child component

Suppose I have components Parent and Child. Child can be used either as a standalone component, or within a Parent component. However, I want Child to have different behavior based upon where it lives.
Suppose Child has an optional #Input [isHappy], which is either true or false. However, whenever a Child component is hosted by a Parent component, suppose isHappy must always be true.
AFAICT there are two ways of doing this:
1) User must just know to always specify [isHappy]="true" whenever a Child is hosted by a Parent.
<parent>
<child [isHappy]="true"></child>
</parent>
2) Parent manually sets this.child.isHappy = true within its ngOnInit lifecycle hook.
Which approach is preferred? In my opinion, approach #2 makes more sense, users don't have to know to set [isHappy]="true" when a Child is hosted by a Parent. On the other hand, I'm aware that it's frowned upon in Angular for components to programmatically change one another, especially if all components are OnPush (please correct me if I'm wrong here).
I want Child to have different behavior based upon where it lives.
How about letting Child component know where it lives with ElementRef:
export class ChildComponent {
hasParent: boolean;
constructor (
private elRef: ElementRef
) {}
ngOnInit() {
const el = this.elRef.nativeElement.parentElement as HTMLElement
this.hasParent = el.localName === 'app-parent'
}
}
stackblitz: https://stackblitz.com/edit/angular-kagdsu
In my opinion 2nd way would work, but that could add couple of things into your solution
Tight coupling between two component
What if parent component don't have child component?
To solve it by better way, I'd suggest you to use Host decorator, that will ask for Parent component dependency from Child component. If that exists the make isHappy property to be true
#Component({...})
export class Child {
#Input() isHappy: boolean = false;
constructor(#Optional() #Host() private parent: Parent) {}
ngOnInit() {
// Do only if parent exists
if (this.parent) {
this.parent.isHappy = true
}
}
}
I understand even the way suggested above has tight coupling between the Parent component. We should think of removing that dependency from the inner child component.
Yes, we can do that by little hackish way, where we would be checking current component's immediate parent component name like below. For achieving the same you had to add ViewContainerRef dependency to get hold of parent/host component.
constructor(private viewContainer: ViewContainerRef) {}
ngOnInit() {
if(this.viewContainer[ '_data' ].componentView.parent.component.constructor.name === 'Parent') {
this.parent.isHappy = true
}
}

Host component changing behavior of child component

Suppose I have the following component Child.
#Component({...})
class Child {
#Input() mySpecialFlag: boolean;
}
Child can either be used as a stand-alone component, or can be wrapped by a Special component, which "scopes" the behavior of Child. When Child is used stand-alone, the user can choose to either set [mySpecialFlag] to true or false. When Child is used within Special, I want the Special component to force the input [mySpecialFlag] to be true, via something like the following:
#Component({...})
class Special {
#ContentChild(Child) child;
...
ngAfterContentInit() {
this.child.mySpecialFlag = true;
}
}
The issue with the above is that it sets [mySpecialFlag] too late. I want to be able to set this flag to true before any of the child's lifecycle methods have run, or at least before the child's ngAfterContentInit has ran. Is this possible?
Setting the flag in ngOnInit of the parent appears to work: the flag is set before ngOnInit of the child component.
ngOnInit() {
this.child.mySpecialFlag = true;
}
See this stackblitz for a demo.
An alternative method to access the child component as early as possible is to associate the #ContentChild decorator to a getter/setter property, and to set the child flag in the setter:
export class ParentComponent {
private _child: ChildComponent;
#ContentChild(ChildComponent) get child(): ChildComponent {
return this._child;
}
set child(val: ChildComponent) {
this._child = val;
this._child.mySpecialFlag = true;
}
}
See this stackblitz for a demo. Please note that the private variable _child and the getter are not needed to set the flag. I added them in case you need to refer to the child component for other purposes.
I believe you could use ngOnInit method. Refer to this link explaining angular Lifecycle hooks: https://angular.io/guide/lifecycle-hooks

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