Categories
Vue 3

Vue 3 — Event Handling

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to handle events in Vue 3 components.

Listening to Events

We can listen to events with the v-on directive, or @ for short.

For instance, we can listen to clicks by writing:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button v-on:click="onClick">click me</button>  
    </div>  
    <script>  
      const vm = Vue.createApp({  
        methods: {  
          onClick() {  
            alert("clicked");  
          }  
        }  
      }).mount("#app");  
    </script>  
  </body>  
</html>

We added the v-on:click directive to run the onClick method when we click the button.

So we should see an alert when we click the button.

To shorten it, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="onClick">click me</button>  
    </div>  
    <script>  
      const vm = Vue.createApp({  
        methods: {  
          onClick() {  
            alert("clicked");  
          }  
        }  
      }).mount("#app");  
    </script>  
  </body>  
</html>

We can put any JavaScript expression as the value of the v-on directive.

Methods in Inline Handlers

We don’t have to bind directly to the method in the expression we pass into v-on .

We can also call the method in there.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="onClick('hi')">hi</button>  
      <button @click="onClick('bye')">bye</button>  
    </div>  
    <script>  
      const vm = Vue.createApp({  
        methods: {  
          onClick(str) {  
            alert(str);  
          }  
        }  
      }).mount("#app");  
    </script>  
  </body>  
</html>

We pass in an argument to the onClick method so that onClick will get the argument and display the message.

To access the event object of the event, we can use the $event object.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="onClick('hi', $event)">click me</button>  
    </div>  
    <script>  
      const vm = Vue.createApp({  
        methods: {  
          onClick(str, event) {  
            event.stopPropagation();  
            alert(str);  
          }  
        }  
      }).mount("#app");  
    </script>  
  </body>  
</html>

to pass in the $event object to our event handler.

Then we can call stopPropagation on it to stop the click event from propagation to parent elements.

This event object is the native event object.

Multiple Event Handlers

We can have multiple event handlers in one expression.

For example, we can write:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <title>App</title>  
    <script src="https://unpkg.com/vue@next"></script>  
  </head>  
  <body>  
    <div id="app">  
      <button @click="one($event), two($event)">click me</button>  
    </div>  
    <script>  
      const vm = Vue.createApp({  
        methods: {  
          one(event) {  
            console.log("one");  
          },  
          two(event) {  
            console.log("two");  
          }  
        }  
      }).mount("#app");  
    </script>  
  </body>  
</html>

to run one and two as event handlers when we click on the button.

Event Modifiers

We can add event modifiers so that we don’t have to call methods like event.preventDefault() or event.stopPropagation() in our event handlers.

The modifiers include:

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

These are added to the v-on directive.

For example, to call event.stopPropagation in our event handler, we can write:

<a @click.stop="onClick"></a>

then the click event won’t be propagated to the parent elements.

And if we write:

<form @submit.prevent="onSubmit"></form>

event.preventDefault() will be called when running onSubmit .

Modifiers can also be chained, so we can write:

<a @click.stop.prevent="onClick"></a>

The capture modifier lets us use capture mode when adding an event listener.

And the self modifier only triggers the event handler if the event.target is the element itself.

once will only trigger the event handler at most once.

The passive modifier corresponds to the addEventListener ‘s passive option.

If we add it to the @scroll directive:

<div @scroll.passive="onScroll">...</div>

then the scroll event’s default behavior will happen immediately instead of waiting for onScroll to complete.

passive and prevent shouldn’t be used together since prevent will be ignored.

passive communicates to the browser that we don’t want to prevent the default browser behavior.

Conclusion

We can listen to events with the v-on directives.

It makes many arguments and modifiers.

Categories
Vue 3

Vue 3 — Directive Shorthands and Arguments

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at the dynamic arguments of a directive.

Dynamic Arguments

Vue 3 directives can take dynamic arguments.

For example, we can write:

<a v-bind:[attributeName]="url"> ... </a>

or:

<a v-on:[eventName]="doSomething"> ... </a>

We can pass in an argument to a directive.

This applies to any directives, including the ones we create.

Modifiers

We can also pass in modifiers to a directive.

For instance, the v-on directive can take a modifier for some event.

We can write:

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

to let us call event.preventDefault in the onSubmit handler.

This lets us stop the default behavior from happening.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="[https://unpkg.com/vue@next](https://unpkg.com/vue@next)"></script>
  </head>
  <body>
    <div id="app">
      <form v-on:submit.prevent="onSubmit">
        <input v-model="name" />
      </form>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            name: ""
          };
        },
        methods: {
          onSubmit() {
            console.log(this.name);
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>

to create a form with the v-on:submit:prevent directive to run the onSubmit method with the event.preventDefault method all in one directive.

Shorthands

We can shorten v-bind to : .

For example, we can write:

<a :href="url"> ... </a>

instead of:

<a v-bind:href="url"> ... </a>

We can also pass in an argument with the shorthand by writing:

<a :[key]="url"> ... </a>

We can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <a :href="url">link</a>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            url: "http://example.com"
          };
        }
      }).mount("#app");
    </script>
  </body>
