Skip to the solution
EasyFrontend7 min read

How do child components emit events to parents?

362 views

The problem

Question presented to candidate: "A star-rating component sits inside a product page. When the user picks a rating, how does the child tell the parent, and what exactly happens when it does?"

What a strong answer should cover:

  • Data flows down through inputs and up through outputs; a child never reaches into its parent.
  • Modern API: rated = output<Rating>() and this.rated.emit(value); the decorator form is @Output() rated = new EventEmitter<Rating>().
  • The parent listens with event-binding syntax (rated)="onRated($event)"; $event is the emitted value, not a DOM event.
  • emit() is synchronous: the parent handler runs before emit() returns.
  • Outputs are not DOM events: they do not bubble to grandparents.
  • An input named x plus an output named xChange enables [(x)] two-way binding; model() creates both at once.

Clarifying questions expected:

  • "Is the receiver the direct parent, or something further up or a sibling?" (the answer changes to a shared service)

Code / implementation expected: Yes β€” a child with an output and a parent handling it.

  • output
  • eventemitter

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 9 sections

Read the code

Angularoutput(): a star rating child emitting to its parent
import { Component, input, output, signal } from "@angular/core";

type Rating = { item: string; stars: number };

// CHILD: knows nothing about its parent. It announces what happened.
@Component({
  selector: "star-rating",
  template: `
    <span>{{ item() }}:</span>
    @for (n of [1, 2, 3, 4, 5]; track n) {
      <button (click)="pick(n)">{{ n }}</button>
    }
  `,
})
export class StarRatingComponent {
  item = input.required<string>(); // data IN from the parent
  rated = output<Rating>(); // event OUT to the parent

  pick(stars: number) {
    console.log(`  child emits rated(${this.item()}, ${stars})`);
    this.rated.emit({ item: this.item(), stars });
    console.log(`  child: emit() has returned`);
  }
}

// PARENT: listens with (rated)="..." and owns the state.
@Component({
  selector: "app-root",
  imports: [StarRatingComponent],
  template: `
    <star-rating item="Coffee" (rated)="onRated($event)" />
    <star-rating item="Tea" (rated)="onRated($event)" />
    <p>Last: {{ last() }}</p>
  `,
})
export class AppComponent {
  last = signal("nothing rated");
  onRated(r: Rating) {
    console.log(`  parent handler received`, JSON.stringify(r));
    this.last.set(`${r.item} got ${r.stars} stars`);
  }
}
Angular@Input + @Output EventEmitter with the xChange two-way pattern (runs in the playground)
import { Component, EventEmitter, Input, Output, signal } from "@angular/core";

// The decorator style. Still supported, and the form you will meet in older
// codebases. It is also what the in-browser playground can run.
@Component({
  selector: "like-button",
  template: `<button (click)="toggle()">{{ liked ? "Unlike" : "Like" }} {{ label }}</button>`,
})
export class LikeButtonComponent {
  @Input({ required: true }) label = "";
  @Input() liked = false;
  @Output() likedChange = new EventEmitter<boolean>();

  toggle() {
    // Never assign the input from inside the child and hope the parent
    // notices. Emit, and let the owner of the state decide.
    this.likedChange.emit(!this.liked);
  }
}

@Component({
  selector: "app-root",
  imports: [LikeButtonComponent],
  template: `
    <!-- Because the pair is named liked / likedChange, banana-box syntax works -->
    <like-button label="post A" [(liked)]="a" />
    <like-button label="post B" [liked]="b()" (likedChange)="b.set($event)" />
    <p>A liked: {{ a }} | B liked: {{ b() }}</p>
  `,
})
export class AppComponent {
  a = false;
  b = signal(false);
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 9 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextEasy

What does the Angular CLI provide?

Continue