Remember: Ionic VueJS Support is Still in Beta
Overview
Ionic Framework with VueJS build a Split-View user interface with Side menu. The application uses the official vuejs state manager, vuex, 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 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
import store from "./store";
Checking for user when app starts up
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
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.
// 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
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
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
<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.
// 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,
},
})
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
// 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
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
<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
<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
<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.
// 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