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.

Categories
Modern JavaScript

Best of Modern JavaScript — Prototypes and Function Names

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at properties in JavaScript.

Using call and apply to Call hasOwnProperty() Safely

hasOwnProperty is a method of an object’s prototype.

Therefore, it can easily be overridden with an object’s own methods.

To call hasOwnProperty safety, we can call it with call .

So instead of writing:

obj.hasOwnProperty('prop')

We write:

Object.prototype.hasOwnProperty.call(obj, 'prop')

The 2nd way is safer because hasOwnProperty is always part of the Object.prototype .

We can’t guarantee that hasOwnProperty isn’t overridden with the first way.

With ES6, we can use the Map constructor to store key-value pairs, so that we don’t need to create objects to store them.

Therefore, we won’t need hasOwnProperty as much.

Abbreviations for Object.prototype and Array.prototype

Using Object.prototype and Array.prototype are long.

But we can shorten Object.prototype to an empty object literal.

We can shorten Array.prototype to an empty array literal.

So instead of writing:

Object.prototype.hasOwnProperty.call(obj, 'prop')

We can write:

({}).hasOwnProperty.call(obj, 'prop')

With arrays, we can write:

[].slice.call(1)

The name Property of Functions

The name property of the function contains the function’s name.

For example, we can write:

function foo() {}

Then foo.name returns 'foo' .

Arrow functions also has the name property.

For instance, if we write:

`const` bar `=` `()` `=>` `{};`

Then bar.name returns 'bar' .

Default Values

If we use a function as a default value, then it gets its name from its variable or parameter.

For example, if we have:

`let` `[foo =` `function` `()` `{}]` `=` `[]`

Then foo.name is 'foo' .

Likewise,

`let` `{` bar`:` foo `=` `function` `()` `{}` `}` `=` `{};`

and

`function` `g(foo =` `function` `()` `{})` `{`
  `return` `foo.name;`
`}`

all get the same result.

Named Function Definitions

If we have function declarations, then the name property of the function will have the name:

function foo() {}
console.log(foo.name);

foo.name would be 'foo' .

For function expressions, we get the same thing:

const bar = function baz() {};
console.log(bar.name);

so bar.name is 'bar' .

However, if we assigned a named function to a variable, then the function’s name would be the function’s name.

For example, if we write:

const bar = function baz() {
  console.log(baz.name);
};

bar()

Then we call it with bar and baz.name would be baz .

But we can’t write baz() to call it, we’ll see the ‘Uncaught ReferenceError: baz is not defined’ error.

Methods in Object Literals

Methods in object literals can be defined with fixed and computed property names.

For instance, we can write:

function qux() {}

let obj = {
  foo() {},
  bar: function() {},
  ['ba' + 'z']: function() {},
  qux,
};

foo is defined with the object method shorthand.

bar is defined as a traditional method.

baz is defined with the computed key.

And qux is passed in from the outside.

If we get the name property of each method:

console.log(obj.foo.name);
console.log(obj.bar.name);
console.log(obj.baz.name);
console.log(obj.qux.name);

We get:

foo
bar
baz
qux

Conclusion

We can use the name property to get the property name of a function.

Also, we can call a constructor instance’s method in shorter ways in some situations.