I simply want to update a boolean value in my parent component on click of a button in my child component. I have a slide-out child component that I hide and show based on a dynamic ngClass. The class is set based on the boolean value from the parent. However when I close that component from a button in the child, I want to update the boolean value in the parent:
The parent component typescript:
export class AppComponent implements OnInit {
showFlyout: boolean
constructor() {}
ngOnInit() {
this.showFlyout = false;
}
}
And parent html:
<main>
<button (click)="showFlyout = !showFlyout"><i class="fa fa-check"></i>Show Flyout</button>
{{showFlyout}}
<app-child id="flyout" [ngClass]="showFlyout ? 'active' : ''"></app-child>
</main>
The child component typescript:
export class ActivateFlyoutComponent implements OnInit {
constructor() { }
ngOnInit() {
}
closeActivationFlyout() {
const flyout = document.getElementById('flyout');
flyout.classList.remove('active');
}
}
And child component html:
<button (click)="closeFlyout()">Close</button>
Here's a Stackblitz. You can see clicking the parent button properly toggles the class but how can I update that boolean from click in the child and therefore make the closeActivationFlyout() method unnecessary in the child component?
Use #Output() like the others have mentioned, but it needs to emit an event and you also need to check for the event being triggered in the parent html.
Here's a working StackBlitz
In the child component.
#Output() onCloseClick = new EventEmitter();
closeFlyout() {
this.onCloseClick.emit();
}
And in the parent components html.
<app-child id="flyout" [ngClass]="showFlyout ? 'active' : ''" (onCloseClick)="showFlyout = false"></app-child>
You can also create a function in the parent component, and trigger that like (onCloseClick)="doFunction()"
You can use two-way databinding to make it works:
AppComponent:
<app-child id="flyout" [(showFlyoutModel)]="showFlyout" [ngClass]="showFlyout ? 'active' : ''"></app-child>
ChildComponent :
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html'
})
export class ChildComponent implements OnInit {
#Input()
showFlyoutModel;
#Output()
showFlyoutModelChange = new EventEmitter<boolean>();
constructor() { }
ngOnInit() {
}
closeFlyout() {
this.showFlyoutModelChange.emit(!this.showFlyoutModel);
}
}
https://stackblitz.com/edit/angular-v95emc?file=app%2Fchild-component%2Fchild.component.ts
Related
I am trying to pass data from the parent to the child. I get the correct output when I pass the string (data1) from parent to child but when I try to display object (data) the console says it's unidentified and nothing is displayed on the screen.
Parent Component:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data={
name:'Charles',
age:24,
email:'charles#gmail.com'
};
data1 = "Charles";
}
<app-contact [sendName] ="data"></app-contact>
Child Component
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent implements OnInit {
#Input() sendName;
constructor() { }
ngOnInit(): void {
console.log(this.sendName);
}
}
<h1 class="text-center">Contact Form</h1>
<h3 class="pb-5">from the parent {{sendName.name}}</h3>
<h3 class="pb-5">from the parent {{sendName.age}}</h3>
please someone help me.
Thank you
If your data (in parent component) variable has static value then your child component will be
#Input() sendName;
ngOnInit(): void {
console.log(this.sendName);
}
If your data (in parent component) variable will assign from dynamic value then your child component will be
(Assume you have an API to call and update sendName variable on API response, but our child component is initialised without waiting the API response then we need to trigger #Input() to send updated data in child component )
#Input() set sendName(value: any) {
if(value){
console.log(value);
this.name = value
// call any function from here
}
}
name:any; // use this variable at anywhere
constructor(){
}
you might be rendering the child, before setting the value in data in parent
in Child component import Onchanges and get from ngOnchanges()
import { Component, OnInit, Input ,Onchanges} from '#angular/core';
export class ContactComponent implements OnInit,Onchanges {
#Input() sendName; constructor() { }
ngOnChanges() {
console.log(this.sendName); }
I have an object from parent component also received in a child component similar to this:
{
attribute: 'aaaa',
attribute2: [
{
value
},
{
value
},
{
value
},
]
}
This object is an #Input from a parent component. When I make changes to the objects inside the attribute2 array, I would like the child component detect that changes were made and then gets updated. As this is an object, I could'nt make it work, so I clone the entire object (this.objet = _.cloneDeep(this.object) in the parent component so then the child component detects that changes happened.
Is there any other way of doing this that does not clone the entire object? Thanks in advance
EDIT:
Child Component
export class ChildComponent implements OnInit, OnChanges {
#Input() public object: any;
}
html
<div>
<span>{{object.attribute}}</span>
<div *ngFor="let items of object.attribute2">{{item.value}}</div>
</div>
Parent Component
export class ParentComponent implements OnInit {
public object: any;
updateObject() {
this.object.attribute2[1] = 'Changed value';
this.object = _.cloneDeep(this.object);
}
}
html
<div>
<child-component [object]="object"></child-component>
</div>
An efficient way is to use EventEmitter and service communication to
trigger changes in the child component.
On way as mentioned by #Tony is to use ngOnChanges(). It is a good shortcut for detecting bounded properties change but as you add more and more bindings, using this hook will affect you application in the long run because it will run every time any of the bound property changes whether or not you desire it all the calls.
So for Service based communication, I've created an example on
Stackblitz:
https://stackblitz.com/edit/angular-fgut7t
Gist: https://gist.github.com/stupidly-logical/a34e272156b498513505127967aec851
In this example, I am binding an Array to the child component using #Input() an on addition of new data, the array is updated by the parent and the latest value is passed on the service which then emits this value. The child component subscribes to this value and the relevant code is executed.
The Service:
import { Injectable, EventEmitter } from '#angular/core';
#Injectable({
providedIn: "root"
})
export class DataService {
dataUpdated:EventEmitter<any> = new EventEmitter();
constructor() { }
setLatestData(data) {
this.dataUpdated.emit(data);
}
}
Child Component TS
import { Component, OnInit, Input } from '#angular/core';
import { DataService } from '../data-service.service';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
#Input() allData: [];
latestData: any;
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.dataUpdated.subscribe((data) => {
this.latestData = data;
});
}
}
Child Component HTML
<p>
Latest Data: {{ latestData }}
</p>
<h3>List:</h3>
<li *ngFor="let data of allData">
{{ data }}
</li>
Parent Component TS
import { Component } from '#angular/core';
import { DataService } from './data-service.service'
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
dataArr = [];
constructor(private dataService: DataService){}
onAddTimestamp() {
let timestamp = new Date();
this.dataArr.push(timestamp);
this.dataService.setLatestData(timestamp);
}
}
Parent Component HTML
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<button
(click)="onAddTimestamp()"
>
Add Timestamp
</button>
<app-child
[allData] = "dataArr"
></app-child>
Use the ngOnChanges() lifecycle method in your component.
ngOnChanges is called right after the data-bound properties have been
checked and before view and content children are checked if at least
one of them has changed.
Some like this
#Input() object: string;
ngOnChanges(changes: SimpleChanges) {
console.log(changes.object.currentValue);
// You can also use object.previousValue and
// object.firstChange for comparing old and new values
}
I want to listen a click event on a child component from a parent component, but the event is not emitting to the parent.
Child Component:
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'child-component',
templateUrl: './child-component.component.html',
})
export class ChildComponent implements OnInit {
#Output() click = new EventEmitter<string>();
constructor() { }
ngOnInit() {
}
onClick() {
console.log('click');
this.click.emit();
}
}
Child template:
<label>
<div (click)="onClick()"></div>
</label>
Parent Component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'parent-component',
templateUrl: './parent-component.component.html',
})
export class ParentComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
onChildClicked() {
console.log('Child Clicked!');
}
}
Parent template:
<input type="checkbox" ngModel (click)="onChildClicked()">
<child-component></child-component>
When I click, I get the first message in the console from the child, but I'm not getting the second message, which tells me the event is not being emitted.
Change parent-template from:
<input type="checkbox" ngModel (click)="onChildClicked()">
<child-component></child-component>
to:
<input type="checkbox" ngModel >
<child-component (click)="onChildClicked()"></child-component>
The child-component emits the event, so the handler needs to be located on the child's tag inside the parent's template and not on some arbitrary element like the input.
Try a value in you emit.
this.click.emit('text');
Also you really need to pick a different name than click since the click output might clash with ng click
Click event should be on the element
<child-component (click)="onChildClicked()"></child-component>
You just have to listen to attach the event emitter to the child-component like so:
https://stackblitz.com/edit/angular-d4hhzj
Tip: when creating output properties in angular make sure that the names you pick are different from the native events.
Edit: otherwise you might experience strange (expected) behaviour. If your event-emitter is called click. when you listen for (click)="handleClick()" in the parent, handleClick will actually be called twice
I am new to Angular 7 (2+) & trying my hands on #Input & #Output. However, passing data from Parent to Child component via #Input is understood & in place.
However, very basic on the other hand passing data from Child to Parent component via using #Output concept is understood & but the implementation is not getting right.
Here is what I am trying. When a button is clicked in the
Child component, a property in the parent component should be
converted to Upper case & displayed.
ChildComponent.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
templateUrl: './child.component.html',
})
export class ChildComponent implements OnInit {
#Input('child-name') ChildName: string;
#Output() onHit: EventEmitter<string> = new EventEmitter<string>();
constructor() { }
ngOnInit() {}
handleClick(name): void {
this.onHit.emit(name);
}}
ChildComponent.html
<h1> child works! </h1>
<button (click)="handleClick('eventEmitter')"> Click me! </button>
ParentComponent.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
console.log(e);
}}
ParentComponent.html
<div style="text-align:center">
<app-child [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b (onHit)="catchChildEvent($event)">
<i> {{customerName}} </i>
</b>
No error in console or binding
From the above snippet, when the button in Child Component is clicked the parent Component's property CustomerName should get the value & displayed?
Example: https://stackblitz.com/edit/angular-3vgorr
(onHit)="catchChildEvent($event)" should be passed to <app-child/>
<app-child [child-name]="HelloChild" (onHit)="catchChildEvent($event)"></app-child>
You are emitting event from app-child component so attach the handler for app-child component to make it work.
<div style="text-align:center">
<app-child (onHit)="catchChildEvent($event)" [child-name]="HelloChild"></app-child>
//Trying to bind to Custom event of child component here
<b>
<i> {{customerName}} </i>
</b>
And within the ts file update value of cutomerName property.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
title = 'my-dream-app';
customerName: string = "";
catchChildEvent(e) {
this.customerName = e;
}
}
You should move (onHit)="catchChildEvent($event)" to app-child in parent html:
<app-child [child-name]="HelloChild"
(onHit)="catchChildEvent($event)>
</app-child>
I have a parent component
#Component({
selector: 'mve-trace-multi-filter',
template: `
<child [itemsReady]="itemsReady$ | async"></child>
`
})
export class ParentComponent {
itemsReady$ : Subject<any> = new Subject();
ngOnInit() {
this.store.select('state').subscribe(data => this.itemsReady$.next(data));
}
}
And a child component with onPush
changeDetection: ChangeDetectionStrategy.OnPush
export class ChildComponent {
#Input() set itemsReady( items ) {
console.log('change', items);
}
}
}
The problem is that when the next() method is run, there is not change detection in the child component. I am excepting the child component to be updated because of the async pipe. What is wrong?
I saw the log as null in the first time but that's it.