Categories
JavaScript Vue

Passing Props to Vue.js Route Components with Vue Router

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

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at how to pass props to route components with the Vue Router.

Passing Props

Using the $route in our component creates a tight coupling between the $route object and the component. Therefore, it limits the flexibility of the component as it can be only used on certain URLs.

We can decouple the component from the router by using the props option.

For example, instead of writing:

const User = { template: "<div>User {{ $route.params.id }}</div>" };  
const routes = [  
  {  
    path: "/user/:id",  
    component: User  
  }  
];

const router = new VueRouter({  
  routes  
});

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

We instead write:

src/index.js :

const User = { template: "<div>User {{ id }}</div>" };  
const routes = \[  
  {  
    path: "/user/:id",  
    component: User,  
    props: true  
  }  
\];const router = new VueRouter({  
  routes  
});new Vue({  
  el: "#app",  
  router  
});

src/index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

If we have multiple router-views , then we have to define the props on each one as follows:

src/index.js :

const Foo = { props: ["id"], template: "<div>foo {{id}}</div>" };  
const Bar = { props: ["id"], template: "<div>bar {{id}}</div>" };  
const routes = [  
  {  
    path: "/:id",  
    components: { default: Foo, bar: Bar },  
    props: { default: true, bar: true }  
  }  
];

const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
      <router-view name="bar"></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we go to /#/1 , we see:

foo 1bar 1

displayed.

As we can see when props is set to true , then $route.params will be set as the component props.

This makes testing easier since we don’t have to worry about the $route object when testing.

Object Mode

We can set the props property to an object. This is handy for static props.

For example, we can do that as follows:

src/index.js :

const Foo = { props: ["id"], template: "<div>foo {{id}}</div>" };  
const routes = [  
  {  
    path: "/foo",  
    component: Foo,  
    props: { id: 1 }  
  }  
];

const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src=[https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we see foo 1 displayed when we go to /#/foo .

Function Mode

We can pass in a function that returns props. This lets us do whatever we want to with the route parameters before sending it to the component as props.

src/index.js :

const Foo = { props: ["idString"], template: "<div>foo {{idString}}</div>" };  
const routes = [  
  {  
    path: "/foo/:id",  
    component: Foo,  
    props: route => ({ idString: `id ${route.params.id}` })  
  }  
];  
const router = new VueRouter({  
  routes  
});  
new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
      <router-view name="bar"></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we set a function as the property of props where we returned an object that sets the idString property to `id ${route.params.id}` .

Then our Foo component accepts the idString prop instead of getting id straight from the route parameter. Therefore, we see the ‘id’ before the id route parameter that we passed in since that’s what we returned.

So when we go to /#/foo/1 , we get foo id 1 displayed.

Conclusion

We can pass in route parameter to components as props instead of retrieving route parameters from the $route object.

This makes testing easier and also makes the component more flexible since it decouples the component from the Vue Router.

We can set the props property to true to pass in a route parameter as a prop. For static props, we can set an object as the value of props .

We can also set it as a function to manipulate the route parameter before returning an object where the value will be sent as props to the component and retrieved by the key of the returned object in the component.

Categories
JavaScript TypeScript Vue

Writing Vue.js Apps with TypeScript

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 write Vue.js apps with TypeScript.

Support for TypeScript

Vue.js has built-in support for TypeScript. It provides us with a mostly static type system that reduces the chances for run-time errors in our code.

Vue ships with official type declarations for TypeScript. It’s included in Vue, Vue Router and Vuex.

TypeScript can resolve types declarations in NPM packages, so we don’t have to worry about them resolving them ourselves.

Using Vue CLI

We can use the Vue CLI to create a Vue TypeScript project. To do this, we have to run Vue CLI by running:

npx vue create .

in our project directory.

Then we select ‘Manually select features’ and then TypeScript. The rest can stay with the default options.

Then we run npm run serve to run the project hot reload.

If we want, we can also set the following recommended options in tsconfig.json :

{  
  "compilerOptions": {  
    "target": "es5",  
    "strict": true,  
    "module": "es2015",  
    "moduleResolution": "node"  
  }  
}

target is the build target. Setting it to es5 maximizes support for browsers.

strict enables or disables strict type check. Setting it to true will enable it.

“module”: “es2015” leverage tree shaking capabilities of Webpack 2+ or Rollup to reduce bundle size and speed up loading.

Defining and Using Components

We can define components by using the class syntax. For example, with the TypeScript project that we created with Vue CLI, we can do this as follows:

src/components/Message.vue :

<template>  
  <div class="hello">  
    <h1>{{ msg }}</h1>  
  </div>  
</template><script lang="ts">  
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component 
export default class Message extends Vue {  
  @Prop() private msg!: string;  
}  
</script>

In the code above, we have the @Prop decorator to define the message prop. The exclamation mark after msg makes msg required. To the right of the colon, we have the type of the prop, which is a string.

src/App.vue :

<template>  
  <div id="app">  
    <input v-model="message">  
    <Message :msg="message"/>  
    <button @click="showMessage">Show Message</button>  
  </div>  
</template>

<script lang="ts">  
import { Component, Vue } from 'vue-property-decorator';  
import Message from './components/Message.vue';

@Component({  
  components: {  
    Message,  
  },  
})  
export default class App extends Vue {  
  message: string = ''; showMessage(): void{  
    alert(this.message)  
  }  
}  
</script><style>  
#app {  
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif  
}  
</style>

