From Vanilla JS to Vue.js: A guide to Vue essentials
Tags: javascript, vue, pwa
Published: 2018-05-14 19:05:50

---
title: From Vanilla JS to Vue.js: A guide to Vue essentials
published: true
description: Learn Vue.js essentials with a side-by-side comparison to vanillaJS
tags: javascript, vuejs, pwa
canonical_url: https://pmbanugo.me/from-vanilla-js-to-vuejs-a-guide-to-vue-essentials
cover_image: https://thepracticaldev.s3.amazonaws.com/i/oqtj8qsmyuei9rqez1as.jpeg
---

[Vue.js](https://vuejs.org) is a framework for building web applications. It has a reactivity system that allows you to model and manage your application state such that when data changes, it's reflected in the UI, without you having to query the DOM. If you’ve built apps in vanilla JavaScript or with jQuery, you know you need to query DOM elements and update them in order to display some data or show some other application state. 

For a large application this becomes difficult to manage. Last week I spent a few hours to learn some basic things, and adopted it in a side-project built in vanilla JS. I want to share with you some of my learnings and compare side by side the differences in code. The [project](https://github.com/pmbanugo/shopping-list) is a shopping list [progressive web application](https://developer.mozilla.org/en-US/Apps/Progressive) which uses [Hoodie](http://hood.ie/). 


![](https://d2mxuefqeaa7sj.cloudfront.net/s_91FF8ED057E446856C9CF50C4D669F5A0F04EACF5A7BF3A9A812074AEC870A7E_1526036344108_shop.png)


If you want to follow along, you can download the [source code](https://github.com/pmbanugo/shopping-list) in Vanilla JS while I show you how I added in Vue (follow this [link](https://www.twilio.com/blog/2018/03/practical-introduction-pwa-node-hoodie-offline-first.html) if you want to read about how I built the app in Vanilla JS, Hoodie, and Service Worker).


# Adding items

The application allows users to add shopping items to their shopping list. This is done in `index.html`  in the public folder. Lines 92 to 124 contains the markup for this:

```html
<div>
  <div class="mdl-grid center-items">
  <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="text" id="new-item-name">
      <label class="mdl-textfield__label" for="new-item-name">Item Name</label>
  </div>
  <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="number" id="new-item-cost">
      <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label>
  </div>
  <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="number" id="new-item-quantity">
      <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label>
  </div>
  </div>

  <div class="mdl-grid center-items">
  <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
      Add Item
  </button>
  </div>
</div>
```

The code to handle data collection and saving the data is in the file `public/js/src/index.js`. The function `saveNewItem()` on line 28 collects the value from the input elements and saves the item. It is then bound to the click event of the `add-item` button. Here’s the code:

```javascript
function saveNewitem() {
  let name = document.getElementById("new-item-name").value;
  let cost = document.getElementById("new-item-cost").value;
  let quantity = document.getElementById("new-item-quantity").value;
  let subTotal = cost * quantity;
  if (name && cost && quantity) {
    hoodie.store.withIdPrefix("item").add({
      name: name,
      cost: cost,
      quantity: quantity,
      subTotal: subTotal
    });
    document.getElementById("new-item-name").value = "";
    document.getElementById("new-item-cost").value = "";
    document.getElementById("new-item-quantity").value = "";
  } else {
    let snackbarContainer = document.querySelector("#toast");
    snackbarContainer.MaterialSnackbar.showSnackbar({
      message: "All fields are required"
    });
  }
}

document.getElementById("add-item").addEventListener("click", saveNewitem);
```


## Switching to Vue

In adopting Vue the first thing is to add a reference to Vue on your page. I added this to `index.html` as follows:


    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>

I also added a `div` element with an id of `app` to surround every page elements inside the body tag. This is needed because when we will initialise a Vue instance, we need to tell it what section of our app we want it to control. And by doing this I’m telling it to manage everything inside that block. I modified the markup to use some Vue directives. Vue Directives are special attributes with the `v-` prefix. Below is the updated markup

```html
<form v-on:submit.prevent="onSubmit">
  <div class="mdl-grid center-items">
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="text" id="new-item-name" v-model="name">
      <label class="mdl-textfield__label" for="new-item-name">Item Name</label>
    </div>
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="number" id="new-item-cost" v-model.number="cost">
      <label class="mdl-textfield__label" for="new-item-cost">Item Cost</label>
    </div>
    <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
      <input class="mdl-textfield__input" type="number" id="new-item-quantity" v-model.number="quantity">
      <label class="mdl-textfield__label" for="new-item-quantity">Quantity</label>
    </div>
  </div>

  <div class="mdl-grid center-items">
    <button id="add-item" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
      Add Item
    </button>
  </div>
</form>
```

The `v-on` directive if used to listen to DOM events. In code above it’s used in the form element to listen to the submit event. It also uses the `.prevent`  modifier which tells the `v-on` directive to call `event.preventDefault()` on the triggered event. We’ve used `v-model` directives on the input elements. It is used to create two-way data bindings on form input. It will automatically pick the correct way to update the element based on the input type. We’ve used the `.number` modifier for the cost and quantity input elements. What it does is to automatically typecast the value from the input element to a number. This is because even if the type set is `type=number` the value will always return string. So these modifiers I’ve used here helps short-circuit some extra checks checks we would have had to do. 

I created a new file `index-vue.js` to contain code equivalent of what is in `index.js` but using Vue. Below is the code in this file, which creates a Vue instance with needed properties to handle the form event and collect data. 

```javascript
const vm = new Vue({
  el: "#app",
  data: {
    name: "",
    cost: "",
    quantity: ""
  },
  methods: {
    onSubmit: function(event) {
      if (this.name && this.cost && this.quantity) {
        hoodie.store.withIdPrefix("item").add({
          name: this.name,
          cost: this.cost,
          quantity: this.quantity,
          subTotal: this.cost * this.quantity
        });

        this.name = "";
        this.cost = "";
        this.quantity = "";
      } else {
        const snackbarContainer = document.querySelector("#toast");
        snackbarContainer.MaterialSnackbar.showSnackbar({
          message: "All fields are required"
        });
      }
    }
  }
});
```

In the code block above, I created a Vue instance passing it an object that tells Vue how to setup the application. The `el` property tells it the id of the DOM element that Vue will pick and define its territory. It’s within this territory that it picks Vue directives (and other things related to Vue), and when it is initialised, it sets up bindings and event handlers for the app. 

The `data`  property contains the application state. All the properties in the containing object here will be added to Vue’s reactivity system when the Vue instance is initialised. It is this reactivity system that causes the UI to update when one of the values bound to the DOM changes. For example, the `name` property is bound to the name input element using the `v-model="name"` directive. That directive sets up a two-way binding between the `name` and the input element such that when a character is added or removed in the input field, it updates the `name` property which will cause the value of the input to reflect the current value of `name`. Same way other elements bound to the `name` will also change as a user types in value.  

The `methods` property contains functions. The code above defines an `onSubmit()` function that is bound to the form’s submit event.

# Displaying Saved Items

The `onSubmit` functions saves an item to Hoodie. I want to display the items added in a table in the UI. The Vanilla JS app had the following markup for it:

```html
<div class="mdl-grid center-items">
  <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
    <thead>
      <tr>
        <th class="mdl-data-table__cell--non-numeric">Item Name</th>
        <th class="mdl-data-table__cell--non-numeric">Cost</th>
        <th class="mdl-data-table__cell--non-numeric">Quantity</th>
        <th class="mdl-data-table__cell">Sub-total</th>
        <th class="mdl-data-table__cell--non-numeric">
          <button class="mdl-button mdl-js-button mdl-button--icon">
            <i class="material-icons">delete</i>
          </button>
        </th>
      </tr>
    </thead>
    <tbody>

    </tbody>
  </table>
</div>
<div class="mdl-grid center-items">
  <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <input class="mdl-textfield__input" type="number" id="total-cost" readonly value="0">
    <label class="mdl-textfield__label" for="cost">Total Item Cost</label>
  </div>
</div>

<script id="item-row" type="text/template">
  <tr id='{{row-id}}'>      
    <td class="mdl-data-table__cell--non-numeric">{{name}}</td>
    <td class="mdl-data-table__cell--non-numeric">{{cost}}</td>
    <td class="mdl-data-table__cell--non-numeric">{{quantity}}</td>
    <td class="mdl-data-table__cell">{{subTotal}}</td>
    <td class="mdl-data-table__cell--non-numeric">
          <button class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored"
          onclick="pageEvents.deleteItem('{{item-id}}')">
          <i class="material-icons">remove</i>
          </button>
    </td>
  </tr>
</script>
```

In the markup above I used micro-template because the table will contain dynamic data and I want a way to replace some placeholder with real data and attach it to the DOM. 

Below is the code that displays the items in the UI as it gets added:

```javascript
function addItemToPage(item) {
  if (document.getElementById(item._id)) return;
  let template = document.querySelector("#item-row").innerHTML;
  template = template.replace("{{name}}", item.name);
  template = template.replace("{{cost}}", item.cost);
  template = template.replace("{{quantity}}", item.quantity);
  template = template.replace("{{subTotal}}", item.subTotal);
  template = template.replace("{{row-id}}", item._id);
  template = template.replace("{{item-id}}", item._id);
  document.getElementById("item-table").tBodies[0].innerHTML += template;
  
  let totalCost = Number.parseFloat(
    document.getElementById("total-cost").value
  );
  
  document.getElementById("total-cost").value = totalCost + item.subTotal;
}

hoodie.store.withIdPrefix("item").on("add", addItemToPage);
```

In the code block above, it gets the script template from the DOM, replaces the placeholders with actual data, then appends it to the DOM. The total cost is also calculated and displayed in the UI.


## The Vue Alternative

Transitioning to Vue I removed the script template from the page and updated the table element to use Vue’s `v-for` directive which will loop through data property that contains the items. Below is the markup

```html
<div class="mdl-grid center-items">
  <table id="item-table" class="mdl-data-table mdl-js-data-table mdl-shadow--2dp">
    <thead>
      <tr>
        <th class="mdl-data-table__cell--non-numeric">Item Name</th>
        <th class="mdl-data-table__cell--non-numeric">Cost</th>
        <th class="mdl-data-table__cell--non-numeric">Quantity</th>
        <th class="mdl-data-table__cell">Sub-total</th>
        <th class="mdl-data-table__cell--non-numeric">
          <button class="mdl-button mdl-js-button mdl-button--icon">
            <i class="material-icons">delete</i>
          </button>
        </th>
      </tr>

    </thead>
    <tbody>
      <tr v-for="item in items" :key="item._id">
        <td class="mdl-data-table__cell--non-numeric">{{ item.name}}</td>
        <td class="mdl-data-table__cell--non-numeric">{{ item.cost}}</td>
        <td class="mdl-data-table__cell--non-numeric">{{ item.quantity}}</td>
        <td class="mdl-data-table__cell">{{ item.subTotal}}</td>
        <td class="mdl-data-table__cell--non-numeric">
          <button @click="deleteRow(item._id)" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
            <i class="material-icons">remove</i>
          </button>
        </td>
      </tr>
    </tbody>
  </table>
</div>

<div class="mdl-grid center-items">
  <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
    <!-- <input class="mdl-textfield__input" type="number" id="total-cost" readonly value="0">
    <label class="mdl-textfield__label" for="cost">Total Item Cost</label> -->
    <h4>Total Cost: {{ total }}</h4>
  </div>

</div>
```

There isn’t a big change in the markup. I copied the content from the previous micro-template and used Vue directives and text interpolation. I’m using the `v-for` directive to render the list of items which will be gotten from a data property called `items`. The respective columns renders the data using Vue’s text interpolation `{{ item.name }}`. This is similar to the placeholder we used with the micro-template. Total is displayed on the page using text interpolation. 

Updating the JavaScript code in `index-vue.js` will give us the following:

```javascript
const vm = new Vue({
  el: "#app",
  data: {
    name: "",
    cost: "",
    quantity: "",
    items: []
  },
  computed: {
    // a computed getter
    total: function() {
      // `this` points to the vm instance
      return this.items.reduce(
        (accumulator, currentValue) => accumulator + currentValue.subTotal,
        0
      );
    }
  },
  methods: {
    .....
  }
});

hoodie.store.withIdPrefix("item").on("add", item => vm.items.push(item));
```

The Vue adaptation is much shorter and simpler. What I did in the code above was add a data property `items`, which is what gets used in the `v-for` directive seen earlier. When an item gets added Hoodie calls the function which runs `vm.items.push(item)` to update the state and with Vue’s reactive system the UI is automatically updated. To calculate the total there’s no need to track items in the DOM. I used a computed property which runs a reduce function on `items`. Now with Vue’s reactive system the UI gets updated whenever any of these values change. The good thing here is that I don’t have to worry about DOM manipulation in my code. So in fewer lines of code we achieved what required more code when using vanilla JS (I reckon it would be a similar thing with jQuery).


# Save items as a list

After adding items, I want to save them for later reference and be able to add another list of shopping items. I have a button `Save List` which will gather the items, save them as a group of items with hoodie, and allow the user add new set of items. 

The Vanilla JS version had the button bound to a click event. Below is the markup and code that made it work

```html
//index.html
<div class="mdl-grid center-items">
  <button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" onclick="pageEvents.saveList()">
    Save List
  </button>
</div>
```

```javascript
//index.js
function saveList() {
  let cost = 0;

  hoodie.store
    .withIdPrefix("item")
    .findAll()
    .then(function(items) {
      for (var item of items) {
        cost += item.subTotal;
      }

      //store the list
      hoodie.store.withIdPrefix("list").add({
        cost: cost,
        items: items
      });

      //delete the items
      hoodie.store
        .withIdPrefix("item")
        .remove(items)
        .then(function() {
          //clear the table
          document.getElementById("item-table").tBodies[0].innerHTML = "";

          //notify the user
          var snackbarContainer = document.querySelector("#toast");
          snackbarContainer.MaterialSnackbar.showSnackbar({
            message: "List saved succesfully"
          });
        })
        .catch(function(error) {
          //notify the user
          var snackbarContainer = document.querySelector("#toast");
          snackbarContainer.MaterialSnackbar.showSnackbar({
            message: error.message
          });
        });
    });
}

window.pageEvents = {
  deleteItem: deleteItem,
  saveList: saveList
  ....
};
```

## Vue Alternative

Switching to Vue didn’t require much of a difference. I still had to bind to a click event and added the event handler method to the methods property in the Vue options object during initialisation. 

Below is the markup for it:

```html
<div class="mdl-grid center-items">
  <button @click="saveList" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
    Save List
  </button>
</div>
```

The `@click="saveList"`  is a shorthand for `v-on:click=saveList"` which is used to listen to a DOM event. The same `saveList` function from the Vanilla JS version is added to the methods property of the Vue object.

# Navigation Bar

Now that the items can be saved as a list, I want to see a history with the total cost of each list in a period of time. It will be on another page and will look like what’s shown in the image below


![](https://www.twilio.com/blog/wp-content/uploads/2018/03/1SZ-kxjbuM83S4pmMZ0LzgtUE0diKobqJtEFqGW0KQWnTh5Js0CqRNVmLQFh_XAXuFoxzrH04iTOWl9K0coAbygVoCBLjpdIdrh1nyrJhd9bSuoQwj2BviIYoJYMh39K8JYF_FwQ.png)


This page has its markup in `public/history.html` and code to control the page in `public/js/src/history.js`. This page shares some code in common with `index.html` which is the navigation bar at the top. The navigation bar contains the links to different pages, the `Login` and `Register` links which when clicked brings up login or register dialog forms, and the `Signout` button.

 In the version of the app that’s using Vanilla JS I duplicated the same HTML markup in both pages. Below is the markup for the Navigation bar: 
 
```html
<header class="mdl-layout__header">
    <div class="mdl-layout__header-row">
    <!-- Title -->
    <span class="mdl-layout-title">Shopping List</span>
    <!-- Add spacer, to align navigation to the right -->
    <div class="mdl-layout-spacer"></div>
    <!-- Navigation. We hide it in small screens. -->
    <nav class="mdl-navigation mdl-layout--large-screen-only">
        <a class="mdl-navigation__link" href="index.html">Home</a>
        <a class="mdl-navigation__link" href="history.html">History</a>
        <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a>
        <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a>
        <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>
    </nav>
    </div>
</header>
<div class="mdl-layout__drawer">
    <span class="mdl-layout-title">Shopping List</span>
    <nav class="mdl-navigation">
    <a class="mdl-navigation__link" href="index.html">Home</a>
    <a class="mdl-navigation__link" href="history.html">History</a>
    <a onclick="pageEvents.showLogin()" style="cursor: pointer" class="mdl-navigation__link login">Login</a>
    <a onclick="pageEvents.showRegister()" style="cursor: pointer" class="mdl-navigation__link register">Register</a>
    <a onclick="pageEvents.signout()" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>
    </nav>
</div>
```

From the markup you can see that when the links for Login, Register, and Logout are clicked, they call their respective methods. Those page event handlers are defined in `index.js` 

```javascript
import * as shared from "shared.js";

....

shared.updateDOMLoginStatus();
window.pageEvents = {
  showLogin: shared.showLoginDialog,
  showRegister: shared.showRegisterDialog,
  signout: shared.signOut
};

The actual functions that gets called are defined in `shared.js`. Below are the functions in `shared.js` responsible for the navigation bar:


//register dialog element
let loginDialog = document.querySelector("#login-dialog");
dialogPolyfill.registerDialog(loginDialog);
let registerDialog = document.querySelector("#register-dialog");
dialogPolyfill.registerDialog(registerDialog);

let showLoginDialog = function() {
  loginDialog.showModal();
};

let showRegisterDialog = function() {
  registerDialog.showModal();
};

let showAnonymous = function() {
  document.getElementsByClassName("login")[0].style.display = "inline";
  document.getElementsByClassName("login")[1].style.display = "inline";
  document.getElementsByClassName("register")[0].style.display = "inline";
  document.getElementsByClassName("register")[1].style.display = "inline";
  document.getElementsByClassName("logout")[0].style.display = "none";
  document.getElementsByClassName("logout")[1].style.display = "none";
};

let showLoggedIn = function() {
  document.getElementsByClassName("login")[0].style.display = "none";
  document.getElementsByClassName("login")[1].style.display = "none";
  document.getElementsByClassName("register")[0].style.display = "none";
  document.getElementsByClassName("register")[1].style.display = "none";
  document.getElementsByClassName("logout")[0].style.display = "inline";
  document.getElementsByClassName("logout")[1].style.display = "inline";
};

let updateDOMLoginStatus = () => {
  hoodie.account.get("session").then(function(session) {
    if (!session) {
      // user is singed out
      showAnonymous();
    } else if (session.invalid) {
      // user is signed in, but session is no longer authenticated
      showAnonymous();
    } else {
      // user is signed in
      showLoggedIn();
    }
  });
};

let signOut = function() {
  hoodie.account
    .signOut()
    .then(function() {
      showAnonymous();
      let snackbarContainer = document.querySelector("#toast");
      snackbarContainer.MaterialSnackbar.showSnackbar({
        message: "You logged out"
      });
      location.href = location.origin;
    })
    .catch(function() {
      let snackbarContainer = document.querySelector("#toast");
      snackbarContainer.MaterialSnackbar.showSnackbar({
        message: "Could not logout"
      });
    });
};

export {
  signOut,
  showRegisterDialog,
  showLoginDialog,
  updateDOMLoginStatus
};
```

This code exports functions which was used in `index.js`. The `showLoginDialog()` and `showRegisterDialog()` functions displays a Modal for login and register respectively. The `signout()` functions logs the user out and calls `showAnonymous()` which hides the `Logout` link and shows only the `Register` and `Login` links. The function `updateDOMLoginStatus` checks if the user is authenticated and displays the appropriate links. This function is called when the page loads. 

Achieving a shared navigation bar required duplicating markup and querying DOM elements and applying CSS to show and hide links in the navigation bar. Let’s look at the Vue alternative.


## Vue Alternative

Many web applications have portions that are the same across pages, for example navigation headers. These should be abstracted into some sort of container or component. Vue provides what is called a component, which can be used to solve the issue of the navigation bar in this example. Vue components are self-contained and re-usable. 

Moving to Vue components I created a new file `shared-vue.js`. Inside it I defined a Vue component for the navigation bar as follows:

```javascript
Vue.component("navigation", {
  props: ["isLoggedIn", "toggleLoggedIn"],
  template: `<div>
              <header class="mdl-layout__header">
        <div class="mdl-layout__header-row">
          <!-- Title -->
          <span class="mdl-layout-title">Shopping List</span>
          <!-- Add spacer, to align navigation to the right -->
          <div class="mdl-layout-spacer"></div>
          <!-- Navigation. We hide it in small screens. -->
          <nav class="mdl-navigation mdl-layout--large-screen-only">
            <a class="mdl-navigation__link" href="index.html">Home</a>
            <a class="mdl-navigation__link" href="history.html">History</a>
            <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a>
            <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a>
            <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>
          </nav>
        </div>
      </header>
      <div class="mdl-layout__drawer">
        <span class="mdl-layout-title">Shopping List</span>
        <nav class="mdl-navigation">
          <a class="mdl-navigation__link" href="index.html">Home</a>
          <a class="mdl-navigation__link" href="history.html">History</a>
          <a v-show="!isLoggedIn" @click="showLogin" style="cursor: pointer" class="mdl-navigation__link login">Login</a>
          <a v-show="!isLoggedIn" @click="showRegister" style="cursor: pointer" class="mdl-navigation__link register">Register</a>
          <a v-show="isLoggedIn" @click="logout" style="cursor: pointer" class="mdl-navigation__link logout">Logout</a>
        </nav>
      </div>
            </div>`,
  methods: {
    showLogin: function() {
      const loginDialog = document.querySelector("#login-dialog");
      dialogPolyfill.registerDialog(loginDialog);
      loginDialog.showModal();
    },
    showRegister: function() {
      const registerDialog = document.querySelector("#register-dialog");
      dialogPolyfill.registerDialog(registerDialog);
      registerDialog.showModal();
    },
    logout: function() {
      hoodie.account
        .signOut()
        .then(() => {
          this.toggleLoggedIn();
        })
        .catch(error => {
          alert("Could not logout");
        });
    }
  }
});
```

In the code above we registered a Vue component named `navigation` with an options object similar to what we used when creating a Vue instance. The first property is the [**props**](https://vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props)**.** Props are a way to pass data to a component. A component can define its own data, but in cases where a piece of application state needs to be used in different components, props are used. The `isLoggedIn` props holds a boolean value showing if a user is authenticated or not. 

The second property `template` contains the markup that will be shown in the page. The markup is almost exactly like the vanilla JS alternative in the previous section, except that we’ve used two Vue directives, `v-show`  and `@click`. The `v-show` attribute is used for conditional rendering. Here I’m telling it to show the `Logout` link when `isLoggedIn` is true, or show `Login` and `Register` links when it’s false. Vue also provides `v-if` and `v-else` for conditional rendering and you can read more about them [here](https://vuejs.org/v2/guide/conditional.html). The `@click` attribute is a shorthand for `v-on:click` directive. I’ve set `showLogin`, `showRegister`, and `logout` as event handlers for click events of the respective links. 

These functions are defined in the methods property. The `logout` function after successful signout, calls `this.toggleLoggedIn()` which is the props passed to this component. This will execute the function passed props, and is expected to change the value of `isLoggedIn` props which is this component can’t modify. When it changes, Vue’s reactivity system will update the DOM accordingly. 

This component is added to `index.html` like as a custom element. I’ll remove the navigation bar markup from lines 59 to 84 and replace it with the following


    <navigation v-bind:is-logged-in="isLoggedIn" v-bind:toggle-logged-in="toggleLoggedIn"></navigation>

In the JavaScript code we declared props `isLoggedIn` and `toggleLoggedIn`, but when passing props these values use their kebab-cased equivalents. This is because HTML attributes are case-insensitive. I’ve used the `v-bind` directive to pass values for these props dynamically. Without this directive, it’ll be passed as a static value and the component will receive the string `isLoggedIn` instead of a boolean value. We can as well use the shorthand `:` for `v-bind` and it can be re-written as `<navigation :is-logged-in="isLoggedIn" :toggle-logged-in="toggleLoggedIn"></navigation>`

The value `isLoggedIn` is an application state and `toggleLoggedIn` is a method declared in the Vue instance in `index-vue.js` as follows

```javascript
const vm = new Vue({
  el: "#app",
  data: {
    name: "",
    cost: "",
    quantity: "",
    items: [],
    isLoggedIn: false
  },
  computed: {
    .....//collapsed code
  },
  methods: {
    toggleLoggedIn: function() {
      this.isLoggedIn = !this.isLoggedIn;
    },
    ......//collapsed code
  }
});

.....//collapsed code

hoodie.account.get("session").then(function(session) {
  if (!session) {
    // user is singed out
    vm.isLoggedIn = false;
  } else if (session.invalid) {
    vm.isLoggedIn = false;
  } else {
    // user is signed in
    vm.isLoggedIn = true;
  }
});
```

With the Vue alternative I’ve eliminated duplicate markup and if in the future I need to make any change for it, I will do it from one location, and this is done using Vue component. I eliminated having to traverse/query the DOM to select which elements to show or hide based on the authentication state. 

# Login Dialog

The `Login` and `Register` links shows a modal which allows a user to enter username and password to get authenticated. The markup for both are duplicated across pages just like the Navigation bar. This can be seen on lines 171 to 244 in `index.html` and lines 100 to 158 in `history.html`. 

```html
<dialog id="login-dialog" class="mdl-dialog">
  <h4 class="mdl-dialog__title">Login</h4>
  <div class="mdl-dialog__content">
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <input class="mdl-textfield__input" type="text" id="login-username">
        <label class="mdl-textfield__label" for="login-username">Username</label>
      </div>
    </div>
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <input class="mdl-textfield__input" type="password" id="login-password">
        <label class="mdl-textfield__label" for="login-password">Password</label>
      </div>
    </div>
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <span id="login-error"></span>
      </div>
    </div>
  </div>
  <div class="mdl-dialog__actions">
    <button onclick="pageEvents.closeLogin()" type="button" class="mdl-button close">Cancel</button>
    <button onclick="pageEvents.login()" type="button" class="mdl-button">Login</button>
  </div>
</dialog>

<dialog id="register-dialog" class="mdl-dialog">
  <h4 class="mdl-dialog__title">Login</h4>
  <div class="mdl-dialog__content">
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <input class="mdl-textfield__input" type="text" id="register-username">
        <label class="mdl-textfield__label" for="register-username">Username</label>
      </div>
    </div>
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <input class="mdl-textfield__input" type="password" id="register-password">
        <label class="mdl-textfield__label" for="register-password">Password</label>
      </div>
    </div>
    <div class="mdl-grid center-items">
      <!-- Simple Textfield -->
      <div class="mdl-textfield mdl-js-textfield">
        <span id="register-error"></span>
      </div>
    </div>
  </div>
  <div class="mdl-dialog__actions">
    <button onclick="pageEvents.closeRegister()" type="button" class="mdl-button close">Cancel</button>
    <button onclick="pageEvents.register()" type="button" class="mdl-button">Register</button>
  </div>
</dialog>
```

The code to handle both login and register is defined in `shared.js` and used in `index.js`

```javascript
//shared.js

//register dialog element
let loginDialog = document.querySelector("#login-dialog");
dialogPolyfill.registerDialog(loginDialog);
let registerDialog = document.querySelector("#register-dialog");
dialogPolyfill.registerDialog(registerDialog);

let closeLoginDialog = function() {
  loginDialog.close();
};

let closeRegisterDialog = function() {
  registerDialog.close();
};

let showAnonymous = function() {
  ...
};

let showLoggedIn = function() {
  ....
};

let signOut = function() {
  ....
};

let updateDOMLoginStatus = () => {
  ....
};

let login = function() {
  let username = document.querySelector("#login-username").value;
  let password = document.querySelector("#login-password").value;

  hoodie.account
    .signIn({
      username: username,
      password: password
    })
    .then(function() {
      showLoggedIn();
      closeLoginDialog();

      let snackbarContainer = document.querySelector("#toast");
      snackbarContainer.MaterialSnackbar.showSnackbar({
        message: "You logged in"
      });
    })
    .catch(function(error) {
      console.log(error);
      document.querySelector("#login-error").innerHTML = error.message;
    });
};

let register = function() {
  let username = document.querySelector("#register-username").value;
  let password = document.querySelector("#register-password").value;
  let options = { username: username, password: password };

  hoodie.account
    .signUp(options)
    .then(function(account) {
      return hoodie.account.signIn(options);
    })
    .then(account => {
      showLoggedIn();
      closeRegisterDialog();
      return account;
    })
    .catch(function(error) {
      console.log(error);
      document.querySelector("#register-error").innerHTML = error.message;
    });
};

export {
  register,
  login,
  closeRegisterDialog,
  closeLoginDialog,
  ...
};
``` 

**index.js**

```javascript
//index.js

window.pageEvents = {
  closeLogin: shared.closeLoginDialog,
  showLogin: shared.showLoginDialog,
  closeRegister: shared.closeRegisterDialog,
  showRegister: shared.showRegisterDialog,
  login: shared.login,
  register: shared.register,
  signout: shared.signOut
};
```


## Vue Alternative

When switching to Vue I used separate component for both login and register component. Below is the component registration for login dialog

```javascript
Vue.component("login-dialog", {
  data: function() {
    return {
      username: "",
      password: ""
    };
  },
  props: ["toggleLoggedIn"],
  template: `<dialog id="login-dialog" class="mdl-dialog">
      <h4 class="mdl-dialog__title">Login</h4>
      <div class="mdl-dialog__content">
        <div class="mdl-grid center-items">
          <!-- Simple Textfield -->
          <div class="mdl-textfield mdl-js-textfield">
            <input v-model="username" class="mdl-textfield__input" type="text" id="login-username">
            <label class="mdl-textfield__label" for="login-username">Username</label>
          </div>
        </div>
        <div class="mdl-grid center-items">
          <!-- Simple Textfield -->
          <div class="mdl-textfield mdl-js-textfield">
            <input v-model="password" class="mdl-textfield__input" type="password" id="login-password">
            <label class="mdl-textfield__label" for="login-password">Password</label>
          </div>
        </div>
        <div class="mdl-grid center-items">
          <!-- Simple Textfield -->
          <div class="mdl-textfield mdl-js-textfield">
            <span id="login-error"></span>
          </div>
        </div>
      </div>
      <div class="mdl-dialog__actions">
        <button @click="closeLogin" type="button" class="mdl-button close">Cancel</button>
        <button @click="login" type="button" class="mdl-button">Login</button>
      </div>
    </dialog>`,
  methods: {
    closeLogin: function() {
      const loginDialog = document.querySelector("#login-dialog");
      dialogPolyfill.registerDialog(loginDialog);
      loginDialog.close();
    },
    login: function(event) {
      hoodie.account
        .signIn({
          username: this.username,
          password: this.password
        })
        .then(() => {
          this.toggleLoggedIn();
          this.closeLogin();
        })
        .catch(error => {
          console.log(error);
          document.querySelector("#login-error").innerHTML = "Error loggin in";
        });
    }
  }
});
```

It is registered with data, props, template and methods as properties of the options object passed to `Vue.component()`. Then on the pages I replace the markup with Vue’s custom element


    //index.html
    <login-dialog v-bind:toggle-logged-in="toggleLoggedIn"></login-dialog>

Similar steps apply to the register dialog which I’ve skipped. 

I also skipped showing some parts of the app in order to avoid showing duplicate Vue syntax. Follow this [link](https://www.twilio.com/blog/2018/03/practical-introduction-pwa-node-hoodie-offline-first.html) if you want to learn how I built the whole app step by step. It also explained concepts such as [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers/) and [Push API.](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) 

# Conclusion

So far I’ve shown you some of the changes I made to my application while moving from Vanilla JS to Vue.js. It’s not complicated to start using Vue if you’ve just learned how to build web apps with HTML, CSS and JavaScript (or jQuery). You don’t need to know ES6 or understand any build step to get started. I achieved less code duplication and better code organisation while using Vue. I only covered the basic things you need to understand to start using Vue, but of course there’s more to Vue. In a future post I’ll cover more concepts as I keep digging into it and hopefully share how to build a fairly complex single page applications with it. 

You can find the complete code for the Vanilla JS  and Vue.js applications on GitHub using the links below. 

- [Shopping List - Vanilla JS](https://github.com/pmbanugo/shopping-list)
- [Shopping List - Vue.js](https://github.com/pmbanugo/shopping-list-vue)

> Thanks to [Andrew Hedges](https://twitter.com/segdeha) for taking the time to review this.