最近流行りのVue.jsを使ってWeb開発をしているのですが、その過程でVue.jsのナビゲーションガードを使って、ユーザ別のトップページに遷移させる、というのを実装しました。
イメージとしては、ヘッダーに共通の「Home」ボタンがあって、これを押下した際に、ユーザ別のトップページに遷移させるというのものです。
#Vue.routerとは
Vue.jsの拡張ライブラリで、これを使うとシングルページアプリケーション(SPA)を構築することが出来ます。
シングルページアプリケーションは、複数のページ使ってを画面遷移をするのではなく、Webページ上の要素を置き換えることで画面遷移を実現する、というものです。
#ナビゲーションガードとは
公式にも説明はありますが、簡単に説明します。ナビゲーションガードを使うと、画面遷移する際に処理を加えることができ、遷移先を変更したりすることが出来ます。
#(その前に・・・)前提条件
今回はこのナビゲーション機能を使って、ヘッダーにあるホームボタンを押した際に、ユーザ別(一般ユーザ、管理者ユーザなど)のトップページに遷移させる処理を実装していきます。
が、その前に一つ前提条件があります。それはログインした際に、Vuexにログイン情報を保持しておくということです。
Vuexについての知識が浅い方はまず、それについて勉強してください。今回はログインの際に、「ログインID」、「パスワード」、「role」の3つの情報を、Vuexに保存しています。
なおこの「role」プロパティは1~3の値を取り、この値を見て、どのユーザー(一般ユーザーなのか管理ユーザーなのか)を判断していきます。
#ユーザ別のトップページに遷移する
それでは実装していきましょう。まずはVue.routerの設定です。
router.beforeEach((to, from, next) => {
//Vuexに保持しているloginオブジェクトを取得
const currentUser = store.state.login
//遷移先が/loginのときは、Vuexの値を空にする
if (currentUser.role) {
if (to.path == '/login') {
store.commit("loginUser", {})
}
//roleプロパティによって遷移先を振り分け
if (to.path == '/home') {
if (currentUser.role == 1) {
next({
path: '/A01',
})
}else if(currentUser.role == 2){
next({
path: '/B01'
})
}else if(currentUser.role == 3){
next({
path: '/C01'
})
} else {
next({
path: '/login',
})
}
}
}
})
遷移先を空のページである、/homeに一旦指定しておき、実際の遷移先は、Vuexに保持しているloginオブジェクトを取り出し、そのroleプロパティの値によって、振り分けています。roleプロパティが存在しない際は、ログインページに遷移するようにしています。
また、空のページとして指定する/homeですが、index.jsに最低限pathを指定しないと動きません。(nameとcomponentは記述しなくてもよい。なので、importもしなくてよい。)
どういうことかというと、index.jsに以下の記述が最低限必要ということです。
Vue.use(Router)
export default new Router({
routes: [
{
path: '/login',
name: 'Login',
component: Login,
meta: {
isPublic: true
}
},
{
path: '/A01',
name: 'A01',
component: A01
},
{
path: '/B01',
name: 'B01',
component: B01
},
{
path: '/C01',
name: 'C01',
component: C01
},
//ここの記述が最低限必要。
{
path: '/home'
},
{
path: '*',
redirect: '/Login'
}
]
})
また、リダイレクト機能も実装しました。リダイレクトに関してはこちらを参考に実装しました。
router.beforeEach((to, from, next) => {
const currentUser = store.state.login
//省略
if (to.matched.some(record => record.meta.isPublic)) {
next()
} else {
if (currentUser.role) {
next()
} else {
next({
path: '/login',
query: {
redirect: to.path
}
})
}
}
})
全体としてはこんな感じです。
router.beforeEach((to, from, next) => {
const currentUser = store.state.login
if (currentUser.role) {
if (to.path == '/login') {
store.commit("loginUser", {})
}
if (to.path == '/home') {
if (currentUser.role == 1) {
next({
path: '/A01',
})
}else if(currentUser.role == 2){
next({
path: '/B01'
})
}else if(currentUser.role == 3){
next({
path: '/C01'
})
} else {
next({
path: '/login',
})
}
}
}
if (to.matched.some(record => record.meta.isPublic)) {
next()
} else {
if (currentUser.role) {
next()
} else {
next({
path: '/login',
query: {
redirect: to.path
}
})
}
}
})
これで、画面遷移する際の準備が出来ました。
続いて画面側の実装をします。既にお分かりの方もいるかもしれませんが、ヘッダーのホームボタンを押した際、遷移先に/homeを指定すれば、ユーザ毎のトップページに遷移先を振り分けてくれます。
<template>
<div class="container">
<span v-if="this.$route.name !== 'A01' && this.$route.name !== 'B01' && this.$route.name !== 'C01' && this.$route.name !== 'Login'">
<h3 @click="home" class="home"><font-awesome-icon icon="home" /></h3></span>
<span v-if="this.$route.name !== 'Login'">
<h3 @click="logOut" class ="arrow"><font-awesome-icon icon="sign-out-alt" /></h3>
</span>
<div align="center" class="message">{{message}}</div>
</div>
</template>
<script>
import Auth from '@/Auth'
import store from "@/store"
export default {
name: 'Header',
data () {
return {
message:'〇〇システム',
Auth:Auth,
}
},
methods: {
home: function () {
this.$router.push('/home')
},
user: function(){
return this.$store.state.user
},
logOut: function () {
this.$router.push('/login')
}
},
computed:{
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.message{
clear: both;
}
.home{
float: left;
cursor: pointer;
}
.user{
float: right;
}
.arrow{
float: right;
cursor: pointer;
}
</style>
<template> の部分ででやっていることを少し捕捉すると、A01、B01、C01というように、各ユーザのトップページでは、「ホーム」ボタンを表示しないようにしています。また、ログインページにおいては、「ホーム」ボタンおよび「ログアウト」ボタンを表示しないようにしています。
もう一点。今回私のコードでは、ホームボタンにfont-awesomeのアイコンを使っていますが、これはなんでもかまいません。
<h3 @click="home" class="home"><font-awesome-icon icon="home" /></h3>
部分を
<h3 @click="home" class="home">Home</h3>
とすれば、アイコンではなくHomeという文字ボタンに変更できます。
はい。これでホームボタンを押した際、ユーザごとのトップページに遷移する、というのが実装できました。
#(おまけ)存在しないURLをたたいた時に、ログイン画面に遷移する。
おまけです。存在しないURLをたたいたとき、ログイン画面に遷移するというのを実装します。
今回は先に完成形のコードをお見せします。
Vue.use(Router)
export default new Router({
routes: [
{
path: '/login',
name: 'Login',
component: Login,
meta: {
isPublic: true
}
},
{
path: '/A01',
name: 'A01',
component: A01
},
{
path: '/B01',
name: 'B01',
component: B01
},
{
path: '/C01',
name: 'C01',
component: C01
},
{
path: '*',
redirect: '/Login'
}
]
})
この最後のところ
{
path: '*',
redirect: '/Login'
}
で、存在しないURLをたたいたとき、/loginに遷移させる、というのを実現しています。