I want to pass data from child to parent component but it doesn't work in a proper way: function isn't invoked.
<router-outlet (activeElement)='getActive($event)'></router-outlet>
<div class="myDIV">
<button *ngFor="let i of numberOfButtons" (click)="navigate(i)" [ngClass]="(i === active) ? 'btn active': 'btn'">{{i}}</button>
</div>
child component ts.file
#Output() activeElement = new EventEmitter();
constructor(private activatedRoute:ActivatedRoute,private getPlanets:GetPlanetsService,private router: Router, private renderer:Renderer2,private elRef: ElementRef) {
activatedRoute.params.subscribe(value=>{
this.fivePlanetIndex=value.id;
this.activeElement.emit(value.id);
});
}
parent component .ts file
getActive(i){
console.log(i); //it is not invoked
}
Output will work by using component selector, not with router-outlet.
Because router-outlet used to render multiple components and it doesn't make sense to set all Inputs and Outputs on there.
If you wanna use router-outlet and catch events from children components, you can use Observables; where child component send result and parent component subscribe to it.
sample.service.ts
// you will use subject to emit event from child
subject = new Subject<any>();
// you will use observable to listen to events from parent
observable = this.subject.asObservable();
child.component.ts
constructor(service: SampleService) {}
// instead of emit function
this.service.subject.next();
parent.component.ts
constructor(service: SampleService) {}
// instead of event listener
this.service.observable.subscribe();
Related
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.
I want to do a component reusable, setting multiple children and render one component at time, I have a collection of views got with #ContenChildren, at parent component, something like this:
#ContentChildren(ChildComponent) children: QueryList<ChildComponent>;
//getCurrentChild function
getCurrentChild(){
//index is a position of child
return this.children.toArray()[this.index];
}
and render parent template:
<ng-container *ngTemplateOutlet="getCurrentChild().content"></ng-container>
content is rendered in the child component with #ViewChild:
#ViewChild('currentChild') child;
and the template looks like this:
<ng-template #currentChild>
<ng-content>
</ng-content>
</ng-template>
When I want to implement this structure, I do something like this:
<parent>
<child>
<specific-one></specific-one>
</child>
<child>
<specific-two></specific-two>
</child>
</parent>
Now I have a method in the parent component that is fired on click button and need to call a method in the specific components (specific-one or specific-two depending on currentChild):
export class SpecificOneComponent implements OnInit{
action(){
//this action will be called when parent is clicked
}
}
I've tried calling method trough child reference but the action doesn't exit. also passing a context and neither. It looks like the way I'm setting the content in parent template is not right.
Any help would be appreciate it.
Every child component will have its own content, so this means that it would be enough to only get the reference to the ng-template in which there would be ng-content.
Also, because you want to call methods from specific components when a certain event occurs on the parent, we are going to use a service in order to be able to notify the specific components.
specific.service.ts
private _shouldCallAction$ = new Subject();
shouldCallAction$ = this._shouldCallAction$.asObservable();
constructor() { }
triggerAction (uniqueCompId) {
this._shouldCallAction$.next(uniqueCompId);
}
specific-{one|two|...n}.component.ts
This goes for every component that depends on some events that occur in the parent component.
private shouldCallActionSubscription: Subscription;
uniqueId: number | string;
constructor(private specificService: SpecificService) {
this.uniqueId = randomId();
}
ngOnInit() {
this.shouldCallActionSubscription = this.specificService.shouldCallAction$
.pipe(
filter(id => id === this.uniqueId)
)
.subscribe(() => {
console.log('calling action for specific-one')
});
}
ngOnDestroy () {
this.shouldCallActionSubscription.unsubscribe();
}
child.component.html
<ng-container *ngIf="instanceIdx === crtIdx">
<h3>trigger action of current specific component</h3>
<button (click)="triggerAction()">trigger</button>
</ng-container>
<ng-template #currentChild>
<ng-content></ng-content>
</ng-template>
child.component.ts
Here you'll also need to get a reference to the specificComponent in order to get its unique id.
// Before class
let instances = 0;
#ViewChild('currentChild', { static: true }) tpl: TemplateRef<any>;
#ContentChild('specific', { static: true }) specificComp;
get uniqueSpecificCompId () {
return this.specificComp.uniqueId;
}
constructor (private specificService: SpecificService) {
this.instanceIdx = instances++;
}
triggerAction () {
this.specificService.triggerAction(this.uniqueSpecificCompId);
}
parent.component.ts
#ViewChildren(ChildOneComponent) children: QueryList<ChildOneComponent>;
#ViewChild('container', { static: true, read: ViewContainerRef }) container: ViewContainerRef;
crtIdx = 0;
ngAfterViewInit () {
this.setNewView();
}
setNewView () {
this.container.clear();
this.container.createEmbeddedView(this.children.toArray()[this.crtIdx].tpl);
}
updateIndex (idx) {
this.crtIdx = idx;
this.setNewView();
}
parent.component.html
<app-child [crtIdx]="crtIdx">
<!-- ... -->
<app-specific-two #specific></app-specific-two>
</app-child>
<app-child [crtIdx]="crtIdx">
<!-- ... -->
<app-specific-one #specific></app-specific-one>
</app-child>
<ng-container #container></ng-container>
<h3>Select a child</h3>
<button
*ngFor="let _ of [].constructor(n); let idx = index;"
(click)="updateIndex(idx)"
>
Select {{ idx + 1 }}
</button>
Here is the demo.
Best of luck!
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.
If I have multiple levels of angular components, how can I use #Output to emit an event from child to the grand parent?
Grandparent:
<parent (handleClick)="grandmaHandleClick($event)">
<parent>
...
grandmaHandleClick(event) {
console.log('grandma knows you clicked')
}
Parent:
<child (handleClick)="handleClick($event)">
</child>
Child:
<div (click)="onClick()">Click button
</div>
...
#Output() handleClick = new EventEmitter
onClick() {
this.handleClick.emit('clicked a button')
}
I am trying to have it so that #Output can prop drill a few components deep, whats the best way to accomplish this, and can you provide example?
There could be 2 ways:
Using #output:
Grandparent
<parent (notifyGrandParent)="grandmaHandleClick($event)">
<parent>
...
grandmaHandleClick(event) {
console.log('grandma knows you clicked')
}
Parent:
<child (handleClick)="childEvent($event)">
</child>
#Output() notifyGrandParent= new EventEmitter();
childEvent(event) {
this.notifyGrandParent.emit('event')
}
Child is implemented properly in the code so it is good to go.
Using BehaviorSubject via Service: With this much level of nesting, you can actually create some service like EventService, and then create BehaviorSubject which can directly be subscribed by the GrandParent. Also, to make this service more component specific, you can keep this service in a module which will have other 3 components (GrandParent, Parent and Child)
export class EventService{
private childClickedEvent = new BehaviorSubject<string>('');
emitChildEvent(msg: string){
this.childClickedEvent.next(msg)
}
childEventListner(){
return this.childClickedEvent.asObservable();
}
}
and then in components:
ChildComponent
export class ChildComponent{
constructor(private evtSvc: EventService){}
onClick(){
this.evtSvc.emitChildEvent('clicked a button')
}
}
GrandParent
export class GrandComponent{
constructor(private evtSvc: EventService){}
ngOnInit(){
this.evtSvc.childEventListner().subscribe(info =>{
console.log(info); // here you get the message from Child component
})
}
}
Please note that, with #output event, you create a tight coupling of components and so a strong dependency (parent-child-grandchild) is created. If the component is not reusable and is only created to serve this purpose, then #output will also make sense because it'll convey the message to any new developer that they have parent-child relationship.
Creating a service to pass data also exposes the data to other components which can inject service in constructor.
So, the decision should be taken accordingly.
Use rxjs/subject, it can be observer and observable in the same time.
Usage:
Create Subject property in service:
import { Subject } from 'rxjs';
export class AuthService {
loginAccures: Subject<boolean> = new Subject<boolean>();
}
When event happens in child page/component use:
logout() {
this.authService.loginAccures.next(false);
}
And subscribe to subject in parent page/component:
constructor(private authService: AuthService) {
this.authService.loginAccures.subscribe((isLoggedIn: boolean) => {
this.isLoggedIn = isLoggedIn;
})
}
I have two sibling components inside a parent component like below
<parentComponent>
<sibling1></sibling1>
<sibling2></sibling2>
</parentComponent>
Am emitting data from sibling 2 to parent. Then am passing it as Input from parent to sibling 1. But as the sibling 1 gets initialized before sibling 2 am unable to get the data on sibling 1. How to get the data on sibling 1 with the same setup.
When you receive data in parent component you need run a callback function to update sibling1 data. In order to run a callback in parent you can do something like this.
SIBLING2:
class Sibling2 {
#Output() private onChange: EventEmitter<string> = new EventEmitter<string>();
ngOnInit () {
this.onChange.emit("hello parent")
}
}
PARENT:
class Parent {
private parentData: string = null;
ngOnInit () {
this.onChange.emit("hello parent")
}
onSibling2Change(data) {
this.parentData = data; //this will update sibling1 data
}
}
HTML:
<parentComponent>
<sibling1 [data]="parentData"></sibling1>
<sibling2 (onChange)="onSibling2Change($event)"></sibling2>
</parentComponent>