Categories
JavaScript TypeScript

Multiple Inheritance with TypeScript Mixins

In JavaScript, there’s no easy way to inherit from multiple classes. We can make our own mixins to inherit from multiple objects. This is made easier in TypeScript by making mixins a standard. With TypeScript, we can make interfaces that extend multiple classes or interfaces. This way, we can reuse multiple partial classes to create a new child class. We do this with mixins and copy over the properties to a new class that derive members from parent classes with our own function.

To define a interfaces that inherit from multiple classes in TypeScript, we create an interface that extends multiple classes or interfaces. Each of these classes or interfaces is called a mixin. We can mix and match them to create a combined interface to have whatever properties that we want. For example, we can write something like the following code:

class Animal {  
    species: string;  
    constructor(species: string) {  
        this.species = species  
    }  
}

class Person {  
    name: string;  
    constructor(name: string) {  
        this.name = name  
    }  
}

interface Employee extends Person, Animal {  
    employeeCode: string;  
}

let employee: Employee = {  
    species: 'human',  
    name: 'Joe',  
    employeeCode: '123'  
}

In the code above, we made an Employee interface which inherits all the members from the Animal and Person classes and we incorporated the employeeCode member into the Employee interface. This lets us create an Employee object with the species , name , and employeeCode properties.

We can only use the extends keyword with multiple classes or interfaces if we use the keyword in the interfaces. It won’t work if we use it in a class. For example, if we write:

class Animal {  
    species: string;  
    constructor(species: string) {  
        this.species = species  
    }  
}

class Person {  
    name: string;  
    constructor(name: string) {  
        this.name = name  
    }  
}

class Employee extends Person, Animal {  
    employeeCode: string;  
}

let employee: Employee = {  
    species: 'human',  
    name: 'Joe',  
    employeeCode: '123'  
}

Then we get the error message:

Classes can only extend a single class.(1174)

We can also inherit from interfaces, along with classes in an interface like we do in the code below:

class Animal {  
    species: string;  
    constructor(species: string) {  
        this.species = species  
    }  
}

class Person {  
    name: string;  
    constructor(name: string) {  
        this.name = name  
    }  
}

interface Job {  
    title: string;  
}

interface Employee extends Person, Animal, Job {  
    employeeCode: string;  
}

let employee: Employee = {  
    species: 'human',  
    name: 'Joe',  
    employeeCode: '123',  
    title: 'laborer'  
}

As we can see, interfaces are very flexible, we can inherit from different interfaces and classes whatever way we want, unlike classes. Each of our classes is called a mixin.

If we have overlapping properties in our mixins, then they’ll be combined together with declaration merging operations done by TypeScript. For example, if we have 2 classes with the some overlapping members like we have in the code below as long as the overlapping members are identical:

class Animal {  
    species: string;  
    id: number = 0;  
    constructor(species: string) {  
        this.species = species  
    }  
}

class Person {  
    name: string;  
    id: number = 0;  
    constructor(name: string) {  
        this.name = name  
    }  
}

interface Job {  
    title: string;  
}

interface Employee extends Person, Animal, Job {      
    employeeCode: string;  
}

let employee: Employee = {  
    id: 1,  
    species: 'human',  
    name: 'Joe',  
    employeeCode: '123',  
    title: 'laborer'  
}

As we can see, we have an id member in both the Person and Animal interfaces and they’re both of the number type. Overlapping members with the same name and type are allowed for multiple inheritance. However, overlapping members with the same name but different types aren’t allowed. For example, if we have:

class Animal {  
    species: string;  
    id: string = '';  
    constructor(species: string) {  
        this.species = species  
    }  
}

class Person {  
    name: string;  
    id: number = 0;  
    constructor(name: string) {  
        this.name = name  
    }  
}

interface Job {  
    title: string;  
}

interface Employee extends Person, Animal, Job {      
    employeeCode: string;  
}

let employee: Employee = {  
    id: 1,  
    species: 'human',  
    name: 'Joe',  
    employeeCode: '123',  
    title: 'laborer'  
}

Then we get the error message:

