Felix
Felix

Reputation: 23

Angular 9: Parent function not called on EventEmitter.emit from child

I'm an experienced programmer but pretty new to Angular.

I've just gone through a good Angular tutorial and tried out to practice passing values from a child component to its caller (parent).

Somehow, eventhough I reduce the complexity to minimum, my parent function is not even called, which I've bound to the emitter.

After serving the application (ng serve), I see only the following in the console:

child.component.ts:17 submitButtonClicked called

I stepped into the emit-Call using the VS Code and Debugger for Chrome:

submitButtonClicked() {
  console.log("submitButtonClicked called");
  this.emitter.emit("Test");
}

After two steps, I landed in the Subject.js and saw, that its member variable "observers" is empty. I would expect, that the observers array contain at least the ParentComponent. See the Subject.js code snipped at the end of the page.

I mean, it's really simple but I just can't identify, where I've the mistake :-/

What could be the cause and the solution?

I really would appreciate your help!

Parent (parent.component.html)

 <p>parent works!</p>
 <app-child (submitButtonClicked)="parentFunction($event)"></app-child>

Parent (parent.component.ts)

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss']
})
export class ParentComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

  parentFunction (str: string) {
    console.log("parentFunction called, str = " + str);
  }
}

Child (child.component.html)

<p>child works!</p>
<button type="submit" (click)="submitButtonClicked()">Submit</button>

Child (child.component.ts)

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {

  @Output() emitter = new EventEmitter<string>();
  constructor() { }

  ngOnInit(): void {
  }

  submitButtonClicked() {
    console.log("submitButtonClicked called");
    this.emitter.emit("Test");
  }

}

Root (app.component.html)

<app-parent></app-parent>

Root (app.component.ts)

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'My App';
}

Root Module (app.module.ts)

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { ParentComponent } from './parent/parent.component';
import { ChildComponent } from './child/child.component';


@NgModule({
  declarations: [
    AppComponent,
    ParentComponent,
    ChildComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Subject.js

export class Subject extends Observable {
    constructor() {
        super();
        this.observers = [];
        this.closed = false;
        this.isStopped = false;
        this.hasError = false;
        this.thrownError = null;
    }
    [rxSubscriberSymbol]() {
        return new SubjectSubscriber(this);
    }
    lift(operator) {
        const subject = new AnonymousSubject(this, this);
        subject.operator = operator;
        return subject;
    }
    next(value) {
        if (this.closed) {
            throw new ObjectUnsubscribedError();
        }
        if (!this.isStopped) {
            const { observers } = this;
            const len = observers.length;
            const copy = observers.slice();
            for (let i = 0; i < len; i++) {
                copy[i].next(value);
            }
        }
    }
 ... // Snippet ends here

My Angular environment (ng --version):

Angular CLI: 9.0.7
Node: 12.16.1
OS: win32 x64
Angular: 9.0.7
... animations, cli, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes
Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.900.7
@angular-devkit/build-angular     0.900.7
@angular-devkit/build-optimizer   0.900.7
@angular-devkit/build-webpack     0.900.7
@angular-devkit/core              9.0.7
@angular-devkit/schematics        9.0.7
@ngtools/webpack                  9.0.7
@schematics/angular               9.0.7
@schematics/update                0.900.7
rxjs                              6.5.5
typescript                        3.7.5
webpack                           4.41.2

Upvotes: 1

Views: 2350

Answers (1)

Barremian
Barremian

Reputation: 31145

You don't have to dig deep down. It's a simple fix. The emitter is called emitter. So you have to bind to that name. Try the following

<app-child (emitter)="parentFunction($event)"></app-child>

Or if you wish to bind to submitButtonClicked, then the emitter should be called submitButtonClicked.

@Output() submitButtonClicked = new EventEmitter<string>();

If you wish to learn more about event emitter, you could look at it's source. It is a simple extension of a Rxjs Subject. So when an event emitter is created, it actually is an observable called by the name decorated with the @Output() decorator. So to obtain the values from the emitter, it needs to be subscribed to. And the emitter name is the name of the observable that is being subscribed to.

Upvotes: 0

Related Questions