Vue routerを活用したアプリケーション開発

- 作者: 川口和也,喜多啓介,野田陽平,手島拓也,片山真也
- 出版社/メーカー: 技術評論社
- 発売日: 2018/09/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
これを毎日コツコツと進めておりますが、現在
4.Vue routerを活用したアプリケーション開発
に入りました。
ルーティングの基礎から
細かく別れて書かれてます。
4.4 サンプルアプリケーションの実装
辺りから
- リストページ
- リストページの改修
- 詳細ページ
- ユーザー登録ページ
- ログインページとログイン状態
ってなるのだけど、それをちょっとずつちょっとずつ「追加」していくわけなんだけど、「追加」は上になるのか?下になるのか?ってことがいまいちわからず。
[3. 詳細ページ]から[5.ログインページとログイン状態]までは実は一気に書かないと実装がわからないという作り。。。
なんだけど、それぞれはパーツでしかないから、いざ起動するとエラーが・・・
Error in render: "TypeError: Cannot read property 'loggedIn' of undefined"
とかって何〜状態。
loggedIn辺りを確認してみても変わらない。
結局は、積み上げ方が間違えていたってことらしいのだけど。
[https://jsfiddle.net/kitak/qkrzyh08:title]
を参考にしてみたらこういうことらしい。
htmlだけだとこんな感じで動いた。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue.js SPAのサンプルアプリケーション</title>
</head>
<body>
<div id="app">
<nav>
<!-- router-link によるナビゲーション定義-->
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-link to="users/new?redirect=true">新規ユーザー登録</router-link>
<router-link to="/login" v-show="!Auth.loggedIn()">ログイン</router-link>
<!-- <router-link to="/logout" v-show="Auth.loggedIn()">ログアウト</router-link> -->
</nav>
<router-view></router-view>
</div>
<!-- Vue.js本体とVue Routerの読み込み -->
<script src="http://unpkg.com/vue@2.5.17"></script>
<script src="http://unpkg.com/vue-router@3.0.1"></script>
<!-- ここからを書く-->
<!-- 必要な分のコンポーネントのテンプレート定義 -->
<!--ログインページ-->
<script type="text/x-template" id="login">
<div>
<h2>Login</h2>
<p v-if="$route.query.redirect">
ログインしてください
</p>
<form @submit.prevent="login">
<label><input v-model="email" placeholder="email"></label>
<label><input v-model="pass" placeholder="password"></label>
<br>
<button type="submit">ログイン</button>
<p v-if="error" class="error">ログインに失敗しました</p>
</form>
</div>
</script>
<!-- ユーザー作成ページ -->
<script type="text/x-template" id="user-create">
<div>
<div class="sending" v-if="sending">Sending...</div>
<div>
<h2>新規ユーザー作成</h2>
<div>
<label>名前:</label>
<input type="text" v-model="user.name">
</div>
<div>
<label>説明文:</label>
<textarea v-model="user.description"></textarea>
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div>
<input type="button" @click="createUser" value="送信">
</div>
</div>
</div>
</script>
<!--詳細ページ-->
<script type="text-x-template" id="user-detail">
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="user">
<h2>{{ user.name }}</h2>
<p>{{ user.description }}</p>
</div>
</div>
</script>
<!-- ユーザー一覧 -->
<script type="text/x-template" id="user-list">
<!-- コンポーネントで使用するテンプレートHTMLを記載。コンポーネントごとに繰り返しscriptタグで定義-->
<div>
<div class="loading" v-if="loading">読み込み中...</div>
<div v-if="error" class="error">
{{ error }}
</div>
<!--usersがロードされたら各ユーザーの名前を表示する-->
<div v-for="user in users" :key="user.id">
<h2>{{ user.name }}</h2>
</div>
</div>
</script>
<!-- いつくかのテンプレート定義が続く-->
<script>
// コンポーネントとルート定義からVueインスタンスの生成など
// ここにコードを書いていく
// ログイン・ログアウトの実装
var Auth = {
login: function (email, pass, cb) {
// ダミーデータを使った擬似ログイン
setTimeout(function () {
if (email === 'vue@example.com' && pass === 'vue') {
// ログイン成功時はローカルストレージにtokenを保存する
localStorage.token = Math.random().toString(36).substring(7)
if (cb) { cb(true) }
} else {
if (cb) { cb(false) }
}
}, 0)
},
logout: function () {
delete localStorage.token
},
loggedIn: function () {
// ローカルストレージにtokenがあればログイン状態とみなす
return !!localStorage.token
}
}
// ダミーデータの定義。本来はデータベースの情報をAPI経由で取得する
var userData = [
{
id: 1,
name: 'Takuya Tejima',
description: '東南アジアで働くエンジニアです。'
},
{
id: 2,
name: 'Yohei Noda',
description: 'アウトドア・フットサルが趣味のエンジニアです。'
}
]
// 擬似的にAPI経由で情報を取得したようにする
var getUsers = function (callback) {
setTimeout(function () {
callback(null, userData)
}, 1000)
}
var getUser = function (userId, callback) {
setTimeout(function () {
var filteredUsers = userData.filter(function (user) {
return user.id === parseInt(userId, 10)
})
callback(null, filteredUsers && filteredUsers[0])
}, 1000)
}
// 擬似的にAPI経由で情報を更新したようにする
// 実際のWebアプリケーションではServerへPOSTリクエストを行う。
var postUser = function (params, callback) {
setTimeout(function () {
// idは追加されるごとに自動的にincrementされていく
params.id = userData.length + 1
userData.push(params)
callback(null, params)
}, 1000)
}
// ログインコンポーネント
var Login = {
template: '#login',
data: function () {
return {
email: 'vue@example.com',
pass: '',
error: false
}
},
methods: {
login: function () {
Auth.login(this.email, this.pass, (function (loggedIn) {
if (!loggedIn) {
this.error = true
} else {
// redirectパラメーターが付いている場合はそのパスに遷移
this.$router.replace(this.$route.query.redirect || '/')
}
}).bind(this))
}
}
}
// リストページの実装
// ユーザーリストコンポーネント
var UserList = {
template: '#user-list',
data: function () {
return {
loading: false,
users: function () {
return []
},
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
getUsers((function (err, users) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.users = users
}
}).bind(this))
}
}
}
// 詳細ページのコンポーネント
var UserDetail = {
template: '#user-detail',
data: function () {
return {
loading: false,
user: null,
error: null
}
},
created: function () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData: function () {
this.loading = true
// this.$route.params.userId に現在のURL上のパラメーターに対応したuserIdが格納される
getUser(this.$route.params.userId, (function (err, user) {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.user = user
}
}).bind(this))
}
}
}
// 新規ユーザー作成コンポーネント
var UserCreate = {
template: '#user-create',
data: function () {
return {
sending: false,
user: this.defaultUser(),
error: null
}
},
created: function () {
},
methods: {
defaultUser: function () {
return {
name: '',
description: ''
}
},
createUser: function () {
// 入力パラメーターのバリデーション
if (this.user.name.trim() === '') {
this.error = 'Nameは必須です'
return
}
if (this.user.description.trim() === '') {
this.error = 'Descriptionは必須です'
return
}
postUser(this.user, (function (err, user) {
this.sending = false
if (err) {
this.error = err.toString()
} else {
this.error = null
// デフォルトでフォームをリセット
this.user = this.defaultUser()
alert('新規ユーザーが登録されました')
// ユーザー一覧ページに戻る
this.$router.push('/users')
}
}).bind(this))
}
}
}
var router = new VueRouter({
// 各ルートにコンポーネントをマッピング
// コンポーネントはVue.extend() によって作られたコンポーネントコンストラクタでも
// コンポーネントオプションのオブジェクトでも渡せる
routes: [
{
path: '/top',
component: {
template: '<div>トップページです。</div>'
}
},
{
path: '/users',
component: UserList
},
{
path: '/users/new',
component: UserCreate,
beforeEnter: function (to, from, next) {
// 認証されていない状態でアクセスした時はloginページに遷移する
if (!Auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
// 認証済みであればそのまま新規ユーザー作成ページへ進む
next()
}
}
},
{
// /users/newの前にこのルートを定義するとパターンマッチにより/users/newが動作しなくなるので注意
path: '/users/:userId',
component: UserDetail
},
{
path: '/login',
component: Login
},
{
path: '/logout',
beforeEnter: function (to, from, next) {
Auth.logout()
next('/top')
}
},
{
// 定義されていないパスへの対応。トップページへリダイレクトする。
path: '*',
redirect: '/top'
}
]
})
// ルーターのインスタンスをrootとなるVueインスタンスに渡す
var app = new Vue({
data: {
Auth: Auth
},
router: router
}).$mount('#app')
</script>
</body>
</html>
ここで言うところの
var Authの位置が重要で、次にダミーデータuserDataにしなきゃなのか?
その辺りの入れ替えで動くようにはなった。
まぁ手が覚えるがまず重要なんだけど、どこが問題なのかがまだまだわからない。