How do I check if Output in Component changes? Then run another method
Here is Parent component,
After it gets data from Child, want to immediately run another event.
Parent HTML:
<div>
Address Type:*
<app-address-type-dropdown (selectedItemOutput) = "test"></app-address-type-dropdown>
</div>
Parent Typescript:
Goal: When value is outputted, detect changes in this parent, and write console command.
export class AddressFormatheaderFormComponent implements OnInit {
constructor() { }
public test: any;
public sayHi(){
console.log(this.test);
}
ngOnInit() {
}
}
You can do so by creating another function, an event handler essentially. So when your child component <app-address-type-dropdown> emits a value, this event handler will take care of what to do next.
E.g. onNewItemSelect($event) is the event handler.
<div>
Address Type:*
<app-address-type-dropdown (selectedItemOutput)="onNewItemSelect($event)"></app-address-type-dropdown>
</div>
export class AddressFormatheaderFormComponent implements OnInit {
constructor() { }
public test: any;
ngOnInit() {
}
onNewItemSelect(itemSelected){
this.test = itemSelected;
console.log(this.test);
//do something else
}
}
Do have read on this section of of Angular official docs on component interaction for more information.
I think you will need EventEmitter so in child component, when value changes, it will emit event and in parent component, it will detect changes and call parent function.
Something like let's say your child component.
import { Component, EventEmitter, Output } from '#angular/core';
#Component({
selector: 'app-address-type-dropdown,
template: `<button class='btn btn-primary' (click)="valueChanged()">Click me</button> `
})
export class AppAddressTypeDropdown{
#Output() selectedItemOutput= new EventEmitter();
Counter = 0;
valueChanged() { // You can give any function name
this.counter = this.counter + 1;
this.selectedItemOutput.emit(this.counter);
}
}
And in parent html, just try update call slightly. Please call any function to know when it changes.
<app-address-type-dropdown (selectedItemOutput) = "changeDetect($event)"></app-address-type-dropdown>
export class AddressFormatheaderFormComponent implements OnInit {
constructor() { }
public test: any;
public sayHi(){
console.log(this.test);
}
ngOnInit() {
}
changeDetect(counter){
console.log(counter);
//do something here
}
Related
i am trying to understand the callback ngOnChanges() so i created the below posted example. but at the compile time despite the interface Post
has values for its attributes title and content respectively, however, i do not receive any logs from ngOnChanges
please let me know how to use correctly
app.component.ts:
import { Component, OnInit, OnChanges, SimpleChanges,Output, EventEmitter } from '#angular/core';
export interface Post {
title:string;
content:string;
}
#Component({
selector: 'app-post-create',
templateUrl: './post-create.component.html',
styleUrls: ['./post-create.component.css']
})
export class PostCreateComponent implements OnInit {
#Output() post : Post;
#Output() onPostSubmittedEvtEmitter: EventEmitter<Post> = new EventEmitter<Post>();
constructor() {
this.post = {} as Post;
}
ngOnInit(): void {
}
ngOnChanges(changes: SimpleChanges) {
for (let changedProperty in changes) {
console.log("ngOnChanges->: changes[changedProperty].previousValue: " + changes[changedProperty].previousValue);
console.log("ngOnChanges->: changes[changedProperty].currentValue):" + changes[changedProperty].currentValue);
}
}
onSubmitPost(post: Post) {
this.post = {
title: this.post.title,
content: this.post.content
};
this.onPostSubmittedEvtEmitter.emit(this.post);
console.log("onSubmitPost->: post.title: " + post.title);
console.log("onSubmitPost->: post.content:" + post.content);
}
}
update 05.04.2021
as recommended i have added the ngOnChanges to observe changes in a prpoperty annotated with Input decorator as follows:
#Input() postsToAddToList: Post[] = [];
now, when I compile the code i add some values, i receive the following logs from ngOnChanges :
ngOnChanges->: changes[changedProperty].previousValue: undefined
post-list.component.ts:20 ngOnChanges->: changes[changedProperty].currentValue):
but the problem is when i keep adding more values, i do not receive any logs from the ngOnChanges
please let me know why despite i keep adding more values that result in changing the contents of the object that is decorated with #Input??!
post-list.component.ts:
import { Component, Input,OnInit, OnChanges, SimpleChanges,Output, EventEmitter } from '#angular/core';
import { Post } from '../post-create/post-create.component';
#Component({
selector: 'app-post-list',
templateUrl: './post-list.component.html',
styleUrls: ['./post-list.component.css']
})
export class PostListComponent implements OnInit {
constructor() {}
#Input() postsToAddToList: Post[] = [];
ngOnInit(): void {}
ngOnChanges(changes: SimpleChanges) {
for (let changedProperty in changes) {
console.log("ngOnChanges->: changes[changedProperty].previousValue: " + changes[changedProperty].previousValue);
console.log("ngOnChanges->: changes[changedProperty].currentValue):" + changes[changedProperty].currentValue);
}
}
}
ngOnChanges() only gets called when component's inputs changed from the parent component(fields that marked with #Input decorator). But you have #Output fields. The idea of ngOnChanges() is to react to changes that were done by the parent.
Following your business logic, you can handle whatever you want straight in onSubmitPost.
Answer for the update 05.04.2021
You add values to the array itself. Since the link to the array hasn't changed, ngOnChanges() does not catch these changes. But if you put new link to the component and do the following in the parent:
component:
this.yourArrInTheParent = [...this.yourArrInTheParent];
template:
<app-post-lis [postsToAddToList]="yourArrInTheParent"></app-post-lis>
Now value that you passed to the input changed and you will see the changes in the ngOnChanges(). The same goes for objects if you change object's property, angular won't see it as a change in ngOnChanges() since it only detects changes in #Input() values.
In order to catch those changes, you can use ngDoCheck hook. But it is power consuming, bear in mind not to perform heavy calculations there.
I think you are doing in correct way. Its just you missing to implement onChanges class. In latest Angular versions it straight throws error but in older version it does not.
Try this.
export class PostListComponent implements OnInit, OnChanges{
constructor() {}
#Input() postsToAddToList: Post[] = [];
ngOnInit(): void {}
ngOnChanges(changes: SimpleChanges) {
for (let changedProperty in changes) {
console.log("ngOnChanges->: changes[changedProperty].previousValue: " +
changes[changedProperty].previousValue);
console.log("ngOnChanges->: changes[changedProperty].currentValue):" +
changes[changedProperty].currentValue);
}
}
}
As already pointed out by #Vadzim Lisakovich
ngOnChanges() only gets called when component's inputs changed from
the parent component
Now, the thing is that the input is compared using === operator i.e. shallow comparison. If you add something to the post array, the reference to the array stays the same thus no event is triggered.
To fix that you can implement ngDoCheck() or replace the reference.
Here is a very similar question to yours:
Angular2 change detection: ngOnChanges not firing for nested object
And of cause the documentation:
https://angular.io/guide/lifecycle-hooks#docheck
I'm creating a reusable component which can be shown from any external component, but can be hidden using a function in same component, but somehow the property change in parent component is not updating child.
Here is the stackblitz for the same.
https://stackblitz.com/edit/angular-hfjkmu
I need "Show" button should show the component all the time and I can hide the component using "hide" button any time.
you need sync value from child to parent using Output
#Input()
show = false;
#Output()
showChange = new EventEmitter<boolean>();
constructor() { }
ngOnInit() {
}
hide(){
this.show = false;
this.showChange.emit(this.show);
}
<app-show-hide [(show)]="show"></app-show-hide>
The show property from child do not pointing to same prop in the parent comp, because it's primitive value.
I don't recommend to modify data that not belong to child component (reference type, eg: object, array), it can lead to unexpected behavior.
Online demo with reference type (be careful when modify ref type): https://stackblitz.com/edit/angular-vhxgpo?file=src%2Fapp%2Fshow-hide-obj%2Fshow-hide-obj.component.tsenter link description here
You have the problem because your child component modify Input value within your child component scope so no way parent component know the data is change
Your child component
export class ShowHideComponent implements OnInit {
#Input('show') show: boolean;
#Output() updateShowValue: EventEmitter<any> = new EventEmitter<
any
>();
constructor() { }
ngOnInit() {
console.log(this.show);
}
hide() {
this.updateShowValue.emit(!this.show);
}
}
In the app.component.html
<app-show-hide [show]="show" (updateShowValue)="update($event)"></app-show-hide>
And app.component.ts
export class AppComponent implements OnInit {
show:boolean = false;
ngOnInit() {
this.show = false;
console.log(this.show)
}
showComp(){
this.show = !this.show;
}
update(event) {
this.show = event;
}
}
You need to add an #Output in your child component, when you click the hide button (in the child component) you need to notify your parent component and change the value of show variable to false, this is done with the EventEmitter.
Changes to made are :
ShowHideComponent.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-show-hide',
templateUrl: './show-hide.component.html'
})
export class ShowHideComponent {
#Input('show') show : boolean;
#Output('') hideEE = new EventEmitter();
constructor() { }
hide(){
this.hideEE.emit(false);
}
}
AppComponent.ts
import { Component,OnInit } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
show:boolean = false;
}
appComponent.html
<button type="button" (click)="show = true">Show</button>
<app-show-hide [show]="show" (hideEE)="show = $event"></app-show-hide>
stackblitz Link
This question related to Syntactically anonymous/Arrow Function/add-hoc/factory DP functions:
I have a component which is embedded in the Html.
The component has a click event which is binded to a function. This function content depend on another component which has a reference to this component.
This is the component with the click event:
HTML:
<div (click)="doSomething()">Content.....</div> \\ Should it be with a brackets ?
In the component I just want to define the function signature:
#Component({
selector: 'app-embedded'
})
export class className
{
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) => any; //The function get a boolean parameter as input and return void or any
}
Now this is where the component is embedded:
<div >
<app-embedded #emb></app-embedded>
</div>
This is the component of the container of the embedded component, which has a reference to the embedded component:
#Component({
selector: 'app-container',
})
export class container
{
#ViewChild('emb') private emb: ElementRef;
booleanParam : booelan;
constructor()
{
emb.doSomething = containerFunction(true);
}
containerFunction(booleanParam : boolean)
{
// do something in this context
}
}
The idea is that this embedded component is embedded in many other containers and whenever the click event triggered a function that was set in the doSomething function variable should be executed.
What changes in the code I need to do in order to accomplish this ?
The best way i see of doing this would be to simply use an event emitter and capture the event on the other side? so embedded would have this:
#Component({
selector: 'app-embedded'
})
export class className
{
#Output()
public something: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
this.something.emit(booleanparams);
}; //The function get a boolean parameter as input and return void or any
}
Then where it is called:
<div >
<app-embedded #emb (something)="doSomething($event)"></app-embedded>
</div>
Other solution that would allow a return
#Component({
selector: 'app-embedded'
})
export class className
{
#Input()
public somethingFunc: (boolean)=>any;
constructor() { }
ngOnInit() { }
doSomething:(booleanparams: boolean) {
let w_potato = this.somethingFunc(booleanparams);
//Do whatever you want with w_potato
}; //The function get a boolean parameter as input and return void or any
}
in this case the view would be
<div >
<app-embedded #emb [somethingFunc]="doSomething"></app-embedded>
</div>
I hope this helps! Passing the function or emitting an event will be much more angular than trying to modify an instance of a component. On top of that, a constructor is only called once when Angular starts up so #emb at that time will not be defined to be anything. If you wanted to do it that way you would have to bind yourself in something ngAfterViewInit.
But again, I think that passing it through attributes will be much more angular looking.
Good Luck let me know if this doesn't suit your answer.
I was just wondering if there's any way, shape or form to reference a function that's created within ngOnInit(), or some sort of closure you can create to do so?
Basically:
component(){
somefunc()
//be able to call the function that's created in ngOnInit from the component via
//click event after the component renders
ngOnInit() {
function somefunc(){ ...whatever }
}
}
Is there any way to do this?
It can be done by assigning the method to a class member property in ngOnInit. In the code below, I define the method as an arrow function, to make sure that this refers to the instance of the component in the body of the method. See this stackblitz for a demo.
export class AppComponent implements OnInit {
public onButtonClick: (event: Event) => void; // Member property will refer to the method
ngOnInit() {
let data = "And this comes from the closure!"; // Can be used inside of onButtonClick
// Assign the method to the member property
this.onButtonClick = (event: Event): void => {
console.log("The button was clicked!", data);
...
};
}
}
The method can then be used as an event handler:
<button (click)="onButtonClick($event)">Click me!</button>
Your pseudo syntax is a bit confusing.
You can call a created function like this:
import { Component, OnInit } from '#angular/core';
#Component({
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
showImage = false;
constructor() {
}
ngOnInit(): void {
this.toggleImage();
}
toggleImage(): void {
this.showImage = !this.showImage;
}
}
You can also call it from a click event on a button like this:
<button class='btn btn-primary'
(click)='toggleImage()'>
Show Image
</button>
Is this what you are asking?
I want to reload component after a button click from another component Angular 6.
As #MariyamMohammedJalil said you can use an EventEmitter to trigger the update of your first component.
See following sample:
first.component.ts
#Component({
selector: 'first-component',
template: '<div>{{label}}</label>
})
export class FirstComponent {
#Input() update: EventEmitter<string>;
label = 'First Component';
constructor() {}
ngOnInit() {
if (this.update) {
// Subscribe to the event emitter to receive an update event
this.update.subscribe((value: string) => {
this.refresh(value);
})
}
}
refresh(value: string) {
// Do your stuff here
this.label = value;
}
}
second.component.ts
#Component({
selector: 'second-component',
template: '<button (click)="updateFirstCmp()">Update First Component</button>'
})
export class SecondComponent {
#Input() update: EventEmitter<string>;
constructor(){}
updateFirstCmp() {
// Emit an event to update your first component
this.update.emit('Updated First Component');
}
}
And for example you should add following to your app.component.ts:
updateEventEmitter: EventEmitter<string>;
constructor() {
...
this.updateEventEmitter = new EventEmitter();
}
And in your app.component.html:
<first-component [update]="updateEventEmitter"></first-component>
<second-component [update]="updateEventEmitter"
Another way do solve your problem can be to enter the first.component as input parameter to the second.component to call the refresh function directly without EventEmitter. See following sample:
app.component.html
<first-component #firstComponent></first-component>
<second-component [firstCmp]="firstComponent"></second-component>
second.component.ts
#Component({
selector: 'second-component',
template: '<button (click)="updateFirstCmp()">Update First Component</button>'
})
export class SecondComponent {
#Input() firstCmp: FirstComponent;
constructor(){}
updateFirstCmp() {
// Update the first component directly
this.firstCmp.refresh('Updated First Component');
}
}
With this sample you don't need to subscribe to an update event, because you're not using an EventEmitter.