In the code above, we defined the message field, which is bound to the template with v-model .

We also have a showMessage method that’s run when the Show Message button is clicked.

Then we can see an input box where we can type things in. What we type will show in below the input box in the Message component, and when we click Show Message, we get what we typed is shown in the alert box.

Use Vue.extend to Create Components

We can also use Vue.extend to create components.

For example, we can replace src/components/Message.ts with the following”

import Vue, { VNode } from 'vue'
const Component = Vue.extend({  
  props: ['msg'],  
  computed: {  
    greeting(): string {  
      return `Hi. ${this.msg}`;  
    }  
  },  
  render(createElement): VNode {  
    return createElement('div', [  
      createElement('p', this.msg),  
      createElement('p', this.greeting),  
    ])  
  }  
})export default Component;

In the code above, we render a div with the msg prop and greeting computed property.

Note that we have VNode return type for render . This will ensure that we return the right type of data. TypeScript can’t infer types in many cases.

Since we use Vue.extend , we’ll get type inference and autocomplete.

We have to export by writing:

export default Component;

so that it’ll be available to other components.

Then we can import it as usual in src/App.vue :

<template>  
  <div id="app">  
    <input v-model="message">  
    <Message :msg="message"/>  
    <button @click"showMessage">Show Message</button>  
  </div>  
</template><script lang="ts">  
import { Component, Vue } from 'vue-property-decorator';  
import Message from './components/Message';

@Component({  
  components: {  
    Message,  
  },  
})  
export default class App extends Vue {  
  message: string = ''; showMessage(): void{  
    alert(this.message)  
  }  
}  
</script><style>  
#app {  
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif  
}  
</style>

Then we the input where we can type in something and it’ll show up with and without Hi added before it.

We keep the Show Message button acting the way before.

Augmenting Types for Use with Plugins

We can add our own properties to existing types by writing the following:

import Vue from 'vue'declare module 'vue/types/vue' {  
  interface Vue {  
    $myProperty: string  
  }  
}

The code above imports Vue and then add the $myProperty string property as a valid property.

This should make TypeScript compiler be aware of the code and it should compile successfully.

Conclusion

We can create a new project with TypeScript by using the Vue CLI. To do this, we choose Manually Select Features and then TypeScript. We can keep the default option for the rest or we can choose what we want if we know we want it.

To get type inference and autocomplete, we can define components with the Vue.extend method or define it as a class.

We can augment existing Vue types by using the declare module syntax and adding what we want inside it.

TypeScript can’t infer return types of functions in many cases. Therefore, we should annotate the return type so that we get autocomplete and type checking.

Categories
JavaScript Vue

