# Event Handling

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Core Angular Concepts
Source: https://www.devtutorials.ai/learn/angular/core-concepts/event-handling

> How Angular handles click, keyboard, input and change events, reading the event object, passing parameters to handlers, and preventing the browser's default behavior.

## Introduction to Events

An **event** is a signal the browser sends when something happens on the page, such as a click, a key press or a change to a form field. **Event handling** is the code that runs in response.

In Angular, the template listens for an event and calls a method in the component class. The method updates the class data, and Angular updates the page.

### Real-World Example

On a checkout page, clicking **Apply** validates a coupon code, pressing **Enter** in the address field moves focus to the next field, and changing the delivery option recalculates the total.

Submitting the form must not reload the page, so the handler stops the browser's default submit and sends the order to the API instead.

<figure className="ngc-fig">
<svg viewBox="0 0 640 120" role="img" aria-label="A user action fires an event, the event binding calls a handler method, the method changes data, and Angular updates the view.">
<defs><marker id="ngc-e1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-orange"><rect x="6" y="40" width="112" height="60" rx="9" /><text x="62.0" y="67" textAnchor="middle" style={{fontSize:"14px"}}>User action</text><text className="ngc-sub" x="62.0" y="85" textAnchor="middle">click, key, change</text></g></g><line className="ngc-link ngc-flow c-slate" x1="120" y1="70" x2="132" y2="70" markerEnd="url(#ngc-e1)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-solid c-red"><rect x="134" y="40" width="112" height="60" rx="9" /><text x="190.0" y="67" textAnchor="middle" style={{fontSize:"14px"}}>(event)</text><text className="ngc-sub" x="190.0" y="85" textAnchor="middle">event binding</text></g></g><line className="ngc-link ngc-flow c-slate" x1="248" y1="70" x2="260" y2="70" markerEnd="url(#ngc-e1)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="262" y="40" width="112" height="60" rx="9" /><text x="318.0" y="67" textAnchor="middle" style={{fontSize:"14px"}}>Handler</text><text className="ngc-sub" x="318.0" y="85" textAnchor="middle">class method</text></g></g><line className="ngc-link ngc-flow c-slate" x1="376" y1="70" x2="388" y2="70" markerEnd="url(#ngc-e1)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-blue"><rect x="390" y="40" width="112" height="60" rx="9" /><text x="446.0" y="67" textAnchor="middle" style={{fontSize:"14px"}}>Data</text><text className="ngc-sub" x="446.0" y="85" textAnchor="middle">class property</text></g></g><line className="ngc-link ngc-flow c-slate" x1="504" y1="70" x2="516" y2="70" markerEnd="url(#ngc-e1)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="518" y="40" width="112" height="60" rx="9" /><text x="574.0" y="67" textAnchor="middle" style={{fontSize:"14px"}}>View</text><text className="ngc-sub" x="574.0" y="85" textAnchor="middle">updated page</text></g></g>
</svg>
<figcaption>An event runs a method. The method changes data. Angular updates the page.</figcaption>
</figure>

## Event Binding Review

You listen for an event with **event binding**: the event name in round brackets, followed by a template statement.

```html
<button (click)="save()">Save</button>
```

- `(click)` is the event to listen for.
- `save()` is the statement Angular runs when the event fires. It usually calls a method of the component class.

Angular supports every standard DOM event this way, such as `click`, `dblclick`, `keydown`, `keyup`, `input`, `change`, `focus`, `blur` and `submit`.

## Click Events

The `click` event fires when the user presses and releases the mouse button on an element, or presses Enter or Space on a focused button. It is the most common event in any app.

```angular runnable title="Click events update a shopping cart"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <p>Cart: {{ quantity }} item(s) · ₹{{ quantity * price }}</p>
    <button (click)="increase()">+ Add</button>
    <button (click)="decrease()" [disabled]="quantity === 0">− Remove</button>
    <button (dblclick)="quantity = 0">Double-click to empty</button>
  `,
})
export class App {
  quantity = 1;
  price = 799;

  increase() {
    this.quantity++;
  }

