Subscribe now

Vue.js UIs [08.16.2017]

Today we're focusing on writing layout code using Vue and Vue-Material. Specifically, we'll be building the Navbar and the Sign Up Page.

Building the Navbar

First, let's focus on building the Navbar. Unlike some of the other components, this one needs to exist outside of our router. To do this, let's open App.vue and make some changes to the file.

<template lang="pug">
  #app
    Navbar
    router-view
</template>

<script>
  import Navbar from './components/Navbar'
  export default {
    name: 'app',
    components: {Navbar}
  }
</script>

<style lang="stylus">
  background-color #FAFAFA
</style>

In the script tag, we imported the Navbar component that we had bootstrapped earlier. Then we added a components property in which we passed Navbar. This makes Navbar available for our template to display in the view. Meanwhile, in our template, we've added Navbar just above router-view. router-view is just a component that will render whatever the router tells it to render. When you go to the /login route, it will display the Login component and so on.

Now let's open Navbar.vue and write some code.

<template lang="pug">
md-toolbar
  router-link(tag="md-button", to="/", class="md-icon-button")
    md-icon home
  h2(class="md-title", style="flex: 1") Taskify
  md-button(@click="logout", v-if="authenticated") logout
  router-link(tag="md-button", to="/login", v-if="!authenticated") Login
  router-link(tag="md-button", to="/signup", v-if="!authenticated") Sign Up
</template>

<script>
import router from '../router/index'
import Store from '../Store'
export default {
  store: Store,
  computed: {
    authenticated: () => {
      return Store.state.authenticated
    }
  },
  methods: {
    logout () {
      localStorage.removeItem('idToken')
      this.$store.dispatch('logout')
      this.$router.push('/login')
    }
  }
}
</script>

<style lang="stylus">

</style>

Let's focus on the script tag. First, we are importing the router and Store.js. Then in export object, we integrate Store into our component. This gives our component access to the Vuex data store as well as the ability to commit mutations. Next, in our computed property, we have created the authenticated property which returns the authenticated variable from Store. This will make the authenticated state available to our template. Next we will define some methods. These are actions that can be activated from the template. For Navbar, we only need one method and that is the logout() method. In this case, logout() removes a JWT from localStorage, then dispatches the logout action we defined in our Vuex Store. Then we redirect our route back to /login.

Now let's take a look at the templatetag. You may be thinking what is all of this stuff being defined all over the place? Nearly everything you see here comes from Vue-Material. Vue-Material gives us these components such as md-toolbarandmd-buttonto construct our UIs quicker and easier. So knowing this, you can see where we have definedmd-toolbarwhich starts our Navbar. First we add arouter-linkwhich is just a button that can activate a route. We gave it amd-iconof **home**. Then we have our title called Taskify. Next, we have some buttons we're placing on our Navbar but take particular note of v-if. We are usingv-if` to tell our template that if a user is authenticated, only display the Logout button. If a user is not authenticated, then display the Login and Sign Up buttons.

Signup.vue

Now let's go to Signup.vue and also navigate our app to the /signup route. Let's write some code in Signup.vue.

<template lang="pug">
md-layout(md-gutter)
  md-layout(md-column, md-gutter)
    h1 Signup
    form(novalidate, @submit.stop.prevent="submit")
      md-input-container(md-inline)
        label Username
        md-input(v-model="username")
      md-input-container(md-inline, md-has-password=true)
        label Password
        md-input(v-model="password", type="password")
      md-input-container(md-inline, md-has-password=true)
        label Confirm Password
        md-input(v-model="confirm", type="password")
      md-button(@click="signup", class="md-accent md-raised") Sign Up
  md-layout(md-column)
</template>

<script >
import Store from '../Store'
export default{
  store: Store,
  data () {
    return {
      username: '',
      password: '',
      confirm: ''
    }
  },
  methods: {
    signup () {
      const self = this
      if (self.password !== self.confirm) {
        console.log('error')
      } else {
         self.$http.post('http://localhost:3000/user/new',{
          username: self.username,
          password: self.password
         })
         .then(result => {
           if (result.data.data !== 'success') {throw new Error('fail')}
           return self.$http.post('http://localhost:3000/token', {
             username: self.username,
             password: self.password,
             client_id: "my-awesome-clientid",
             client_secret: "my-awesome-clientSecret",
             grant_type: "password"
           })
         })
         .then(jwt => {
           const data = JSON.parse(jwt.data.data)
           localStorage.setItem('idToken', data.access_token)
           const jwtHeader = {'Authorization': 'Bearer ' + localStorage.getItem('idToken')}
           return self.$http.get('http://localhost:3000/user/' + this.username, {headers: jwtHeader})
         })
         .then(user => {
           this.$store.dispatch('login')
           this.$store.dispatch('setProfile', user.data.data)
           self.$router.push('/')
         })
         .catch(err => {
           console.log(err)
         })
      }
    }
  }
}
</script>

<style lang="stylus" scoped>
form
  margin-left 1em
h1
  margin-left 0.5em
</style>

For now, let's only focus on the template tag. First, we used md-layout to establish a grid via Vue-Material then we set up a form. In our form, we're preventing the default submit action then using md-input-container to set up our inline text inputs. Pay special attention to md-input, particularly the v-model attribute. This binds the value of md-input to whatever value you have defined in script. In this case, our Username input is assigned to the v-model called username. If you look inside the script tag under the data property, you will see username defined here along with password and confirm. Now if you go back to template and look through the inputs, you'll see username, password, and confirm assigned to the corresponding inputs. In the next lesson, you will see how this makes it far easier to access these data properties programmatically for any operations you may need such as the signup operation being called on the last md-button.

Conclusion

Tomorrow we will go over the script tag of this component and learn how to make HTTP requests and handle the responses using Vue-Resource.