Ionic Framework with VueJS: Split-View Menu with Authentication Flow Using, Vuex & Vue Composition
Tags: ionic, javascript, vue, tutorial
Published: 2020-06-01 16:14:36

>Remember: Ionic VueJS Support is Still in Beta

###Overview
[Ionic Framework](www.ionic.com) with [VueJS](vuejs.org) build a Split-View user interface with Side menu. The application uses the official [vuejs state manager, vuex](https://vuex.vuejs.org/), for authentication state management in the login flow. We also use information from the store to protect routes and hide the side-menu when the user is not authenticated.

The second part of the blog post, we show how to do the same but using the new [Vue Composition API](https://github.com/vuejs/composition-api) to manage state and implement the same functionality.
>Since Vue Composition API is for v3.x, we re using a plugin to provide that functionality to this v2.x application

This post is to cover the important pieces of the code; the bulk of the details are contained in the two video showing each of the specific implementations
> **📺 You can jump to end of post to see the videos...**


###Using Vuex
####Setup
Import the store in `main.js`
```javascript
import store from "./store";
```
Checking for user when app starts up
```javascript
store.dispatch("user/checkAuth").then(() => {
  new Vue({
    render: h => h(App),
    store,
    router
  }).$mount("#app");
});
```
####Protecting Routes
We need to get access to the state information in our `beforeEnter` handler to protect routes. Since we are using namespaces, the user state is at `store.state.user` and the actual user is `store.state.user.user` in this case we are checking for the existence of a user to determine if we should allow access to the specific route
```javascript
const privateRoute = (to, from, next) => {
  let userStore = store.state.user;
  let isAuthenticated = userStore.user !== null;
  console.log("isAuthenticated:" + isAuthenticated);
  
  if (!isAuthenticated) {
    next({ name: "login" });
  } else {
    next();
  }
};
```
####Logging Into Application
For logging into the application we can access the store using `$store` and dispatch the login function the combination of the namespace and the action and passing the payload.
```javascript
// login.vue
export default {
  name: "Login",
  methods: {
    async doLogin() {
      let result = await this.$store.dispatch("user/login", {
        email: this.email,
        password: this.password
      });
      if (result) {
        console.log(this.$store.state);
        this.$router.push("/");
      }
    }
  },
```
####Controlling Menu Display and Content
we use a computed property to get the `currentUser`
```javascript
computed: {
  currentUser() {
    return this.$store.state.user.user;
  }
},
```
For logging out we dispatch the `logout` action in the same manner that we dispatched the `login` action above
```javascript
async logout() {
  let menuController = document.querySelector("#main-menu");
  await menuController.close(true);
  await store.dispatch("user/logout");
  this.$router.replace("/login");
}
```

To hide the menu we use `currentUser` computed property from the component, we could have used `isLoggedIn`
```html
<template >
  <ion-menu content-id="main" side="start" 
            id="main-menu" v-if="currentUser"
...rest of template code
</template>
```

####The Store
Since we we are using `namespaces` we need to do a bit more setup on the store.
```javascript
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './auth-store'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    user,
  },
})
```
```javascript
export default {
  namespaced: true,

  state: {
    user: null,
    authChecked: false,
  },

  // ACTIONS (asynchronous)
  actions: {
    checkAuth({ commit }) {
      let state = this.state.user;
      return new Promise((resolve) => {
        commit("checkAuth", { authChecked: true, user: state.user });
        resolve(true);
      });
    },
    login({ commit }, payload) {
      if (payload.email !== "") {
        commit("hasUser", { ...payload });
        return true;
      } else {
        commit("clearUser", {});
        return false;
      }
    },
    logout({ commit }) {
      return new Promise((resolve) => {
        commit("clearUser", {});
        resolve(true);
      });
    },
  },

  // MUTATIONS ( set the state )
  mutations: {
    hasUser(state, payload) {
      state.user = { ...payload };
    },
    clearUser(state, payload) {
      state.user = null;
    },
    checkAuth(state, payload) {
      state.user = payload.user;
      state.authChecked = payload.authChecked;
    },
  },
};
```
###Using Vue Composition

####Setup
```javascript
// main.js
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
```
####Protecting Routes
We need to get access to the state information in our `beforeEnter` handler to protect routes
```javascript
const privateRoute = (to, from, next) => {

let { state } = useAuth();
let isAuthenticated = state.value.loggedIn;
... rest of the code
```

####Logging Into Application
For logging into the application we don't need to use the `setup` approach as we did above, you can just import `useAuth` and call the function on the module
```javascript
<script>
import useAuth from "../useAuth";
export default {
  name: "Login",
  methods: {
    async doLogin() {
      let { login } = useAuth();
      login();
      this.$router.push("/");
    }
  },
... rest of script
</script>
```

####Controlling Menu Display and Content
```javascript
<script>
import useAuth from "../useAuth";

export default {
  name: "Menu",
  // VUE COMPOSITION
  setup() {
    let { state, logout } = useAuth();
    return {
      state: state.value,
      logout
    };
  },
```
In this component, we are using the new `setup` functionality to incorporate the information returned from the vue composition api in the the component as data properties.

Now to call the logout function you must use `this.logout`. To hide the menu we can get the `loggedIn` state from the component now
```html
<template >
  <ion-menu content-id="main" side="start" 
            id="main-menu" v-if="state.loggedIn">
...rest of template code
</template>
```
####The Store
I tried to keep the store straight forward with no real code for authentication, this is really to demonstrate the approach.

So just calling the `login` function will log the user in and set the appropriate state values.

The `logout` function clears the user object and sets `loggedIn` to false. 
```javascript
// useAuth.js
import Vue from "vue";
import VueCompositionApi, { computed, ref } from "@vue/composition-api";
Vue.use(VueCompositionApi);

// STATE
const state = ref({
  user: {},
  loggedIn: false,
  error: {},
});

export default function() {
  return {
    state: computed(() => state.value),
    login: () => {
      state.value.user = { id: 100, name: "aaron" };
      state.value.loggedIn = true;
    },
    logout: () => {
      state.value.user = {};
      state.value.loggedIn = false;
    },
  };
}
```
###📺 Videos
{% youtube T6YL1jeH8EM %}
{% youtube -QJlRn-R_so %}