I’m trying to pass data from child to parent component with a variable value from child to parent, unfortunately no changes are being made in parent component. Any ideas whats going on? Or does @Output decorator strictly works only with events that come from clicks in child component? I also would expact that it doesnt matter if i use method for eventemitter or not, so for example setting emit for eventemitters from inside constructor or ngOnInit methods, i tried it and still doesnt work.
Child component:
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-server',
templateUrl: './server.component.html',
styles: [
`.online {
color: white;
}
`
]
})
export class ServerComponent {
serverId: number = 10;
serverStatus: string = 'offline';
serverName: 'Random server';
@Output() serverNameEmitter = new EventEmitter<string>();
constructor() {
this.serverStatus = Math.random() > 0.5 ? 'online' : 'offline';
}
ngOnInit(): void {
this.passServerName(this.serverName);
}
passServerName(serverName: string) {
this.serverNameEmitter.emit(serverName);
}
getColor() {
return this.serverStatus == 'online' ? 'green' : 'red';
}
getServerStatus() {
return this.serverStatus;
}
}
Parent component
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-servers',
templateUrl: './servers.component.html',
styleUrls: ['./servers.component.css'],
})
export class ServersComponent implements OnInit {
allowNewServer = false;
serverCreationStatus = 'No server was created!';
serverNames = "";
serverStatus = false;
servers = [];
serverName = {
name: "Test server"
};
constructor() {
setTimeout(() => {
this.allowNewServer = true;
}, 2000);
}
ngOnInit(): void {}
onCreateServer() {
this.serverStatus = true;
this.servers.push(this.serverName.name);
this.serverCreationStatus = 'Server has been created! Name is ' this.serverName.name;
}
onUpdateServerName(event: Event) {
this.serverNames = (event.target as HTMLInputElement).value;
}
changeServerName() {
this.serverName.name = this.serverNames;
}
gettingServerName(serverName: string) {
this.serverNames = serverName;
}
}
Parent component HTML
<label>Server Name</label>
<input type="text" (input)="onUpdateServerName($event)">
<!--<input type="text" [(ngModel)]="serverName.name">-->
<!--<p>{{serverName}}</p>-->
<button [disabled]="!allowNewServer"
(click)="onCreateServer()">Add Server</button>
<!--<p [innerText]="allowNewServer"></p>-->
<!--<p>{{serverCreationStatus}}</p>-->
<p>Here's {{ serverNames }}</p>
<p *ngIf="serverStatus; else noServer">Server was created, server name is {{
serverName.name }}</p>
<ng-template #noServer>
<p>No server was created!</p>
</ng-template>
<button (click)="changeServerName()">Change server name</button>
<app-server *ngFor="let server of servers"
(serverNameEmitter)="gettingServerName($event)"></app-server>
CodePudding user response:
Ok, I have created a Stackblitz project with your code. But i don't understand something.... You set a new server name in the parent component. Then you can add a new server. But you never set the name to the child if it created. So the child emit the event (it works) and you set the serverNames to "Random server" always.
The only thing I saw: You got a ExpressionChangedAfterItHasBeenCheckedError if you emit the serverName in the child in onInit(). Simple trick: setTimeout
.
But see the Stackblitz and play with it. If you have other question let me know.
Greetings, Flo
CodePudding user response:
You can just trigger change-detection manually everytime gettingServerName()
is invoked. This way you can avoid the ExpressionChangedAfterItHasBeenCheckedError mentioned by Flo. No need to use a timeout.
Just import ChangeDetectorRef
:
import { ChangeDetectorRef } from '@angular/core';
Inject it via your constructor and then call detectChanges()
at the end of the gettingServerName()
method:
constructor(private cdRef: ChangeDetectorRef) {}
gettingServerName(serverName: string) {
this.serverNames = serverName;
this.cdRef.detectChanges();
}
Though I would like to add: You don't really use @Output
the way it is intended.