Interface 'Employee' cannot simultaneously extend types 'Person' and 'Animal'.Named property 'id' of types 'Person' and 'Animal' are not identical.(2320)

since we id in the Animal class is a string , but id in the Person class is a number.

Copying Implementation Parent Classes to the Derived Class

To copy the mixin methods into a new class. We can write the following function to copy the methods from the parent class into a new class. The function has to loop through the base classes and get the content inside the classes and then define new properties in the class derived from the parent classes and then set them one by one in the new class. This is works because classes are just syntactic sugar for constructor objects that are in JavaScript since the early days.

We can write the following function to achieve what we want:

function applyMixins(derivedConstructor: any, baseConstructors: any[]) {  
    baseConstructors.forEach(baseConstructor => {  
      Object.getOwnPropertyNames(baseConstructor.prototype)  
      .forEach(name => {  
        Object.defineProperty(derivedConstructor.prototype,   
           name,  
           Object.  
             getOwnPropertyDescriptor(  
               baseConstructor.prototype,   
               name  
             )  
           );  
        });  
    });  
}

The applyMixin does exactly what we described above. Now we just have to call it with the child class that inherits the parent classes as the first argument and then an array with the parent classes as the second argument. We call it as in the following code:

applyMixins(Employee, [Person, Animal])

Then given that we have the following classes defined:

class Animal {  
    species: string;  
    id: number = 0;  
    constructor(species: string) {  
        this.species = species  
    }  
    eat(){}  
}

class Person {  
    name: string;  
    id: number = 0;  
    constructor(name: string) {  
        this.name = name  
    }  
    speak(){}  
}

interface Employee extends Person, Animal {      
    employeeCode: string;  
}

class Employee {      
}

We should get that the prototype of Employee having the eat and speak methods. We can call eat directly on a Employee object like the following code:

let employee: Employee = new Employee();  
employee.eat();

We can define our mixins with our class notation to let us do multiple inheritance with TypeScript. Then we define an interface that specifies which mixins we inherit from. Once we did that, we copy over the members that are in the parent classes to the child class’ prototype. Then we have all the properties of the parent classes accessible from the child class.

Categories
JavaScript Rxjs

Rxjs Filtering Operators — Audit and Debounce

RxJS is a library for doing reactive programming. Creating operators are useful for generating data from various sources to be subscribed to by Observers.

In this article, we’ll look at some filtering operators which let us filter out emitted values in a way that we specify, including the audit, auditTime, debounce, and debounceTime operators.

audit

The audit operator lets us filter out emitted values from the source Observable for a period of time, then emits the most recent values. Then the process is repeated.

It takes one argument, which is the durationSelector function which receives a value from the source Observable to use to compute the duration to which to ignore emitted values.

The durationSelector function can return s promise or an Observable.

It returns the Observable that does the rate-limiting.

It’s similar to throttle, but it returns the last value from the silenced duration instead of the first.

When the duration Observable emits a value or completes, the timer is disabled. Then the most recent source value is emitted from the output Observable.

For example, we can use it as follows:

import { interval } from "rxjs";  
import { audit } from "rxjs/operators";

const interval$ = interval(1000);  
const result = interval$.pipe(audit(ev => interval(4000)));  
result.subscribe(x => console.log(x));

The code above takes the interval$ Observable then pipes the value into the audit operator, which has the function which returns interval(4000). This means the value from the returned Observable will emit values every 4 seconds.

The effect produced would be that every 3 values are skipped.

auditTime

The auditTime operator ignores the source Observable’s values for duration milliseconds then emit the most recent value from the source Observable and repeats the process.

It takes up to 2 arguments. One is the duration , which is the time to wait before emitting the most recent value. The duration is measured in milliseconds or the time unit determined by the optional scheduler argument.

The second is an optional scheduler to let us time the values emitted.

It returns the Observable that does the rate-limiting.

For example, we can use it as follows:

import { interval } from "rxjs";  
import { auditTime } from "rxjs/operators";

const interval$ = interval(1000);  
const result = interval$.pipe(auditTime(5000));  
result.subscribe(x => console.log(x));