  decrease() {
    if (this.quantity > 0) this.quantity--;
  }
}
```

The third button listens for `dblclick`, which fires only after two quick clicks.

## Keyboard Events

Keyboard events fire when the user presses keys while an element has focus.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="Keys are pressed; the keyup.enter filter lets only Enter call the handler.">
<defs><marker id="ngc-k1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Key pressed</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">a, b, Enter</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-k1)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>(keyup.enter)</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">Angular checks the key</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-k1)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Only Enter</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">other keys stop here</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-k1)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>search()</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">your method runs</text></g></g><circle className="ngc-packet c-blue" r="5"><animateMotion dur="3.6s" repeatCount="indefinite" path="M78 106 L562 106" /></circle><text className="ngc-note" x="320" y="128" textAnchor="middle">Angular calls the method only for the key you name in the event.</text>
</svg>
<figcaption>A key filter such as keyup.enter calls the method only for that key.</figcaption>
</figure>

- `keydown` fires when a key goes down. It repeats while the key is held.
- `keyup` fires when the key is released.

Angular adds **key event filters**. `(keyup.enter)` runs only for the Enter key, and `(keydown.escape)` runs only for Escape. You can combine modifier keys: `(keydown.control.s)`.

```angular runnable title="Key filters run code only for specific keys"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input #box placeholder="Search products" (keyup.enter)="search(box.value)" (keydown.escape)="box.value = ''; result = 'Search cleared'" />
    <p>{{ result }}</p>
  `,
})
export class App {
  result = 'Type a word and press Enter. Press Escape to clear.';

  search(term: string) {
    this.result = term ? 'Searching for "' + term + '"…' : 'Enter a search term first.';
  }
}
```

Key filter names are the browser's `KeyboardEvent.key` values in lower case, such as `enter`, `escape`, `tab`, `space`, `arrowup` and `arrowdown`. Modifier keys come first: `(keydown.shift.enter)`.

## Input Events

The `input` event fires on every change to the value of an `<input>` or `<textarea>`, including typing, pasting and deleting. Use it for live feedback, such as a character counter.

```angular runnable title="The input event updates a counter on every key press"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <textarea rows="3" cols="40" maxlength="140" (input)="onInput($event)"></textarea>
    <p [style.color]="remaining < 20 ? '#dc2626' : '#374151'">{{ remaining }} characters left</p>
  `,
})
export class App {
  limit = 140;
  remaining = 140;

  onInput(event: Event) {
    const text = (event.target as HTMLTextAreaElement).value;
    this.remaining = this.limit - text.length;
  }
}
```

## Change Events

The `change` event fires when the user **commits** a change. The timing depends on the element:

- For `<select>`, checkboxes and radio buttons, it fires as soon as the selection changes.
- For text inputs, it fires only when the field loses focus after its value changed.

```angular runnable title="Change events recalculate the delivery cost"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <label>Delivery:
      <select (change)="setDelivery($event)">
        <option value="standard">Standard (₹0)</option>
        <option value="express">Express (₹99)</option>
        <option value="sameday">Same day (₹199)</option>
      </select>
    </label>
    <p><label><input type="checkbox" (change)="giftWrap = !giftWrap" /> Gift wrap (₹49)</label></p>
    <p>Order total: ₹{{ subtotal + deliveryCost + (giftWrap ? 49 : 0) }}</p>
  `,
})
export class App {
  subtotal = 1200;
  deliveryCost = 0;
  giftWrap = false;
  costs: Record<string, number> = { standard: 0, express: 99, sameday: 199 };

  setDelivery(event: Event) {
    const choice = (event.target as HTMLSelectElement).value;
    this.deliveryCost = this.costs[choice];
  }
}
```

| Point | `input` | `change` |
|---|---|---|
| Text fields | Fires on every key press | Fires when the field loses focus |
| `<select>`, checkbox, radio | Fires on selection | Fires on selection |
| Best for | Live feedback: counters, search-as-you-type | Committed values: filters, settings, totals |

## Event Objects (`$event`)

`$event` is a special variable in an event binding. It contains the **event object** that the browser creates for the event.

