Skip to the solution
MediumFrontend8 min read

Explain what output() and outputFromObservable() are in modern Angular and how they differ from @Output().

978 views

The problem

Question presented to candidate: "A search box component debounces keystrokes with RxJS and needs to expose the result as an output. Explain output(), outputFromObservable() and outputToObservable(), and how this differs from putting an EventEmitter behind @Output()."

What a strong answer should cover:

  • output() is imperative: you call emit(). outputFromObservable(obs$) is declarative: every value the observable pushes is an event.
  • Both implement OutputRef; the parent binds either one with (search)="...".
  • outputFromObservable subscribes to the source once per listener, so a cold source or an expensive pipe runs per listener unless shared.
  • Completion stops the output; errors are not handled by Angular, so catch them in the pipe.
  • outputToObservable(ref) goes the other way, for consumers that want RxJS operators.
  • @Output() with EventEmitter was a Subject you pushed into; these APIs keep RxJS optional and tie subscriptions to the component lifecycle.

Clarifying questions expected:

  • "Is the source observable hot or cold, and could more than one listener attach?"

Code / implementation expected: Yes β€” a component with one output() and one outputFromObservable() fed by a debounced pipeline.

  • signals
  • output

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

8 min read Β· 10 sections

Read the code

AngularDebounced search: output() plus outputFromObservable() (needs the Angular compiler)
import { Component, output, signal } from "@angular/core";
import { outputFromObservable } from "@angular/core/rxjs-interop";
import { Subject, debounceTime, distinctUntilChanged, filter, map } from "rxjs";

// CHILD: one imperative output and one output fed by an RxJS pipeline.
@Component({
  selector: "search-box",
  template: `
    <input placeholder="Search" (input)="keystrokes.next($any($event.target).value)" />
    <button (click)="cleared.emit()">Clear</button>
  `,
})
export class SearchBoxComponent {
  protected keystrokes = new Subject<string>();

  // output(): you call emit() yourself.
  cleared = output<void>();

  // outputFromObservable(): the observable IS the output. Each value it pushes is an event.
  search = outputFromObservable(
    this.keystrokes.pipe(
      map(v => v.trim()),
      filter(v => v.length >= 2),
      debounceTime(300),
      distinctUntilChanged(),
    ),
  );
}

@Component({
  selector: "app-root",
  imports: [SearchBoxComponent],
  template: `
    <search-box (search)="onSearch($event)" (cleared)="log.set([])" />
    <p>Searches: {{ log().join(", ") || "none" }}</p>
  `,
})
export class AppComponent {
  log = signal<string[]>([]);
  onSearch(term: string) {
    console.log(`  parent got search("${term}")`);
    this.log.update(l => [...l, term]);
  }
}

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 22 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 nextMedium

How do functional route guards and lazy loadComponent work in modern Angular?

Continue