The code above takes the emitted values from the interval$ Observable, which emits numbers every second, then pipe s it to the auditTime(5000) operator, which emits the latest value from the interval$ Observable every 5 seconds.

The result will be that we get the latest value emitted from interval$ every 5 seconds.

debounce

debounce emits a value from the source Observable after a particular time span has passed without another source emission.

It takes one argument, which is a durationSelector function that receives a value from the source Observable for computing the timeout duration for each source value.

The durationSelector function can return a promise or an Observable.

It returns an Observable that emits values according to the delay specified by the return value of the durationSelector function. The returned Observable may drop some values if emissions from the source happen too frequently.

For example, we can use it as follows. Given that we have the following input element:

<input type="text" />

We can write the following JavaScript code to delay the emission of the input value for 2 seconds:

import { fromEvent, interval } from "rxjs";  
import { debounce } from "rxjs/operators";

const clicks = fromEvent(document.querySelector("input"), "input");  
const result = clicks.pipe(debounce(() => interval(2000)));  
result.subscribe(x => console.log(x));

The input events are received by the fromEvent function, then it’s pipe d to our durationSelector function which is () => interval(2000) via the debounce operator.

debounceTime

Like the debounce operator, the debounceTime operator emits values from the source Observable after a delay has passed without another emission from the source Observable.

The difference is in the argument it accepts. It takes up to 2 arguments. The first is the dueTime number, which is the duration in milliseconds or the time unit specified by the optional scheduler argument for the time to delay emission of values.

The second argument is the optionalscheduler argument, which defaults to async . It lets us time the emissions as we want to.

It returns an Observable that delays the emission of the source Observable by the specified dueTime and may drop some values if emissions from the source happen too frequently.

For example, we can use it as follows. Given that we have the following input element:

<input type="text" />

We can use the debounceTime operator to delay the emission of input event objects as follows:

import { fromEvent } from "rxjs";  
import { debounceTime } from "rxjs/operators";

const clicks = fromEvent(document.querySelector("input"), "input");  
const result = clicks.pipe(debounceTime(2000));  
result.subscribe(x => console.log(x));

The code above will delay the emission of input events objects by 2 seconds and drops whatever is emitted less than 2 seconds.

audit and auditTime lets us filter out emitted values from the source Observable for a period of time, then emits the most recent values. Then the process is repeated.

debounce and debounceTime operators emit values from the source Observable after a delay has passed without another emission from the source Observable.

Categories
Angular JavaScript

Angular Directives — NgSwitch, Input, and Output

Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.

In this article, we’ll look at the NgSwitch directive, template variables, and Input and Ouput syntax.

The NgSwitch directives

We can use the NgSwitch directive to display an item from a list of possible choices.

For example, we can use it as follows:

app.component.ts:

import { Component } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  name = "foo";  
}

app.component.html:

<div [ngSwitch]="name">  
  <p *ngSwitchCase="'foo'">foo</p>  
  <p *ngSwitchCase="'bar'">bar</p>  
  <p *ngSwitchCase="'baz'">baz</p>  
</div>

In the code above, we have the name field in AppComponent set to 'foo'.

Then in app.component.html, we have ngSwitch set to name so that it checks the value of name in AppComponent to get what to display.

We have multiple *ngSwitchCase with different values so that if name is 'foo', we display ‘foo’, if name is 'bar' then we display ‘bar’, and if name is 'baz', we display ‘baz’.

Since name is ‘foo’, we display ‘foo’.

*ngSwitchCase works with both native elements and components.

Template Reference Variables (#var)

A template reference variable is often a reference to a DOM element within a template. It can also refer to a directive, an element. TemplateRef or a web component.

For example, we can use it as follows:

app.component.ts:

import { Component } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  call(phone) {  
    alert(`Calling ${phone.value}`);  
  }  
}

app.component.html :

<input #phone placeholder="phone" />  
<button (click)="call(phone)">Call</button>

In the code above, we added a call method which takes in the phone element as the argument and shows an alert with the value typed into the phone input.

Then we added #phone in app.component.html to reference the input element. Also, we added a button to call the call method with the phone input reference so that we can show the value in the alert.

