Angular Events
Events let your template react to user actions.
Event Binding Essentials
- Bind with
(event)
to run a component method;$event
is the native Event. - Use common DOM events like
(click)
,(input)
, and key filters like(keyup.enter)
. - Debounce handlers to limit work during fast input.
- Bubbling: Child events bubble up; call
$event.stopPropagation()
when needed.
<button (click)="onClick()">Click</button>
<input (input)="onInput($event)" (keyup.enter)="submit()">
<div (click)="onParentClick()">
<button (click)="onChildClick($event)">Child</button>
</div>
Note: See Templates for markup and interpolation, Data Binding for property/two-way binding, and Conditional Rendering for showing/hiding content.
Basic Events
- Handle
(click)
to update component state. - Read input values from
$event.target
(cast or use$any
when needed). - Track the last key pressed via
(keyup)
.
<button (click)="increment()">Click me</button>
<input (input)="onInput($event)" (keyup)="lastKey = $any($event).key">
Example
Handle common events and keep component state in sync with user input:
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<h3>Events</h3>
<p>Count: {{ count }}</p>
<button (click)="increment()">Click me</button>
<div style="margin-top:12px">
<input placeholder="Type..." (input)="onInput($event)" (keyup)="lastKey = $any($event).key">
<p>Value: {{ value }}</p>
<p>Last key: {{ lastKey }}</p>
</div>
`
})
export class App {
count = 0;
value = '';
lastKey = '';
increment() { this.count++; }
onInput(e: Event) { this.value = (e.target as HTMLInputElement).value; }
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
(click)="increment()"
: Calls the component method to increase thecount
.(input)="onInput($event)"
: Reads the input's current text from$event.target.value
and stores it invalue
.(keyup)="lastKey = $any($event).key"
: Stores the last pressed key inlastKey
.- Display:
{{ count }}
,{{ value }}
, and{{ lastKey }}
show the component fields.
Notes:
- Keep handlers small: Do minimal work in event handlers; delegate heavy work to services.
- Type the event: Narrow
$event
or use$any($event.target)
when reading inputs. - Propagation/default: Use
$event.stopPropagation()
/$event.preventDefault()
when needed.
Event Filtering (keyup.enter)
- Use key aliases to run handlers only on specific keys (e.g., Enter).
- Keep UI reactive by updating state on filtered events.
<input (keyup.enter)="add()" (keyup)="lastKey = $any($event).key">
Example
Filter keyboard events with key aliases like keyup.enter
:
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
styles: [`
.toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
ul { margin-top: 10px; }
li { line-height: 1.8; }
input[type="text"] { padding: 6px 8px; }
`],
template: `
<h3>Event Filtering (keyup.enter)</h3>
<div class="toolbar">
<input type="text" placeholder="Add item and press Enter"
[value]="draft"
(input)="draft = $any($event.target).value"
(keyup)="lastKey = $any($event).key"
(keyup.enter)="add()">
<button (click)="add()">Add</button>
<button (click)="clear()" [disabled]="items.length === 0">Clear</button>
<span style="margin-left:8px;color:#666">Last key: {{ lastKey }}</span>
</div>
<ul>
<li *ngFor="let it of items; let i = index">{{ i + 1 }}. {{ it }}</li>
</ul>
`
})
export class App {
draft = '';
lastKey = '';
items = ['Buy milk', 'Learn Angular'];
add() {
const v = (this.draft || '').trim();
if (!v) return;
this.items = [...this.items, v];
this.draft = '';
}
clear() { this.items = []; }
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
(keyup.enter)="add()"
: Runs only when the Enter key is pressed, adding the current draft.[value]="draft"
/(input)
: Keeps the input element and thedraft
field in sync.- List: New items are appended immutably and rendered with
*ngFor
;lastKey
shows the last pressed key. - Buttons: "Add" and "Clear" call component methods to update the list state.
Notes:
- Use key aliases: Use
(keyup.enter)
instead of checking key codes manually. - Immutable updates: Add/remove items with new array references (e.g., spread) to keep change detection predictable.
Debounced Input
- Delay updates until typing pauses to avoid excessive work.
- Use
setTimeout
or RxJS to debounce input changes.
// Pseudo
handle: any;
onInput(e) {
clearTimeout(handle);
handle = setTimeout(() => doWork(e), 400);
}
Example
Debounce user input to reduce work during fast typing:
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<h3>Debounced Input</h3>
<input type="text" placeholder="Type here" (input)="onInput($event)">
<p>Immediate: {{ immediate }}</p>
<p>Debounced (400ms): {{ debounced }}</p>
`
})
export class App {
immediate = '';
debounced = '';
private handle: any;
onInput(e: Event) {
const v = (e.target as HTMLInputElement)?.value ?? '';
this.immediate = v;
clearTimeout(this.handle);
this.handle = setTimeout(() => this.debounced = v, 400);
}
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
- Immediate vs debounced:
immediate
updates on every input;debounced
updates after 400ms of no typing. onInput(e)
: Readse.target.value
, setsimmediate
, clears any pending timer, and schedules a new timeout to setdebounced
.- Timer handle:
handle
stores the timeout ID so it can be cleared on the next keystroke.
Notes:
- Cleanup timers: Clear pending timers on each input to avoid stale updates.
- Right delay: Choose a debounce that fits UX (e.g., 300-500ms).
- For streams, consider RxJS in advanced cases.