</html>

v-on also has their own shorthand.

Instead of writing:

<a v-on:click="doSomething"> ... </a>

We can write:

<a @click="doSomething"> ... </a>

for short.

If we want a dynamic event, we can write:

<a @[event]="doSomething"> ... </a>

So we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <button @click="toggle">toggle</button>
      <p>{{on}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            on: true
          };
        },
        methods: {
          toggle() {
            this.on = !this.on;
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>

to add a click listener to our button.

Caveats

Dynamic value has some constraints.

Whatever is inside the square brackets must return a string or null .

null can be used to remove the binding.

Any other value will trigger a warning.

Certain characters aren’t valid in HTML, so we can’t have them as the dynamic argument’s value.

So we can’t have names with spaces of quotes.

Something like:

<a v-bind:['foo ' + bar]="value"> ... </a>

won’t work.

We should also avoid naming keys with uppercase characters since browsers will convert them to lowercase.

JavaScript Expressions

Templates expressions are sandboxes so we can’t use global variables in template expressions.

It can only access the list of global variables from a whitelist.

The whitelist is at https://github.com/vuejs/vue-next/blob/master/packages/shared/src/globalsWhitelist.ts#L3.

Conclusion

Vue directives can take dynamic arguments.

Also, there’re various shorthands we can use in place of some directives.

Categories
Vue 3

Vue 3 — Computed Properties and Watchers

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at Vue computed properties and watchers.

Computed Properties

We can add computed properties to derive some from an existing property.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="message" />
      <p>{{reversedMessage}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            message: ""
          };
        },
        computed: {
          reversedMessage() {
            return this.message
              .split("")
              .reverse()
              .join("");
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>

We added the message property to the object we return in data .

Then we added the computed property to the object we passed into createApp .

Methods are inside it getters, so we return things derived from other properties.

The method must be synchronous.

Now when we type in something to the input, we’ll see the reversed version displayed below it.

Computed Caching vs Methods

We can achieve the same result by running a method to reverse the string.

But it wouldn’t be cached based on their dependency if we use a regular method to return derived data.

Computed properties are cached based on the original reactive dependencies.

As long as this.message stays the same, reversedMessage won’t be run.

However, if we have dependencies that aren’t reactive in our computed property, then the method won’t run.

In this case, we’ve to use methods to make them update.

So something like:

computed: {
  now() {
    return Date.now()
  }
}

won’t update.

Computed Setter

We can have setters in our computed properties.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="firstName" placeholder="first name" />
      <input v-model="lastName" placeholder="last name" />
      <p>{{fullName}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            firstName: "",
            lastName: ""
          };
        },
        computed: {
          fullName: {
            get() {
              return `${this.firstName} ${this.lastName}`;
            },
            set(newValue) {
              const names = newValue.split(" ");
              this.firstName = names[0];
              this.lastName = names[names.length - 1];
            }
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>

We have the firstName and lastName properties in the object we return with data .

Then we can create the getter for the fullName property by returning the 2 properties combined into one string.

The setter would be the set method which takes the newValue and then split it back into its parts.

We set the this.firstName and this.lastName based on the combined string we have returned from get .

When this.firstName and this.lastName change then get is run.

If get is run, then set is run.

Watchers

Computed properties work for most cases, but sometimes we need watchers to provide us with a more generic way to watch for data changes.

For example, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <input v-model="name" />
      <p>{{data}}</p>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            name: "",
            data: {}
          };
        },
        watch: {
          name(newName, oldName) {
            if (oldName !== newName) {
              this.getData(newName);
            }
          }
        },
        methods: {
          getData(newName) {
            fetch(`https://api.agify.io/?name=${newName}`)
              .then(res => res.json())
              .then(res => {
                this.data = res;
              });
          }
        }
      }).mount("#app");
    </script>
  </body>
</html>

We have a getData method which takes a newName parameter and get some data from an API.

And then we have our watch property, which has an object with watchers.

The name of the method would match the name of the instance property name.

A watcher takes an old value and a new value as the parameter.

And we can run what we want inside it.

The code we run is async so we got to use a watcher instead of computed properties.

Conclusion

Computed properties and watchers let us watch for reactive data changes and let us run code when they change.

Categories
Vue 3

Vue 3 — Component and Vue Instance

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create components with Vue 3.

Components and Props

Props are attributes that we can add to components to pass data to them.

To create a component that takes a prop, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <hello name="mary" />
    </div>
    <script>
      const root = {};

      const app = Vue.createApp(root);

      app.component("hello", {
        props: ["name"],
        template: `<p>hello {{name}}</p>`
      });

      app.mount("#app");
    </script>
  </body>
