Why #Output doesn't emit anything (in angular 8) - javascript

I'm trying to transfer an object to a pot component via #Output.
But emit doesn't return anything and when you put it in the log it's undefined.
This is my code:
Transmitter component :
#Output() menubuttonclicked = new EventEmitter<object>();
.
.
.
clickedmenu(id: string) {
this.rclickemit.buttonid = id ;
this.menubuttonclicked.emit(this.rclickemit);
}
Transmitter html:
<button *ngFor ="let button of Setting.menu" (click)="clickedmenu(button.id)" mat-menu-item>
<mat-icon>{{button.icon}}</mat-icon>
<span>{{button.name}}</span>
</button>
Recipient Component:
ngOnChanges(changes: SimpleChanges): void {
if (changes['emit']) {
console.log(this.emit);
}
}
But nothing can be printed on the console.
I gave the emit variable to get the output but no value in it.
Does anyone know the problem?
Update
Recipient Component:
<app-ir-graph-d3 [node]="nodes" [link]="links" [Setting]="Setting" (menubuttonclicked)="emit"></app-ir-graph-d3>

Output event emitter is used as an attribute with parentheses. For example:
In your recipient template:
<app-transmitter-component (menubuttonclicked)="onMenuButtonClicked($event)"></app-transmitter-component>
In your recipient component:
onMenuButtonClicked = (buttonID) => {
console.log(buttonID)
}
For further reading: https://angular.io/guide/component-interaction
ngOnChanges is called when a change happend at data-bound properties such as #Input properties.
OnChange: https://angular.io/api/core/OnChanges

You need to handle of Output. Let me show an example:
yourOutputComponent.ts:
export class yourOutputComponent implements OnInit {
#Output() change = new EventEmitter();
onClick(){
this.change.emit({newValue: 'I am your data' });
}
}
and subscribed component fooComponent.html:
<yourOutput (change)="hello($event)"></yourOutput>
fooComponent.ts:
export class fooComponent implements OnInit {
hello(event:any) {
console.log(event);
}
}

Wherever in the Transmitting component you have used Recipient Component Please write eventEmmiter variable, and listen that event in the Recipient Component.
<Recipient Component (menubuttonclicked) = "matButtonClicked($event)">
Recipient Component
matButtonClicked(event) {
console.log(event) // 'this.rclickemit'
}

You should listen to the changes in the html of the Recipient Component not the ngOnChanges
<recipient-component (menubuttonclicked)='clickHandler($event)'></recipient-component>
Then in the typescript file, you declare teh clickHandler function and receive the input there.

Related

ngOnChanges do not display changes expected in a variable

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

How to pass data from child component to parent component when button clicked on parent component

