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
にしなきゃなのか?
その辺りの入れ替えで動くようにはなった。
まぁ手が覚えるがまず重要なんだけど、どこが問題なのかがまだまだわからない。