Introduction to Vue.js Routing

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

In order to create a single-page app with Vue.js, we have to map URLs to components so that when users go to a URL, it’ll show the corresponding component.

In this article, we’ll look at how to create some simple routes with the Vue Router.

Getting Started

We can use by Vue Router by adding a script tag with the URL for the Vue Router library.

We can make a simple app as follows:

src/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>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <div>  
        <router-link to="/foo">Foo</router-link>  
        <router-link to="/bar">Bar</router-link>  
      </div>  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we defined 2 components Foo and Bar in src/index.js .

Then we mapped them to routes with:

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

const router = new VueRouter({  
  routes  
});

Then we created a new Vue instance with:

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

In the template, we have router-links to map the routes to a tags with the URLs to correspond to the routes we define:

<router-link to="/foo">Foo</router-link>  
<router-link to="/bar">Bar</router-link>

Then we have router-view to show the components that are mapped to routes:

<router-view></router-view>

In the end, we should get:

Foo Link Bar Linkfoo

when we click on Foo Link .

and:

Foo Link Bar Linkbar

when we click on Bar Link .

Once we injected the router, we also get access to this.$router .

We can use it to navigate between routes. For example, we can write the following:

src/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,  
  methods: {  
    goBack() {  
      window.history.length > 1 ? this.$router.go(-1) : this.$router.push("/");  
    }  
  }  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <div>  
        <router-link to="/foo">Foo Link</router-link>  
        <router-link to="/bar">Bar Link</router-link>  
        <a href="#" @click="goBack">Go Back</a>  
      </div>  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we click the Foo Link and Bar Link links a few times then click Go Back , we’ll see it’ll go back to the previous routes that we navigated to.

Route Parameters

We can get route parameters with the this.$route.params object.

For example, we can write an app that shows the URL parameter that’s passed in as follows:

src/index.js :

const User = {  
  computed: {  
    username() {  
      return this.$route.params.username;  
    }  
  },  
  template: `<div>{{username}}</div>`  
};

const routes = [{ path: "/user/:username", component: User }];
const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <div>  
        <router-link to="/user/foo">Foo</router-link>  
        <router-link to="/user/bar">Bar</router-link>  
      </div>  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The :username in “/user/:username” is the parameter, so we can get what passed in after /user/ by using this.$route.params.username .

We added a computed property username which returns this.$route.params.username so we can use:

`<div>{{username}}</div>`

to show the username URL parameter.

Conclusion

We can map URLs to components by using Vue Router.

Once we included it, we can define routes which map URLs to components. They can also take parameters, which can be retrieved with the this.$route object that’s made available by injecting the Vue router into our app.

To display links that link to Vue Router routes, we use router-link , and to display the components mapped to routes, we use router-view .

Categories
JavaScript Vue

Redirect and Alias with Vue Router

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

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at how to add redirects and alias to our routes.

Redirect

We can add redirects from one route to another by adding the redirect property to a route.

For example, we can add a redirect as follows:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: "/bar" },  
  { path: "/bar", component: Bar }  
];

const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we go to /#/bar in the browser, we get bar displayed.

A redirect can also target a named route. For example, we can write:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: { name: "bar" } },  
  { path: "/bar", component: Bar, name: "bar" }  
];

const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we get the same result.

We can also set the redirect property to a function. For instance, we can set it to a function that returns a route as follows:

src/index.js :

const Bar = { template: "<div>bar</div>" };  
const routes = [  
  { path: "/foo", redirect: to => "bar" },  
  { path: "/bar", component: Bar }  
];

const router = new VueRouter({  
  routes  
});

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

Then we get the same result as the previous examples.

Navigation guards aren’t applied on routes that redirect, only on its target.

Alias

A redirect means that when a user visits /foo then the URL will be replaced by /bar and then matched as /bar .

An alias means of /foo that’s set as /bar means that when the user visits /bar , the URL remains /bar , but it’ll be matched as if the user is visiting /foo .

For example, we can define a route with an alias as follows:

src/index.js :