NgForm

The NgForm directive change behavior and set the value of the template variable to something else.

With it, we can reference the form with a variable inside our outside the form.

For example, we can use NgForm as follows:

app.component.ts:

import { Component } from "@angular/core";

@Component({  
  selector: "app-root",  
  templateUrl: "./app.component.html",  
  styleUrls: ["./app.component.css"]  
})  
export class AppComponent {  
  onSubmit(itemForm) {  
    if (itemForm.invalid) {  
      return;  
    }  
    alert("form submitted");  
  }  
}

app.component.html:

<form #itemForm="ngForm" (ngSubmit)="onSubmit(itemForm)">  
  <label>Name <input name="name" ngModel required /> </label>  
  <button type="submit">Submit</button>  
</form>
<div [hidden]="itemForm.form.valid">  
  <p>Missing name</p>  
</div>

In the code above, we have the onSubmit method in AppComponent, which takes the itemForm reference. Then we can use it to check for validity in the component before submitting.

Then in app.component.html, we created the #itemForm variable by using the ngForm directive so that we can reference the form and check its validity in the bottom div and in onSubmit with itemForm passed in.

Alternative Syntax

We can use the ref- prefix instead of #, so we can write:

<input ref-phone placeholder="phone" />  
<button (click)="call(phone)">Call</button>

@Input() and @Output() Properties

@Input and @Output allow Angular to share data between the parent context and child directives and components.

How to use @Input()

For example, we can write the following to use @Input to pass in data from the parent component to the child:

app.module.ts:

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';
@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

app.component.ts:

import { Component } from '@angular/core';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  persons = [{ name: 'Jane' }, { name: 'John' }, { name: 'Mary' }];  
}

app.component.html:

<app-person *ngFor="let p of persons" [person]='p'></app-person>

person.component.ts:

import { Component, OnInit, Input } from '@angular/core';

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Input() person;  
}

person.component.html:

<div>{{person.name}}</div>

In the code above, we created a PersonComponent component and then included it in the declarations array of AppModule.

Then in AppComponent, we have:

persons = [{ name: 'Jane' }, { name: 'John' }, { name: 'Mary' }];

and *ngFor in app.component.html to loop through the items and render the PersonComponent.

We pass in p as the value of the person property and then it’ll be the value of person in the PersonComponent.

Finally, we display the name value of person in person.component.html.

In the end, we should see:

JaneJohnMary

displayed on the screen.

How to use @Output()

We can use the @Output decorator in the child component or directive to pass data from child to parent.

For example, we can use it as follows:

app.module.ts:

import { BrowserModule } from '@angular/platform-browser';  
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';  
import { AppComponent } from './app.component';  
import { PersonComponent } from './person/person.component';

@NgModule({  
  declarations: [  
    AppComponent,  
    PersonComponent  
  ],  
  imports: [  
    BrowserModule,  
    AppRoutingModule  
  ],  
  providers: [],  
  bootstrap: [AppComponent]  
})  
export class AppModule { }

app.component.ts:

import { Component } from '@angular/core';

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  emittedName: string;  
}

app.component.html:

<app-person (name)='emittedName = $event'></app-person>  
<p>{{emittedName}}</p>

person.component.ts:

import { Component, Output, EventEmitter } from '@angular/core';

@Component({  
  selector: 'app-person',  
  templateUrl: './person.component.html',  
  styleUrls: ['./person.component.css']  
})  
export class PersonComponent {  
  @Output() name = new EventEmitter<string>();  
  emitName() {  
    this.name.emit('Jane');  
  }  
}

person.component.html:

<button (click)='emitName()'>Get Name</button>

In the code above, we have the button in person.component.html which calls the PersonComponent’s emitName method to emit the string 'Jane' to the parent component with the EventEmitter.

Then in the parent component, AppComponent, we have:

<app-person (name)='emittedName = $event'></app-person>

in app.component.html.

$event has the value emitted from the name emitter. Then we set it to emittedName and displayed it.

@Input() and @Output() together

We can use them together.

For example, we can write:

