Subscribe now

Vue.js & Vue Resource [08.17.2017]

Yesterday we built a Navbar and a Signup page. However, we haven't actually learned what is going on within the script tag of Signup.vue. So let's open up Signup.vue and take a look at what is going on in the script tag.

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>

As you can see, we're importing our Vuex Store then incorporating it into our Vue Component. Then we have the data property which is used for simple data definitions that our component or template may need access to like in our input forms from yesterday. Next we have our method's property with our big signup function. Yes, this one is a doozy but don't worry, we'll take this step by step.

First, we're creating a self variable then checking to make sure that password and confirm are equal. If not, throw an error. Next, we will use Vue-Resource to make our first HTTP call to the api with self.$http.post where we pass the url followed up by the username and password that we are grabbing from the data property we discussed earlier. This action returns a Promise and in this case, the Promise should let us know if we successfully created a User or not. If the operation failed, throw an error. Otherwise we will now make another call to the api requesting an access token. Once the token is received, we store the token in LocalStorage, set up Authorization header using the Bearer scheme. Then we make one more call to the api, this time to get the user profile. Take note that this time, we are passing an additional parameter into this HTTP request. Specifically, we are defining any headers we wish to pass down which in this case, is our authorization header. Once we get our user profile from the api, we dispatch our login action in Vuex which sets the authenticated variable in our Vuex State to true. Then we dispatch another action, specifically, the setProfile action. With setProfile we pass in the data we receive from the api as an argument then setProfile uses that data to assign the profile information such as the username and tasks. Lastly, redirect to the / route where the actual Taskify application lives.

Phew, that was a lot to go through! Now let's reinforce this stuff by opening Login.vue and writing out some code for it. You will likely notice the same patterns taking place here.

Login.vue

<template lang="pug">
md-layout(md-gutter)
  md-layout(md-column, md-gutter)
    h1 Login
    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-button(@click="login", class="md-accent md-raised") Login
  md-layout(md-column)
</template>

<script>
import Store from '../Store'
export default {
  store: Store,
  data () {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    login () {
      this.$http.post('http://localhost:3000/token', {
        username: this.username,
        password: this.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 this.$http.get('http://localhost:3000/user/' + this.username, {headers: jwtHeader})
      })
      .then(user => {
        this.$store.dispatch('login')
        this.$store.dispatch('setProfile', user.data.data)
        this.$router.push('/')
      })
      .catch(err => {
        console.log(err)
      })
    }
  }
}
</script>

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

When perusing the code, you should see a lot of the same from Signup.vue. In the template tag, we have our Vue-Material components, our form, md-input-containers, v-model bindings, it's all the same. Same with the script tag only it's shorter and more friendly looking as we're only requesting the token, then the user profile.

Conclusion

Admittedly, this was a lot to take in at one time. The most important takeaway here is that you are able to see how Vue, Vue-Material, Vue-Resource, and Vuex are able to interact with each other to get the job done. Tomorrow, we will further reinforce this by building the actual To-Do portion of our Taskify app. If there is any confusion at this point, my hope is that the next lesson will really drive home how all of this works together to help you get your job done effectively.