Event Binding
Event binding lets your template react to user interactions and DOM events — clicks, key presses, form input, focus changes, and any native or custom event. Where property binding pushes data into the DOM, event binding pulls signals out of it, wiring user actions to component logic. In modern Angular this is the foundation of interactivity, and it pairs naturally with signals to drive reactive state updates.
The (event) syntax
You bind to an event by wrapping the target event name in parentheses on the left of the assignment, and putting a template statement on the right. The statement runs every time the event fires.
<button (click)="increment()">Add one</button>
The name inside the parentheses is the DOM event name — click, input, keyup, submit, mouseenter, and so on. The right-hand side is a template statement: typically a method call, but it can also be an assignment or a small expression. Unlike interpolation, statements may have side effects.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<p>Count: {{ count() }}</p>
<button (click)="increment()">Add one</button>
<button (click)="count.set(0)">Reset</button>
`,
})
export class CounterComponent {
count = signal(0);
increment(): void {
this.count.update((n) => n + 1);
}
}
Note the second button calls count.set(0) inline — perfectly valid as a template statement. Keep inline statements short; move anything non-trivial into a method.
Accessing the event with $event
Every event handler has access to a special variable named $event, which is the payload the DOM (or a child component) emits. For native DOM events this is the standard event object, so you get full type information when you cast the target.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-search',
standalone: true,
template: `
<input
type="text"
placeholder="Type to search"
(input)="onInput($event)"
/>
<p>You typed: {{ term() }}</p>
`,
})
export class SearchComponent {
term = signal('');
onInput(event: Event): void {
const input = event.target as HTMLInputElement;
this.term.set(input.value);
}
}
Because event.target is typed as the broad EventTarget, you cast it to the concrete element (HTMLInputElement) to read value. For keyboard events the object is a KeyboardEvent, so $event.key, $event.code, and modifier flags like $event.ctrlKey are available.
Tip: Avoid the pattern
(input)="term.set($event.target.value)". Angular’s template type-checking can’t narrow$event.targetto an input element, and it produces a type error in strict mode. Cast inside a method instead.
Key event filters (pseudo-events)
For keyboard events Angular offers a convenient filtering syntax so you don’t have to inspect $event.key manually. You append the key name to the event using a dot.
<input (keyup.enter)="submit()" />
<input (keydown.escape)="cancel()" />
<input (keyup.control.s)="save($event)" />
The handler only fires when the named key (and any listed modifiers) match. This keeps templates declarative and removes boilerplate if (event.key === 'Enter') checks.
Common DOM events
| Binding | Fires when | $event type |
|---|---|---|
(click) | Element is clicked | MouseEvent |
(input) | Input value changes | Event |
(change) | Value committed (blur/select) | Event |
(submit) | A form is submitted | SubmitEvent |
(keyup) / (keydown) | A key is released/pressed | KeyboardEvent |
(focus) / (blur) | Element gains/loses focus | FocusEvent |
(mouseenter) | Pointer enters element | MouseEvent |
Preventing default behaviour
Because the statement runs real code, you can call preventDefault() or stopPropagation() directly on the event. This is common for form submission where you want to handle the data in TypeScript rather than reload the page.
import { Component } from '@angular/core';
@Component({
selector: 'app-login-form',
standalone: true,
template: `
<form (submit)="onSubmit($event)">
<input name="email" #email />
<button type="submit">Sign in</button>
</form>
`,
})
export class LoginFormComponent {
onSubmit(event: SubmitEvent): void {
event.preventDefault();
console.log('Submitting without reload');
}
}
Output:
Submitting without reload
Custom events from components
Event binding isn’t limited to DOM events. Child components expose output() emitters, and you bind to them with the same parentheses syntax. Here $event is whatever value the child emits.
import { Component, output } from '@angular/core';
@Component({
selector: 'app-rating',
standalone: true,
template: `
@for (star of [1, 2, 3, 4, 5]; track star) {
<button (click)="rate.emit(star)">★ {{ star }}</button>
}
`,
})
export class RatingComponent {
rate = output<number>();
}
<app-rating (rate)="onRate($event)" />
onRate(value: number): void {
console.log(`User rated: ${value}`);
}
The parent receives the emitted number through $event, fully typed thanks to output<number>().
Best Practices
- Keep template statements short — delegate any logic beyond a single set/update call to a component method.
- Cast
$event.targetto the concrete element type (e.g.HTMLInputElement) inside a method rather than inline, to satisfy strict template type-checking. - Use key pseudo-events like
(keyup.enter)instead of manualevent.keycomparisons for cleaner, declarative templates. - Type your handler parameters precisely (
MouseEvent,KeyboardEvent,SubmitEvent) so the compiler verifies your usage. - Update state through signals (
set/update) in handlers to keep change detection efficient and predictable. - Call
event.preventDefault()in form(submit)handlers to take full control of the data flow.