<app-input-output [item]="currentItem" (deleteRequest)="deleteItem($event)"></app-input-output>

In the code above, we pass currentItem to the item property to the app-input-output component.

The deleteRequest is emitted from app-input-output and then the deleteItem method is called with what’s passed from the child when deleteRequest is emitted.

We can pass in a name to Input and Output to change the name of them respective and then we can reference them with that name in our templates.

Conclusion

We can use NgSwitch to display an item depending on value of something in the component.

Template variables let us reference DOM elements in our templates and access its properties.

With Input and Output, we can pass data to a child component or directive and pass items from child to parent respectively.

Categories
JavaScript React

Using React Strict Mode to Avoid Deprecated Code and Side Effects

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

In this article, we’ll look at how to use React’s strict mode to get extra warnings about deprecated APIs and side effects during development.

Strict Mode

Strict mode is a tool for highlighting potential problems in an application. It doesn’t render any visible UI.

It’s only used to activate additional checks and warnings for its descendants.

Strict mode doesn’t affect the production build.

We can add strict mode to a React app as follows:

class App extends React.Component {  
  render() {  
    return (  
      <div>  
        <p>foo</p>  
        <React.StrictMode>  
          <p>bar</p>  
        </React.StrictMode>  
      </div>  
    );  
  }  
}

In the code above, the p tag with ‘foo’ isn’t checked by strict mode since it’s outside of the React.StrictMode component, but the p element inside is checked by strict mode.

Identifying Unsafe Lifecycles

Strict mode checks for unsafe lifecycles. Some lifecycle methods are being deprecated because they encourage unsafe coding practices.

They are:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

The UNSAFE_ prefix will be added in an upcoming release.

2 new lifecycle methods are replacing the ones above. They’re the static getDerivedStateFromProps and getSnapshotBeforeUpdate methods.

getSnapshotBeforeUpdate lifecycle is called before mutations are made, and the return value of it will be passed as the 3rd parameter to componentDidUpdate .

getSnapshotBeforeUpdate and componentDidUpdate together covers all the user cases for componentWillUpdate .

Strict mode will warn about the deprecation of old lifecycle hooks.

Warning About Legacy String ref API Usage

Also, React strict mode will warn about using string refs in our code.

String refs are deprecated because they can’t be statically typed. They need to always be consistent. Magic dynamic strings break optimizations in VMs, and it only works on one level.

We can’t pass it around like callback and object refs.

Therefore, it’ll warn us about the usage of string refs since they’re deprecated.

Callback refs and createRef are both supported in the future.

Warning About Deprecated findDOMNode Usage

The findDOMNode method is deprecated. We can use it to search for a DOM node given a class instance.

We shouldn’t need to do this because we can attach a ref to a DOM node.

findDOMNode only returns the first child, but it’s possible for a component to render multiple DOM levels with the use of Fragments.

Therefore, it isn’t very useful now since it only searches one level and we can use ref to get the exact element we’re looking for.

We can attach a ref to the wrapper element instead:

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.wrapper = React.createRef();  
  }  
  render() {  
    return <div ref={this.wrapper}>{this.props.children}</div>;  
  }  
}

If we don’t want to wrapper div to be rendered, we can set display: contents in our CSS if we don’t want the node to be part of the layout.

Detecting Unexpected Side Effects

React works in 2 phases:

  • The render phase makes any changes to the DOM. React calls render during this phase and then compares the result to the previous render.
  • The commit phase runs any lifecycle methods to apply any changes required.

Render phase lifecycles include the following class component methods:

  • constructor
  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • setState

Since they may be called more than once, they shouldn’t commit any side effects. Ignoring this rule may lead to problems like memory leaks and invalid app state.

Strict mode checks if side effects are made by running the following methods twice:

  • Class component constructor method
  • The render method
  • setState
  • The static getDerivedStateFromProps lifecycle

Detecting Legacy Context API

Strict mode will detect uses of the old Context API. It’ll be removed in future versions. We should move to the new one if it’s used.

Conclusion

We can use strict mode to detect uses of deprecated lifecycle methods, legacy Context API, string refs, and some code that commits unexpected side effects.

