Categories
Vue Tips

Vue Tips — Filters and Errors

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 use filters to format data on the screen and how to avoid errors in the console.

Create Filters to Reuse Formatting

Formatting data on the screen can be annoying. We’ve to deal with many formats for numbers, percentages, dates, currencies, etc. It gets complicated fast.

This is why we want to make a reusable piece of code so that we only deal with these problems once and forget about it.

Fortunately, Vue has filters, which we can define to format data our way. It makes templates cleaner by eliminating formatting code from templates and components.

We can define a Vue filter and use it as follows:

index.js :

Vue.filter("localeDateString", date => {
  return date.toLocaleDateString();
});

new Vue({
  el: "#app",
  data: {
    date: new Date()
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{date | localeDateString}}
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we used the Vue.filter method to define a filter. The first argument is the name of the filter. The second is a function that takes a value that we want to format and we return a formatted string.

Then we set the date value to new Date() and applied the localeDateString to it with the pipe operator.

We can reuse this anywhere since it’s a global filter declaration. We can also make a local filter declaration with the filters property in the component options object as follows:

index.js :

new Vue({
  el: "#app",
  data: {
    date: new Date()
  },
  filters: {
    localeDateString(date) {
      return date.toLocaleDateString();
    }
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{date | localeDateString}}
    </div>
    <script src="index.js"></script>
  </body>
</html>

The only difference is that we moved our filter function into the filters object. We can only use a local filter within the component.

We can also pass in arguments to filters as follows:

index.js :

Vue.filter("readMore", (text, moreText) => {
  return `${text.toUpperCase()} ${moreText}`;
});

new Vue({
  el: "#app",
  data: {
    message: "foo"
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{message | readMore('bar')}}
    </div>
    <script src="index.js"></script>
  </body>
</html>

We just pass in arguments like any other function, except that the argument ends up as the value of the 2nd parameter and beyond.

Also, we can chain the filters by adding them one by one from left to right in the order that we want to apply them.

Avoid Annoying Errors and Warnings

We should know the causes of errors and warning that shows in the developer console so that we can eliminate them without taking too long.

For instance, the ‘Property or method “prop” is not defined on the instance but referenced during render…” error shows up when we reference a data in the template but we didn’t initialize it in the object that we return with data .

It can also happen if we have a prop or variable but we misspelled the prop or variable name.

In this case, we should check for typos. For instance, we’ll get the error if we have something like:

index.js :

new Vue({
  el: "#app",
  data: {
    message: "foo"
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      {{messag}}
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have {{messag}} in the template, but we have message: “foo” in the component code.

So we should have sure that they’re the same and the error will be gone.

We have also defined it in the property on a different component than the template is referencing. This is also a common mistake that we make.

Conclusion

Creating and using filters make formatting data on templates easy. We can define them globally or locally. We can compose them by chaining them and we can also pass arguments to them to change the options.

The most common error message that comes from Vue is the ‘Property or method “prop” is not defined on the instance but referenced during render…” error.

This means that we’re referencing items in templates that haven’t been defined as property in the object we returned with data .

Categories
Vue Tips

Vue Tips — Transitions, Routes, and Data

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 create reusable transitions and making requests in a Vue app.

Encapsulated Transition Component

We can encapsulate transitions into its own component so that we can reuse it anywhere.

For instance, we can create a transition component that has the transition component and then reuse it as follows:

styles.css :

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}

index.js :

Vue.component("foo-transition", {
  template: `
  <transition name="fade">
    <slot></slot>
  </transition>
  `
});

new Vue({
  el: "#app",
  data: {
    show: true
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link type="text/css" rel="stylesheet" href='styles.css'></link>
  </head>
  <body>
    <div id="app">
      <button v-on:click="show = !show">
        Toggle transition
      </button>
      <foo-transition>
        <p v-if='show'>foo</p>
      </foo-transition>
      <foo-transition>
        <p v-if='show'>bar</p>
      </foo-transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have the reusable foo-transition component that has a transition component with a slot inside so we can toggle on and off what we want within the foo-transition component with the transition effects applied to it.

Then in index.html , we reused foo-transition by writing:

<foo-transition>
  <p v-if='show'>foo</p>
</foo-transition>
<foo-transition>
  <p v-if='show'>bar</p>
</foo-transition>

Then when we toggle show between true and false , we’ll see both p elements have the fade transition effect applied to it.

Also, we can also use v-bind='$attrs' to bind attributes from parent to child and v-on='$listeners' to pass event listeners from parent to child.

For instance, we can change the example above as follows:

styles.css :

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}

.foo {
  color: green;
}

index.js :

Vue.component("foo-transition", {
  template: `
  <transition name="fade" v-bind="$attrs" v-on="$listeners">
    <slot></slot>
  </transition>
  `
});

new Vue({
  el: "#app",
  data: {
    show: true
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link type="text/css" rel="stylesheet" href='styles.css'></link>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle transition
      </button>
      <foo-transition class="foo">
        <p v-if='show'>foo</p>
      </foo-transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we have v-bind=”$attrs” in transitions . So we’ll get the foo class applied to it if we set the class to foo in foo-transition .

Use Axios for Data Fetching

Vue doesn’t come with its own HTTP client, so to make HTTP requests, we have to add our own. Axios is a good choice since it’s available via CDN and as a Node package.

Also, it’s promise-based so that we won’t have to deal with nested callbacks.

For instance, we can use it as follows:

index.js :

new Vue({
  el: "#app",
  data: {
    info: {}
  },
  async mounted() {
    const { data } = await axios.get(
      "https://api.coindesk.com/v1/bpi/currentprice.json"
    );
    this.info = data;
  }
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <div id="app">
      <p v-if="info.bpi">
        {{info.bpi['USD'].code}} - {{info.bpi['USD'].rate}}
      </p>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we used the Axios library by including it from CDN. Then we get the data in the mounted hook. Since axios.get returns a promise, we can use the async and await syntax to make the request.

An alternative would be the Fetch API, which is similar to Axios, but we don’t have to add a library to use it.

Use vue-router to Handle Client-Side Routing

Vue Router is one of the most commonly used router for Vue apps. It lets us map URLs to components and also get route and query parameters and pass them into our components.

It also supports nested routes and transitions.

A simple example would be the following:

index.js :

const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };

const routes = [
  { path: "/foo", component: Foo },
  { path: "/bar", component: Bar }
];

const router = new VueRouter({
  routes
});

new Vue({
  el: "#app",
  router
});

index.html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
    <div id="app">
      <router-link to="/foo">Foo</router-link>
      <router-link to="/bar">Bar</router-link>
      <router-view></router-view>
    </div>
    <script src="index.js"></script>
  </body>
</html>

In the code above, we added the Vue Router and then add the router-view to display the route content, which are the components we mapped to.

Then we have the router-link that we can click on to navigate between the routes.

Conclusion

We can create reusable transition components by adding slots inside transition components. To make HTTP requests, we can use the Axios HTTP client to make send requests easy. Finally, we can use Vue Router to map URLs to our components.

Categories
Vue Tips

Vue Tips — Cleaning up Props, Computed Properties, and Watchers

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 ways to make developing Vue apps even easier, including reducing props accepted by components, and removing confusion between computed properties and watchers.

Cleaning up Props

We should reduce the number of props that a component takes. It takes components harder to work with and makes the code longer.

We can do this in several ways. The best way is to pass them in as the properties of objects of one prop instead of multiple props. For instance, we can write the following code to do that:

components/Button.vue :

<template>
  <button :style="styles">Button</button>
</template>

<script>
export default {
  name: "Button",
  props: {
    styles: Object
  }
};
</script>

App.vue :

<template>
  <div id="app">
    <Button :styles="{color: 'white', backgroundColor: 'black', outline: 'none'}"/>
  </div>
</template>

<script>
import Button from "./components/Button";

export default {
  name: "App",
  components: {
    Button
  }
};
</script>

In the code above, we passed in an object as the value of the style prop. It’s much more compact than passing in multiple props for each style.

Another example would be pass in multiple attributes with the v-bind directive:

components/Button.vue :

<template>
  <button v-bind="attrs">Button</button>
</template>

<script>
export default {
  name: "Button",
  props: {
    attrs: Object
  }
};
</script>

App.vue :

<template>
  <div id="app">
    <Button :attrs="{autofocus: 'true', name  : 'foo', type: 'button', value: 'foo'}"/>
  </div>
</template>

<script>
import Button from "./components/Button";

export default {
  name: "App",
  components: {
    Button
  }
};
</script>

In the code above, we pass in the attrs prop into the Button component, and then we used v-bind=”attrs” to apply them as attributes for out button element.

We should see the attributes applied when we inspect the button in the developer console.

Don’t Confuse Computed Properties and Watchers

Computed properties should be used as much as possible for derived properties unless we need to watch something and create side effects from them.

A computed property lets us derive new data from existing data. For instance, if we have firstName and lastName and we want to create a property fullName from firstName and lastName , we create a computed property as follows:

<template>
  <div id="app">{{fullName}}</div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      firstName: "Jane",
      lastName: "Smith"
    };
  },
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`;
    }
  }
};
</script>

In the code above, we have the computed property, which has the fullName method that returns `${this.firstName} ${this.lastName}` .

Then we reference it as a property like what we have in data in the template with {{fullName}} .

Computed properties are reactive. A new value will be returned if either firstName or lastName change. Therefore, it’ll always be up to date.

To do this with watchers, we have to write the following:

<template>
  <div id="app">{{fullName}}</div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      firstName: "Jane",
      lastName: "Smith",
      fullName: ""
    };
  },
  watch: {
    firstName: {
      immediate: true,
      handler() {
        this.fullName = `${this.firstName} ${this.lastName}`;
      }
    },
    lastName: {
      immediate: true,
      handler() {
        this.fullName = `${this.firstName} ${this.lastName}`;
      }
    }
  }
};
</script>

As we can see, the code above is much more complex, and we have to remember to set immediate to true , so that we can watch the initial value change in addition to the subsequent ones. In the handler functions, we have to set this.fullName each time.

It’s a much more complex and error-prone way to do the same thing as a computed property.

Computed properties are pure functions, whereas watcher handlers commit side effects, which also make them harder to test.

Where watchers are useful is for creating side effects. As we can see from the code above, it’s used for creating side effects by setting this.fullName as a combination of this.firstName and this.lastName .

For instance, we can use it as follows:

<template>
  <div id="app">
    <input v-model="name">
    <p>{{info.age}}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      name: "",
      info: {}
    };
  },
  watch: {
    async name(val) {
      const response = await fetch(`https://api.agify.io?name=${val}`);
      this.info = await response.json();
    }
  }
};
</script>

In the code above, we watch for the value of the name field and then calls the Agify API to get some data from the name.

Then we set the value of the response to this.info . We can’t do this with computed properties since we don’t want to return a promise in computed properties.

Conclusion

We should clean up our props by passing them in as objects instead of multiple props.

Computed properties are good for deriving data from existing data, while watchers are good for committing side effects like when we need to run asynchronous code.