const Foo = { template: "<div>foo</div>" };  
const routes = [{ path: "/foo", component: Foo, alias: "/bar" }];

const router = new VueRouter({  
  routes  
});new Vue({  
  el: "#app",  
  router  
});

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then we get foo when we go to /#/foo or /#/bar .

An alias lets us map any route to any URL without being constrained by the nesting structure of the routes.

This is handy for mapping a URL that we want for a nested route.

For example, if we define an alias for a nested route as follows:

src/index.js :

const Foo = {  
  template: `<div>  
    foo  
    <router-view></router-view>  
  </div>`  
};  
const Bar = { template: "<div>bar</div>" };  
const routes = [  
  {  
    path: "/foo",  
    component: Foo,  
    children: [{ path: "/bar", component: Bar }]  
  }  
];

const router = new VueRouter({  
  routes  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

Then when we go to /#/foobar , we get:

foobar

displayed on the screen.

We also get the same thing displayed if we go to /#/foo/bar .

Therefore, we don’t have to follow the usual convention for nested route URLs if we use an alias.

Conclusion

Redirects and aliases are handy features of the Vue Router.

Redirects let us redirect from one route to another. Navigation guards aren’t run on redirect routes.

Aliases let us create a new URL for an existing route. When we go to the path or the alias, we get the same result when we map a path to an alias.

This is handy when we don’t want to follow the usual convention for nested route URLs for example.

Categories
JavaScript Vue

Vue.js Directives — Arguments and Values

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 make our directives accept arguments and values to directives and use them in our code.

Dynamic Directive Arguments

We can pass in arguments and values to a directive and then get these values from the binding parameter.

For example, we can make a directive that takes an argument and value as follows:

src/index.js :

Vue.directive("position", {  
  bind(el, binding, vnode) {  
    const validPositions = ["relative", "fixed", "absolute"];  
    if (validPositions.includes(binding.arg)) {  
      el.style.position = binding.arg;  
      el.style.top = `${binding.value}px`;  
    }  
  }  
});

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

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://cdn.jsdelivr.net/npm/vue/dist/vue.js](https://cdn.jsdelivr.net/npm/vue/dist/vue.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <p v-position:absolute="50">{{message}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

In the code above, we defined a positiion directive that takes an argument, which is accessed in the bind hook by using the binding parameter.

The binding parameter has an arg property to get the argument, which is absolute in index.html .

binding.value gets us the value, which is 50 in index.html .

Then we get the element that the directive is bound to with el and then we set the positioning of the element via the values from the binding parameter.

Accepting arguments and values make directives much more flexible than without arguments.

Function Shorthand

If we only have code in the bind and update hooks, then we can just pass in a function with the same signature as those hooks.

For example, we can shorten the example above to:

Vue.directive("position", (el, binding, vnode) => {  
  const validPositions = ["relative", "fixed", "absolute"];  
  if (validPositions.includes(binding.arg)) {  
    el.style.position = binding.arg;  
    el.style.top = `${binding.value}px`;  
  }  
});

They both do the same thing.

Object Literals

We can pass in an object literal as the value of a directive if our directive needs to accept multiple values.

For example, we can write the following code to accept an object’s properties’ values in our directive:

src/index.js:

Vue.directive("position", (el, binding, vnode) => {  
  const validPositions = ["relative", "fixed", "absolute"];  
  const { position, top } = binding.value;  
  if (validPositions.includes(position)) {  
    el.style.position = position;  
    el.style.top = top;  
  }  
});

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

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">  
      <p v-position="{ position: 'absolute', top: '50px' }">{{message}}</p>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>

The code above accepts an object with the position and top property and we take the object’s values and then set them as the style of the p element which the position directive is bound to.

A directive’s value can be any valid JavaScript expression.

Conclusion

Vue directives can take arguments and values. We can get argument values from the binding parameter of the bind hook via the binding.arg property.

To get the value passed to a directive, we can get it from the bind hook’s binding parameter via the binding.value property.

If our directive only has bind or update hooks, then we can shorten it to a function, which has the same signature as the bind or update hooks.