【Vue.js入門】Vue Routerを活用したアプリケーション開発

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

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

  • 作者: 川口和也,喜多啓介,野田陽平,手島拓也,片山真也
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/09/22
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

これを毎日コツコツと進めておりますが、現在

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

に入りました。

webukatu.com

ルーティングの基礎から

細かく別れて書かれてます。

4.4 サンプルアプリケーションの実装

辺りから

  1. リストページ
  2. リストページの改修
  3. 詳細ページ
  4. ユーザー登録ページ
  5. ログインページとログイン状態

ってなるのだけど、それをちょっとずつちょっとずつ「追加」していくわけなんだけど、「追加」は上になるのか?下になるのか?ってことがいまいちわからず。

[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にしなきゃなのか?

その辺りの入れ替えで動くようにはなった。

まぁ手が覚えるがまず重要なんだけど、どこが問題なのかがまだまだわからない。

webukatu.com