Angular event binding uses the (event) syntax to listen to either native DOM events (click, input, submit, keydown, etc.) or custom events emitted by child components via @Output(). When the event fires, Angular runs the bound template statement/method with access to $event, and (in typical setups) triggers change detection for affected views. Events trigger change detection, so test performance under rapid input and large templates.
Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
How does Angular event binding work (DOM events vs @Output events), and what are the common pitfalls?Frontend interview answer
This Angular interview question tests whether you can explain Angular event binding vs @Output: what's the difference, connect it to production trade-offs, and handle common follow-up questions.
- Angular event binding vs @Output: what's the difference explanation without falling back to memorized docs wording
- Events and Binding reasoning, edge cases, and production failure modes
- How you would answer the most likely Angular interview follow-up
Core idea(event)="..." attaches a listener from the template to a template statement (usually a component method). It’s one-way: view ➜ component. The event can be a native DOM event or a custom event emitted by a child component (@Output()).
Event source | Template syntax | What $event is | Typical use |
|---|---|---|---|
Native DOM event |
| A DOM | User interactions on elements |
Child component @Output |
| Whatever the child | Child ➜ parent communication |
Basic syntax
Angular uses parentheses to bind an event name to a handler. Use $event to access the event payload.
<button type="button" (click)="inc()">+</button>
<input
type="text"
(input)="onInput(($event.target as HTMLInputElement).value)"
/>
<form (submit)="onSubmit($event)">
<button type="submit">Save</button>
</form>
<!-- key filtering -->
<input (keyup.enter)="save()" (keydown.escape)="cancel()" />
import { Component } from '@angular/core';
@Component({
selector: 'app-demo',
templateUrl: './demo.component.html'
})
export class DemoComponent {
count = 0;
inc(): void {
this.count += 1;
}
onInput(value: string): void {
// avoid event: any; pass the extracted value
console.log('value:', value);
}
onSubmit(e: SubmitEvent): void {
e.preventDefault();
// submit logic
}
save(): void {}
cancel(): void {}
}
Custom events with @Output()
Child emits; parent listens with the same (event) syntax. The payload type is whatever you emit (and should be typed).
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button type="button" (click)="save()">Save</button>`
})
export class ChildComponent {
@Output() saved = new EventEmitter<{ id: string }>();
save(): void {
this.saved.emit({ id: 'u1' });
}
}
// parent.html
// <app-child (saved)="onSaved($event)"></app-child>
// parent.ts
// onSaved(payload: { id: string }) { ... }
Interview-grade pitfalls | What goes wrong | Fix |
|---|---|---|
Using | You lose type safety and misuse | Use concrete types (e.g. |
Heavy logic in template statements | Hard to test/read; can run often and become brittle | Keep templates thin; call a method and do logic in TS |
Forgetting default browser behavior | Forms reload page; links navigate unexpectedly | Use |
Event propagation surprises | Nested clicks trigger parent handlers unexpectedly | Use |
OnPush expectations | UI doesn’t update when state changes outside Angular awareness | Events in templates normally trigger checks; for external sources use signals/async pipe or |
Using EventEmitter outside @Output | Misused as a general event bus; awkward lifecycle and ownership | Use RxJS Subjects/services for cross-component events; keep EventEmitter for outputs |
<!-- parent template -->
<input
(input)="onQuery(($event.target as HTMLInputElement).value)"
(keydown.enter)="submitFromKeyboard()"
/>
<form (submit)="onSubmit($event)">
<button type="submit">Save</button>
</form>
<app-editor (saved)="onSaved($event)"></app-editor>
What interviewers usually ask next
DOM events and output events share the same template syntax, but they do not behave the same. A DOM submit event still has browser defaults, so you may need preventDefault(). A child @Output() does not bubble through the DOM; Angular calls the parent handler because the parent bound to that output explicitly. Under rapid input, every handler still runs inside Angular's event flow, so expensive parsing or filtering logic can turn into a change-detection hotspot quickly.
(event) binds template events to component logic. For DOM events, $event is a DOM event object; for @Output(), $event is the emitted payload. Keep handlers typed, keep templates thin, and be deliberate about default behaviors and propagation.
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.