</html>

We have the name prop that takes a string and we display it.

We can use v-for to render multiple items and render a component for each.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="[https://unpkg.com/vue@next](https://unpkg.com/vue@next)"></script>
  </head>
  <body>
    <div id="app">
      <hello v-for="p in people" v-bind:person="p" v-bind:key="p.id" />
    </div>
    <script>
      const root = {
        data() {
          return {
            people: [
              { id: 0, name: "mary" },
              { id: 1, name: "jane" },
              { id: 2, name: "bob" }
            ]
          };
        }
      };

      const app = Vue.createApp(root);

      app.component("hello", {
        props: ["person"],
        template: `<p>hello {{person.name}}</p>`
      });

      app.mount("#app");
    </script>
  </body>
</html>

Since we aren’t passing a string prop, we need the v-bind directive to let us pass non-string values to our component.

The key prop is needed so that Vue 3 can track the items properly.

In the app.component call, we have the props property with a prop name that’s accepted by our component.

And we can get the name property from the object.

Vue 3 components resemble web component spec’s custom elements.

It also implements the slot API so that we can use slots to add different content to our components.

The Vue Instance

The Vue instance is the root of our app.

We create it with the createApp method.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      {{a}}
    </div>
    <script>
      const data = { a: 1 };

      const vm = Vue.createApp({
        data() {
          return data;
        }
      }).mount("#app");
    </script>
  </body>
</html>

We have our data object we which we return in the data method.

Then we can use the a property in our template.

The Vue.createApp method returns the Vue instance.

It should contain the properties of the data object as properties.

If we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      {{a}}
    </div>
    <script>
      const data = { a: 1 };

      const vm = Vue.createApp({
        data() {
          return data;
        }
      }).mount("#app");

      console.log(vm.a === data.a);
    </script>
  </body>
</html>

The console log should log true .

And if we change the value of vm.a :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      {{a}}
    </div>
    <script>
      const data = { a: 1 };

      const vm = Vue.createApp({
        data() {
          return data;
        }
      }).mount("#app");

      vm.a = 2;

      console.log(data.a);
    </script>
  </body>
</html>

Then the value of a in the template and the data object should both change to 2.

Conclusion

Components can take props so we can populate it with data dynamically from the outside.

The Vue instance is the root of a Vue app.

It contains the data which can be changed and the change will be reflected everywhere.

Categories
Vue 3

Vue 3 — Class Bindings

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at class bindings.

Class Bindings

To let us style our app dynamically, we got to be able to set dynamic classes and styles with it.

One way to set a class is with th object syntax:

<div :class="{ active: isActive }"></div>

active is the name of the class and isActive is the condition when active is toggled on.

active is only on with isActive is truthy.

We can have multiple classes in one object.

For instance, we can write:

<div
  class="static"
  :class="{ active: isActive, 'has-error': hasError }"
></div>

We have the static static class which is always added.

And we have the class within the :class binding that’s only on when the conditions are truthy.

Bound objects don’t have to be inline, so we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div class="static" :class="classObj"></div>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            classObj: {
              active: true,
              "has-error": false
            }
          };
        }
      }).mount("#app");
    </script>
  </body>
</html>

to pass in classObj as the value of the :class binding.

Array Syntax

We can also have class names in an array.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <div :class="[activeClass, errorClass]"></div>
    </div>
    <script>
      const vm = Vue.createApp({
        data() {
          return {
            activeClass: "active",
            errorClass: "has-error"
          };
        }
      }).mount("#app");
    </script>
  </body>
</html>

We have the activeClass and errorClass in an array.

We can toggle them on and off with JavaScript code.

If we want to toggle a class conditionally, we can write ternary expressions for them.

For example, we can write:

<div :class="[isActive ? activeClass : '', errorClass]"></div>

activeClass is only added is isActive is truthy.

Otherwise, the expression returns an empty string.

Classes With Components

We can add classes with components.

For instance, if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <hello-world class="baz qux"></hello-world>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("hello-world", {
        template: `<p class="foo bar">hello world</p>`
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the hello-world component with the foo and bar classes on the p element.

In addition, we have the baz and qux class on the hello-world component itself.

The classes from both places will be combined since the p element is the root element of the component.

So the baz and qux class will be added to the p element.

Class bindings also work.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <hello-world :class="{ active: isActive }"></hello-world>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            isActive: true
          };
        }
      });

      app.component("hello-world", {
        template: `<p class="foo bar">hello world</p>`
      });

      app.mount("#app");
    </script>
  </body>
</html>

We have the active class combined with the foo and bar classes on the p element since isActive is true .

Conclusion

We can add HTML classes dynamically with an object or an array.

They can be added to Vue components and HTML elements.

If they’re added to a component, then they’ll be added to the root element of the component.