【学習メモ】Vue.js入門 基礎から実践アプリケーション開発まで その3
4 Vue Router を活用したアプリケーション開発
4.2 ルーティングの基礎
4.2.2 ルーティング設計
ルート と ルーターコンストラクタ を用います。
<div id="app">
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
<script src="./assets/js/main.js"></script>
// ルーターコンストラクタ
// ルートオプションを渡してルーターインスタンスを生成
var router = new VueRouter({
// ルート定義
routes: [
{
path: "/top",
component: {
template: `<div>トップページです。</div>`
}
},
{
path: "/users",
component: {
template: `<div>ユーザー一覧ページです。</div>`
}
}
]
});
// Vue のマウント
// ルーターのインスタンスをrootとなるVueインスタンスに渡す
var app = new Vue({
router: router,
el: "#app"
});
4.4 サンプルアプリケーションの実装
<div id="app">
<nav v-cloak>
<!-- `router-link` によるナビゲーション定義 -->
<router-link to="/top">トップページ</router-link>
<router-link to="/users">ユーザー一覧ページ</router-link>
<router-link to="/users/new?redirect=ture">新規ユーザー登録</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>
<script src="https://unpkg.com/vue@2.5.17"></script>
<script src="https://unpkg.com/vue-router@3.0.1"></script>
<!-- ユーザー一覧ページのテンプレート -->
<script type="text/x-template" id="user-list">
<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 type="text/x-template" id="user-detail">
<div>
<div class="loading" v-if="loading">読み込み中</div>
<div v-if="error" class="error">
{{ error }}
</div>
<!-- users がロードされたら各ユーザの名前を表示する -->
<div v-if="user">
<h2>{{ user.name }}</h2>
<p>{{ user.description }}</p>
</div>
</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="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" type="password"></label>
<br>
<button type="submit">ログイン</button>
<p v-if="error" class="error">ログインに失敗しました</p>
</form>
</div>
</script>
<script src="./assets/js/main.js"></script>
//////////////////////////
// ユーザー一覧
//////////////////////////
var getUsers = function(callback) {
setTimeout(function() {
callback(null, [
{
id: 1,
name: "Takuya Tejima"
},
{
id: 2,
name: "Yohei Noda"
}
]);
}, 1000);
};
var UserList = {
template: "#user-list",
data: function() {
return {
loading: false,
users: function() {
return []; // 初期値の空配列
},
error: null
};
},
// 初期化時にデータを取得する
created: function() {
this.fetchData();
},
// $route の変更をwatchすることでルーティングが変更されたときに再度データを取得
watch: {
$route: "fetchData"
},
methods: {
fetchData: function() {
this.loading = true;
// 取得したデータの結果をusersに格納する
getUsers(
function(err, users) {
this.loading = false;
if (err) {
this.error = err.toString();
} else {
this.users = users;
}
}.bind(this)
);
}
}
};
//////////////////////////
// ユーザー詳細
//////////////////////////
var userData = [
{
id: 1,
name: "Takuya Tejima",
description: "東南アジアで働くエンジニアです。"
},
{
id: 2,
name: "Yohei Noda",
description: "アウトドア・フットサルが趣味のエンジニアです。"
}
];
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);
};
var UserDetail = {
template: "#user-detail",
data: function() {
return {
loading: false,
user: null,
error: null
};
},
// 初期化時にデータを取得する
created: function() {
this.fetchData();
},
// $route の変更をwatchすることでルーティングが変更されたときに再度データを取得
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)
);
}
}
};
//////////////////////////
// ユーザー作成
//////////////////////////
// 擬似的にAPI経由で情報を更新したようにする
// 実際のWebアプリケーションではサーバーへへPOSTリクエストを行う
var postUser = function(params, callback) {
setTimeout(function() {
params.id = userData.length + 1;
userData.push(params);
callback(null, params);
}, 1000);
};
// 新規ユーザー作成コンポーネント
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 = null;
} else {
// デフォルトでフォームをリセット
this.user = this.defaultUser();
alert("新規ユーザーが登録されました");
// ユーザー一覧ページに戻る
this.$router.push("/users");
}
}.bind(this)
);
}
}
};
//////////////////////////
// ログイン・ログアウト
//////////////////////////
var Auth = {
login: function(email, pass, cb) {
// ダミーデータを使った疑似ログイン
setTimeout(function() {
if (email === "vue@example.com" && pass === "vue") {
// ログイン成功時はローカルストレージに taken を保存する
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;
}
};
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 router = new VueRouter({
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();
}
}
},
{
path: "/users/:userId",
component: UserDetail
},
{
path: "/login",
component: Login
},
{
path: "/logout",
beforeEnter: function(to, from, next) {
Auth.logout();
next("/top");
}
},
{
// 定義されていないパスへの対応。トップページへリダイレクトする。
path: "*",
redirect: "/top"
}
]
});
//////////////////////////
// Vue のマウント
//////////////////////////
var app = new Vue({
data: {
Auth: Auth
},
router: router,
el: "#app"
});