<figure className="ngc-fig">
<svg viewBox="0 0 640 134" role="img" aria-label="The browser creates an event object, the template passes it as $event, and the method reads target.value.">
<defs><marker id="ngc-k2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-solid c-blue"><rect x="6" y="30" width="145" height="64" rx="9" /><text x="78.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>Browser</text><text className="ngc-sub" x="78.5" y="77" textAnchor="middle">creates the event</text></g></g><line className="ngc-link ngc-flow c-slate" x1="152" y1="62" x2="166" y2="62" markerEnd="url(#ngc-k2)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-orange"><rect x="167" y="30" width="145" height="64" rx="9" /><text x="239.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>$event</text><text className="ngc-sub" x="239.5" y="77" textAnchor="middle">type, key, target</text></g></g><line className="ngc-link ngc-flow c-slate" x1="313" y1="62" x2="327" y2="62" markerEnd="url(#ngc-k2)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="328" y="30" width="145" height="64" rx="9" /><text x="400.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>onInput($event)</text><text className="ngc-sub" x="400.5" y="77" textAnchor="middle">template passes it</text></g></g><line className="ngc-link ngc-flow c-slate" x1="474" y1="62" x2="488" y2="62" markerEnd="url(#ngc-k2)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="489" y="30" width="145" height="64" rx="9" /><text x="561.5" y="59" textAnchor="middle" style={{fontSize:"14px"}}>target.value</text><text className="ngc-sub" x="561.5" y="77" textAnchor="middle">the method reads it</text></g></g><circle className="ngc-packet c-orange" r="5"><animateMotion dur="3.6s" repeatCount="indefinite" path="M78 106 L562 106" /></circle><text className="ngc-note" x="320" y="128" textAnchor="middle">$event carries details about what happened: the element, the key, the text.</text>
</svg>
<figcaption>$event moves the event details from the template to your method.</figcaption>
</figure>

The type of the object depends on the event:

| Event | Object type | Useful properties |
|---|---|---|
| `click` | `MouseEvent` | `clientX`, `clientY`, `ctrlKey`, `target` |
| `keydown`, `keyup` | `KeyboardEvent` | `key`, `code`, `shiftKey`, `target` |
| `input`, `change` | `Event` | `target` (the element, with its `value`) |
| `submit` | `SubmitEvent` | `submitter`, `target` |

```angular runnable title="$event gives the handler details about the event"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <input placeholder="Press any key" (keydown)="onKey($event)" />
    <p>{{ lastKey }}</p>
    <button (click)="onClick($event)">Click me (try with Ctrl or Shift)</button>
    <p>{{ lastClick }}</p>
  `,
})
export class App {
  lastKey = 'No key pressed yet';
  lastClick = 'No click yet';

  onKey(event: KeyboardEvent) {
    this.lastKey = 'Key: ' + event.key + ' (code: ' + event.code + ')';
  }

  onClick(event: MouseEvent) {
    this.lastClick = event.ctrlKey ? 'Clicked with Ctrl' : event.shiftKey ? 'Clicked with Shift' : 'Normal click';
  }
}
```

`event.target` is typed as a general `EventTarget`. To read `value`, tell TypeScript the element type: `(event.target as HTMLInputElement).value`.

## Passing Parameters to Event Handlers

A handler can receive any values from the template, not only `$event`. You pass them as normal method arguments: `(click)="select(plan)"`.

This is how one method serves many elements, such as a list where each row has its own Delete button.

```angular runnable title="Each button passes its own item to the same method"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <ul>
      @for (file of files; track file; let i = $index) {
        <li>{{ file }} <button (click)="remove(i, file)">Delete</button></li>
      }
    </ul>
    <p>{{ message }}</p>
  `,
})
export class App {
  files = ['report.pdf', 'invoice.xlsx', 'logo.png'];
  message = '';

  remove(index: number, name: string) {
    this.files.splice(index, 1);
    this.message = 'Deleted ' + name;
  }
}
```

You can pass both your own values and the event: `(click)="remove(i, file, $event)"`.

## Preventing Default Behavior

Many elements have a **default behavior** that the browser runs after the event. A form submit reloads the page, a link click opens a new address, and a right-click opens the context menu.