It only shows warnings in development and doesn’t affect production code.

Categories
JavaScript Vue

Introduction to Event Handling in Vue.js

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to handle various events with Vue.js and modify an event handler’s behavior.

Listening to Events

We can use the v-on directive to listen to DOM events and run some JavaScript code when they’re triggered.

For example, we can use it as follows:

src/index.js :

new Vue({  
  el: "#app",  
  data: {  
    count: 0  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="count += 1">Add 1</button>  
      <p>Count: {{count}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we click Add 1, the count increases by 1.

Method Event Handlers

We can also run methods when an event is triggered. This helps us a lot because we might need to runs lots of code when an event happens, so we can’t put it all in an expression in the template.

For example, we can set a method as the value of the v-on:click as follows:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="onClick">Click Me</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when Click Me is clicked, we get a ‘clicked’ alert box displayed.

Note that we just have to pass in the function reference, we don’t have to call it in the v-on:click directive.

Methods in Inline Handlers

In addition to passing in the method reference, we can also call methods directly with the v-on:click directive.

For example, we can write the following:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick(message) {  
      alert(message);  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button v-on:click="onClick('hi')">Say Hi</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

We’ll get an alert box that says ‘hi’ when we click Say Hi.

Sometimes we have to access the original DOM element that triggered the event. We can use the $event object to do this.

The $event object has the triggered event’s data, which includes the DOM element that triggered the event.

For example, we can get the ID of the button that was clicked as follows:

src/index.js :

new Vue({  
  el: "#app",  
  methods: {  
    onClick(event, message) {  
      alert(`${event.target.id} says ${message}`);  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <button id="hi-button" v-on:click="onClick($event, 'hi')">Say Hi</button>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above passes the $event object to the onClick handler, which takes the event parameter, which is set to the $event object and get the ID of the button via event.target.id . The message string is also passed in.

Then alert is called with the string `${event.target.id} says ${message}` so when we click Say Hi, we get an alert box with that says hi-button says hi since the buttons’ ID is hi-button .

Event Modifiers

We can use event modifiers to change the behavior of event handlers.

For example, the .prevent modifier for the v-on directive will automatically run event.preventDefault on the event handler function that’s set as the value.

The .prevent modifier can be used as follows:

<form v-on:submit.prevent="onSubmit"> ... </form>

Then event.preventDefault() will be run when the onSubmit handler is run before the rest of the code for onSubmit is run.

Other event modifiers include:

  • .stop
  • .capture
  • .self
  • .once
  • .passive

.stop runs event.stopPropagation() before the rest of the event handler code is run.

.capture let us capture the event. That is, when we run the event handler in an inner element, then the same event handler will also run in the outside elements.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {},  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head><body>  
    <div id="app">  
      <div>  
        <a v-on:click.capture="onClick"> Click Me </a>  
      </div>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then when we click Click Me, we’ll see the ‘clicked’ alert box since the onClick handler is called on the parent div element.

.self will only trigger the event handler if the event.target is the element itself.

.once will trigger an event handler at most once.

For example, if we have the following in src/index.js:

new Vue({  
  el: "#app",  
  data: {},  
  methods: {  
    onClick() {  
      alert("clicked");  
    }  
  }  
});

and the following in index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>Hello</title>  
    <meta charset="UTF-8" />  
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  
  </head> <body>  
    <div id="app">  
      <div>  
        <a v-on:click.once="onClick"> Click Me </a>  
      </div>  
    </div>  
    <script src="./src/index.js"></script>  
  </body>  
</html>

Then we only see the ‘clicked’ alert box once even though we click ‘Click me’ multiple times.

.passive will set the addEventListener ‘s passive option to true . passive set to true means that preventDefault() will never be called.

It’s used for improving performance on mobile devices.

Conclusion

We can set event handlers for DOM element events with thev-on directive.

To use it, we can pass in an expression, event handler method reference, or a call for an event handler as the value of it.

We can also get the data for the event that was triggered by the $event object, where we can get the target element of the event triggered.

Finally, we can add event modifiers to change the behavior of event handlers.