Angular 2 Event Binding Tutorial

See how event bindings listen for DOM events such as click and keypress on standard HTML elements such as button and input.

Event Binding with Parentheses

We handle DOM events using parentheses and template statements.

When an event in parentheses on the left of the equals is detected, the template statement in quotes on the right of the equals is executed. Alternatively, we can use the canonical form by prefixing the event name with on-. These three bindings are equivalent to (click), (dblclick) and (contextment).

The string in quotes is a template statement. Template statements respond to an event by executing some JavaScript-like code. Here we simply call methods on the component class and pass in the $event object.

The built-in $event object is automatically set to the target event, which in this case is of type MouseEvent. We can use this object to access X and Y coordinates, or any keys that were pressed at the time the mouse event was triggered.

mouse.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'mouse',
    template: `
        <h1>Mouse Events</h1>
        <div class="box" (mouseenter)="onEvent($event)"
                         (mouseleave)="onEvent($event)"
                         (mousemove)="coordinates($event)"
                         on-click="onEvent($event)"
                         on-dblclick="onEvent($event)"
                         on-contextmenu="onEvent($event)">
            <p class="type">Type: {{event?.type}}</p>    
            <p>x: {{clientX}}, y: {{clientY}}</p>
            <p>ctrl: {{event?.ctrlKey}}, shift: {{event?.shiftKey}}, meta: {{event?.metaKey}}</p>
        </div>`,
    styles: [
        '.box {width:300px; height:200px; border: 1px solid LightGray; padding: 10px;}',
        '.type {font-size: 30px;}'
    ]
})
export class MouseComponent {

    private event: MouseEvent;
    private clientX = 0;
    private clientY = 0;

    private onEvent(event: MouseEvent): void {
        this.event = event;
    }

    private coordinates(event: MouseEvent):void {
        this.clientX = event.clientX;
        this.clientY = event.clientY;
    }
}

The Application

Here is the output from this component. Move the pointer around the inner box and click, double click and right click on the mouse whilst holding down the shift, ctrl or command key.

Keyboard Events

Keyboard events follow the same pattern; template statements in quotes respond to DOM events which are surrounded by parentheses or prefixed with on-.

Template statements are expected to change the state of the application based on the user's input. Technically, they can contain statements that directly alter data such as "key = $event.key" but this is usually best handled by calling a method. This keeps the application logic contained within the component class.

keyboard.component.ts
import { Component } from '@angular/core';

@Component({
    selector: 'keyboard',
    template: `
        <h1>Keyboard Events</h1>
        <p>
            <input type="text"
                   (keydown)="onEvent($event)"
                   on-keyup="onEvent($event)">
            <span class="large">Type: {{event?.type}}</span>
        </p>
        <p>ctrl: {{event?.ctrlKey}}, shift: {{event?.shiftKey}}, meta: {{event?.metaKey}}</p>
        
        <p>
            <input type="text" 
                   on-keypress="onKeyPress($event)">
            <span class="large"> Key: {{key}}</span>
        </p>
        `,
    styles: [
        '.large {font-size: 30px;}'
    ]
})
export class KeyboardComponent {

    private key: string;
    private event: KeyboardEvent;

    private onKeyPress(event: KeyboardEvent): void {
        this.key = event.key;
    }

    private onEvent(event: KeyboardEvent): void {
        this.event = event;
    }
}

The Application

Here is the output from this component.

Drag and Drop Example

Let's look at event binding in action using a drag and drop example.

We bind to the mousedown and mouseup events and set the isMouseDown value accordingly.

The mousemove event on the container executes a template statement which calls the onMouseMove method. This method checks the isMouseDown value and, if it is true, adjusts the coordinates of the draggable block.

drag-drop.component.ts
import { Component, ViewChild, ElementRef, Renderer, OnInit } from '@angular/core';

const containerSize: number = 320;
const draggableHeight: number = 50;
const draggableWidth: number = 100;

@Component({
    selector: 'drag-drop',
    template: `
        <h1>Drag 'n Drop</h1>
        <div #container 
             class="container"
             (mousemove)="onMouseMove($event)">
            <div #draggable 
                 class="draggable"
                 (mousedown)="onMouseButton($event)"
                 (mouseup)="onMouseButton($event)">
            </div>
        </div>`,
    styles: [`
        .container {
            height: ${containerSize}px;
            width: ${containerSize}px;
            background-color: LightGray;
        }
        .draggable {
            height: ${draggableHeight}px;
            width: ${draggableWidth}px;
            background-color: Green;
            position: absolute;
            cursor: move;
        }
    `]
})

export class DragDropComponent implements OnInit {

    @ViewChild('container') private containerElement: ElementRef;
    @ViewChild('draggable') private draggableElement: ElementRef;

    private boundary: any = {};
    private draggable: any;
    private isMouseDown = false;

    constructor(private renderer: Renderer) {}

    ngOnInit() {
        this.draggable = this.draggableElement.nativeElement;

        const container = this.containerElement.nativeElement;
        this.boundary = {
            left : container.offsetLeft + (draggableWidth / 2),
            right : container.clientWidth + container.offsetLeft - (draggableWidth / 2),
            top : container.offsetTop + (draggableHeight / 2),
            bottom : container.clientWidth + container.offsetTop - (draggableHeight / 2),
        }
    }

    private onMouseButton(event: MouseEvent): void {
        this.isMouseDown = event.buttons === 1;
    }

    private onMouseMove(event: MouseEvent): void {
        if (this.isMouseDown && this.isInsideBoundary(event)) {
            this.renderer.setElementStyle(this.draggable, 'left', event.clientX - (draggableWidth / 2) + "px");
            this.renderer.setElementStyle(this.draggable, 'top', event.clientY - (draggableHeight / 2) + "px");
        }
    }

    private isInsideBoundary(event: MouseEvent) {
        return event.clientX > this.boundary.left &&
            event.clientX < this.boundary.right &&
            event.clientY > this.boundary.top &&
            event.clientY < this.boundary.bottom;
    }
}

The Application

Here is the output. Click on the green box to move it around within the gray container.

Where Next?

To find out more about Angular and TypeScript, check out these tutorials.

  • Hello World - Implement a super-simple <hello-world> custom element using an Angular and TypeScript.
  • The Angular with TypeScript Tutorial - includes examples of components, template syntax, property binding, event binding, bootstrapping and more.
  • Configuration - Configure Angular and TypeScript to download dependencies from node modules or a CDN, and to compile the TypeScript during development or in the browser at runtime.
  • Templates - introduction to inline and external templates.
  • Interpolation - use curly braces and template expressions to output data on the page.
  • Property Binding - bind to DOM properties using square brackets and template expressions.
  • Two-way Binding - combine property and event binding to create two-way binding with ngModel.
  • Input Binding - bind to <input> fields such as text, textarea, checkbox, radio and select.
  • Built-in Directives - see how to use built-in directives ngIf, ngSwitch, ngFor, ngClass and ngStyle.
  • Component Input Output - use @Input and @Output to pass data in to and out of a component.
  • Angular Router - Use the Angular router to navigate between components when the user clicks a link.
  • Nested Child Routes - An example of how child routes allow navigation between multiple views when a user clicks a link in a sub-menu.