<figure className="ngc-fig">
<svg viewBox="0 0 640 234" role="img" aria-label="Without preventDefault the form reloads the page; with it the handler runs and the page stays.">
<defs><marker id="ngc-k3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><text className="ngc-title c-red" x="160" y="22" textAnchor="middle">Without preventDefault()</text><text className="ngc-title c-green" x="480" y="22" textAnchor="middle">With preventDefault()</text><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-red"><rect x="30" y="36" width="260" height="46" rx="9" /><text x="160.0" y="64" textAnchor="middle" style={{fontSize:"13px"}}>Submit clicked</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="83" x2="160" y2="99" markerEnd="url(#ngc-k3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-red"><rect x="30" y="100" width="260" height="46" rx="9" /><text x="160.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>Browser sends the form</text><text className="ngc-sub" x="160.0" y="138" textAnchor="middle">default behavior</text></g></g><line className="ngc-link ngc-flow c-slate" x1="160" y1="147" x2="160" y2="163" markerEnd="url(#ngc-k3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-red"><rect x="30" y="164" width="260" height="46" rx="9" /><text x="160.0" y="184" textAnchor="middle" style={{fontSize:"13px"}}>Page reloads</text><text className="ngc-sub" x="160.0" y="202" textAnchor="middle">data on screen is lost</text></g></g><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-green"><rect x="350" y="36" width="260" height="46" rx="9" /><text x="480.0" y="64" textAnchor="middle" style={{fontSize:"13px"}}>Submit clicked</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="83" x2="480" y2="99" markerEnd="url(#ngc-k3)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-green"><rect x="350" y="100" width="260" height="46" rx="9" /><text x="480.0" y="120" textAnchor="middle" style={{fontSize:"13px"}}>event.preventDefault()</text><text className="ngc-sub" x="480.0" y="138" textAnchor="middle">default is stopped</text></g></g><line className="ngc-link ngc-flow c-slate" x1="480" y1="147" x2="480" y2="163" markerEnd="url(#ngc-k3)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-green"><rect x="350" y="164" width="260" height="46" rx="9" /><text x="480.0" y="184" textAnchor="middle" style={{fontSize:"13px"}}>Your code runs</text><text className="ngc-sub" x="480.0" y="202" textAnchor="middle">page stays</text></g></g>
</svg>
<figcaption>preventDefault() stops the browser action, so only your code runs.</figcaption>
</figure>

Call `event.preventDefault()` in the handler to stop the default behavior. Your own code still runs.

```angular runnable title="preventDefault keeps the form submit inside Angular"
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <form (submit)="onSubmit($event)">
      <input name="email" placeholder="Email" value="asha@example.com" />
      <button type="submit">Subscribe</button>
    </form>
    <p><a href="https://angular.dev" (click)="onLink($event)">Read the docs</a></p>
    <p>{{ status }}</p>
  `,
})
export class App {
  status = 'Nothing submitted yet';

  onSubmit(event: SubmitEvent) {
    event.preventDefault();
    this.status = 'Subscribed without reloading the page';
  }

  onLink(event: MouseEvent) {
    event.preventDefault();
    this.status = 'Link click handled by Angular';
  }
}
```

Click Subscribe and the link. The page stays in place and the status updates.

In real forms, Angular's forms package provides `(ngSubmit)`, which prevents the page reload for you. You will use it in the Forms chapter.

| Method | What it stops |
|---|---|
| `event.preventDefault()` | The browser's default action: submit, navigation, context menu |
| `event.stopPropagation()` | The event reaching parent elements' handlers |

## Common Mistakes

<div className="ngc-mistakes">

| Mistake | What you see | Fix |
|---|---|---|
| Missing parentheses: `(click)="save"` | Nothing happens on click | Call the method: `(click)="save()"` |
| Reading `$event.target.value` in a TypeScript handler | `Property 'value' does not exist on type 'EventTarget'` | Cast: `(event.target as HTMLInputElement).value` |
| Using `change` for live text feedback | The counter updates only after leaving the field | Use the `input` event |
| Form submit without `preventDefault()` | The page reloads and loses its state | Call `event.preventDefault()`, or use `(ngSubmit)` |
| `(keyup.Enter)` with a capital E | The handler never runs | Key names are lower case: `(keyup.enter)` |
| Heavy work in a `keydown` handler | Typing feels slow | Keep handlers short, or react on `keyup.enter` |

</div>

## Check Yourself

## Exercises

## What to Remember

- Listen for events with `(event)="handler()"`, and use key filters such as `(keyup.enter)` for specific keys.
- Use `input` for live feedback and `change` for committed values. Pass `$event` or your own values to the handler.
- Call `event.preventDefault()` to stop the browser's default action, such as a page reload on submit.

Next: **Component Communication**, how parent and child components send data and events to each other.