I need to pass input's value from child component to parent component when user click on a submit button that exists in parent component.
childComp template
<input
type="password"
[(ngModel)]="userPasswordForm.inputId"
class="mr-password-field k-textbox"
/>
childComp TS file
export class PasswordInputComponent{
constructor() { }
#Output() inputValue = new EventEmitter<string>();
userPasswordForm:any={'input':''};
emitValue(value: string) {
this.inputValue.emit(value);
}
}
Parent Component Template
<child-component (inputValue)="" > </child-component>
<button (click)="getValueFromChild()"> </button>
Parent Component TS file
tempUserFormPasswords:any=[];
.
.
.
getValueFromChild(receivedVal){
this.tempUserFormPasswords.push(receivedVal);
}
It would easy to dio it if the button exists inside the child component. but in this case the value should be passed when the button in the parent component is clicked!
For single ChildComponent:
Use ViewChild
For multiple ChildComponent use: ViewChildren
Parent Component TS file
Single Child Component:
tempUserFormPasswords:any=[];
#ViewChild(ChildComponent) child: ChildComponent;
.
.
.
getValueFromChild(receivedVal){
var data = child.getData();
this.tempUserFormPasswords.push(data);
}
Multiple Child Component:
tempUserFormPasswords:any=[];
#ViewChildren(ChildComponent) child: ChildComponent;
#ViewChildren(ChildComponent) children: QueryList<ChildComponent>;
.
.
.
getValueFromChild(receivedVal){
let data;
children.forEach(child => (data = this.updateData(child.data));
this.tempUserFormPasswords.push(data);
}
Create a BehaviorSubject in service file
#Injectable()
export class dataService {
data: BehaviorSubject<any> = new BehaviorSubject<any>(null);
public setData(data: any){
this.data.next(data);
}
public getData(): Observable<any> {
return this.data.asObservable();
}
}
You need to subscribe the data in your child component
PasswordInputComponent
export class PasswordInputComponent{
constructor(private service: dataService) {
this.service.getData().subscribe((data) => {
//Emit the event here
this.inputValue.emit(value);
});
}
#Output() inputValue = new EventEmitter<string>();
userPasswordForm:any={'input':''};
emitValue(value: string) {
this.inputValue.emit(value);
}
}
ParentComponent.ts
tempUserFormPasswords:any=[];
.
.
.
constructor(private service: dataService) { }
getValueFromChild(receivedVal){
this.service.setData('');
this.tempUserFormPasswords.push(receivedVal);
}
When a button clicked on the parent component we are setting the data behaviour subject, when a new value added to that it will automatically subscribed in child component.so, on that time we need to emit a event.
I think this will help you..
Read about Input and Output decorators in angular!
documentation: sharing-data.
Examples: examples
You can do it with ViewChild as already said in the other answer from #Raz Ronen. But keep in mind that depending on the Angular version, you might need to wait for the AfterViewInit lifecycle hook to be executed to interact with the child (or the child won't be available since it's not initialized).
Also, you can do it with a BehaviorSubject, like #Msk Satheesh just answered, and it's perfectly fine too. But it might be considered a bit overkill for such a simple use case.
(this is what we usually do when we don't have a relation between the components e.g one component is not children of the other one)
What I suggest is I think the simplest of all (again, their answers are not bad by any means);
It is basically the same of #Msk Satheesh (but under the hood), just a bit more Angular styled: Output + EventEmitter:
Parent component:
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['./parent.component.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}
Children Component:
import { Component, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<button (click)="sendMessage()">Send Message</button>
`,
styleUrls: ['./child.component.css']
})
export class ChildComponent {
message: string = "a string from child component"
#Output() messageEvent = new EventEmitter<string>();
constructor() { }
sendMessage() {
this.messageEvent.emit(this.message)
}
}
With the code, the parent will always be subscribed to the messageEvent that’s outputted by the child component, and it will run the function (the message function) after the child emits. Handling this with Angular has the advantage that we are sure that we don't have any memory leak in our app (e.g missing unsubscriptions).
When the component that is listening (the subscribed parent) gets destroyed, Angular will unsubscribe automatically to avoid potential memory leaks.

Angular: Check when Output Variable in Component Changes?

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
}

function variable dynamic setting

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.

Handle #Input and #Output for dynamically created Component in Angular 2

How to handle/provide #Input and #Output properties for dynamically created Components in Angular 2?
The idea is to dynamically create (in this case) the SubComponent when the createSub method is called. Forks fine, but how do I provide data for the #Input properties in the SubComponent. Also, how to handle/subscribe to the #Output events the SubComponent provides?
Example:
(Both components are in the same NgModule)
AppComponent
#Component({
selector: 'app-root'
})
export class AppComponent {
someData: 'asdfasf'
constructor(private resolver: ComponentFactoryResolver, private location: ViewContainerRef) { }
createSub() {
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []);
ref.changeDetectorRef.detectChanges();
return ref;
}
onClick() {
// do something
}
}
SubComponent
#Component({
selector: 'app-sub'
})
export class SubComponent {
#Input('data') someData: string;
#Output('onClick') onClick = new EventEmitter();
}
You can easily bind it when you create the component:
createSub() {
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length, this.location.parentInjector, []);
ref.someData = { data: '123' }; // send data to input
ref.onClick.subscribe( // subscribe to event emitter
(event: any) => {
console.log('click');
}
)
ref.changeDetectorRef.detectChanges();
return ref;
}
Sending data is really straigthforward, just do ref.someData = data where data is the data you wish to send.
Getting data from output is also very easy, since it's an EventEmitter you can simply subscribe to it and the clojure you pass in will execute whenever you emit() a value from the component.
I found the following code to generate components on the fly from a string (angular2 generate component from just a string) and created a compileBoundHtml directive from it that passes along input data (doesn't handle outputs but I think the same strategy would apply so you could modify this):
#Directive({selector: '[compileBoundHtml]', exportAs: 'compileBoundHtmlDirective'})
export class CompileBoundHtmlDirective {
// input must be same as selector so it can be named as property on the DOM element it's on
#Input() compileBoundHtml: string;
#Input() inputs?: {[x: string]: any};
// keep reference to temp component (created below) so it can be garbage collected
protected cmpRef: ComponentRef<any>;
constructor( private vc: ViewContainerRef,
private compiler: Compiler,
private injector: Injector,
private m: NgModuleRef<any>) {
this.cmpRef = undefined;
}
/**
* Compile new temporary component using input string as template,
* and then insert adjacently into directive's viewContainerRef
*/
ngOnChanges() {
class TmpClass {
[x: string]: any;
}
// create component and module temps
const tmpCmp = Component({template: this.compileBoundHtml})(TmpClass);
// note: switch to using annotations here so coverage sees this function
#NgModule({imports: [/*your modules that have directives/components on them need to be passed here, potential for circular references unfortunately*/], declarations: [tmpCmp]})
class TmpModule {};
this.compiler.compileModuleAndAllComponentsAsync(TmpModule)
.then((factories) => {
// create and insert component (from the only compiled component factory) into the container view
const f = factories.componentFactories[0];
this.cmpRef = f.create(this.injector, [], null, this.m);
Object.assign(this.cmpRef.instance, this.inputs);
this.vc.insert(this.cmpRef.hostView);
});
}
/**
* Destroy temporary component when directive is destroyed
*/
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}
The important modification is in the addition of:
Object.assign(this.cmpRef.instance, this.inputs);
Basically, it copies the values you want to be on the new component into the tmp component class so that they can be used in the generated components.
It would be used like:
<div [compileBoundHtml]="someContentThatHasComponentHtmlInIt" [inputs]="{anInput: anInputValue}"></div>
Hopefully this saves someone the massive amount of Googling I had to do.
createSub() {
const factory = this.resolver.resolveComponentFactory(SubComponent);
const ref = this.location.createComponent(factory, this.location.length,
ref.instance.model = {Which you like to send}
ref.instance.outPut = (data) =>{ //will get called from from SubComponent}
this.location.parentInjector, []);
ref.changeDetectorRef.detectChanges();
return ref;
}
SubComponent{
public model;
public outPut = <any>{};
constructor(){ console.log("Your input will be seen here",this.model) }
sendDataOnClick(){
this.outPut(inputData)
}
}
If you know the type of the component you want to add i think you can use another approach.
In your app root component html:
<div *ngIf="functionHasCalled">
<app-sub [data]="dataInput" (onClick)="onSubComponentClick()"></app-sub>
</div>
In your app root component typescript:
private functionHasCalled:boolean = false;
private dataInput:string;
onClick(){
//And you can initialize the input property also if you need
this.dataInput = 'asfsdfasdf';
this.functionHasCalled = true;
}
onSubComponentClick(){
}
Providing data for #Input is very easy. You have named your component app-sub and it has a #Input property named data. Providing this data can be done by doing this:
<app-sub [data]="whateverdatayouwant"></app-sub>

Categories