こんにちわ
グローバルセンス株式会社のskanehiraです。
久しぶりの投稿になってしまいましたが、
以前こちらの記事で紹介した構成を使って、簡単なTodoアプリを作ってみようと思います。
フロントだけで完結できるものなので、結局axiosを使用していないが、
それについては別記事に書く予定です。
出来上がったモノ
こちらに上げておきますた。
表示・追加・更新・削除は一通り出来ます。
エラー処理はしていないので、変な動きするかもですがサンプルなのでご容赦下さい。
今回はサンプルなのでDB使わず、
データはブラウザ側のLocalStorageに保存するようにしています。
vueのコンポーネントからLocalStorageの操作をしたいので、
vue-localstorageを使用しています。
全体の構成はこんな感じになっています。
├── app
│ └── src
│ ├── components
│ │ ├── addTodo
│ │ │ ├── addTodo.html
│ │ │ └── addTodo.vue
│ │ ├── editTodo
│ │ │ ├── editTodo.html
│ │ │ └── editTodo.vue
│ │ ├── header
│ │ │ ├── header.html
│ │ │ └── header.vue
│ │ ├── todoList
│ │ │ ├── todoList.html
│ │ │ └── todoList.vue
│ │ └── top
│ │ └── top.vue
│ └── js
│ ├── app.js
│ └── routes.js
├── package-lock.json
├── package.json
├── public
│ ├── index.html
│ └── js
│ └── bundle.js
└── webpack.config.js
使い方
シンプルなので説明するまでもないが一応…
EditはTodoの更新ができます。
追加画面とほぼ同じです。
構成について
今回作成したコンポーネントは以下の5つになります。
-
addTodo
Todoの追加を行う画面
一応簡単なvalidationも軽く実装 -
editTodo
Todoの編集画面
追加画面と基本的に同じ
-
header
ヘッダと書いてありながら実質メニュー -
todoList
追加したTodoの一覧
更新・詳細表示・削除はこの画面から行う
-
top
一番最初に表示される画面
無くてもいいが、とりあえず作った感じ
そして、こちらのファイル達は、重要な役割を担っています。
-
app/src/js/routes.js
v-routerのルーティング処理を実装
どのpathでどのコンポーネントを呼び出すかをここで定義
-
app/src/js/app.js
各種コンポーネント、css、ライブラリを読み込み、vueインスタンスを生成する処理を実装
初期化処理などはここにおく -
public/js/bundle.js
ビルド済みのjsファイル
こいつがあれば問題なし -
public/index.html
ビルド済みのbundle.jsを読み込み
あとはvue-routerでコンポーネントの描写に必要なディレクティブを定義するくらい
あんまり、やることがない…
ざっくり説明するとこんな感じになります。
これ以上分かりやすい説明が思いつかないので、実際ソースを見ながら説明します。
とりあえずindex.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>sample Todo app</title>
</head>
<body>
<main id="app">
<headers></headers>
<router-view></router-view>
</main>
<script src="./js/bundle.js"></script>
</body>
</html>
スリムところかやせ細って骨くらいしか残っていないですね。
それが良いですが。
mainタグにidを付けています。
これはvueインスタンスを生成する時にどのタグにbindするかを指定する必要があるためです。
詳しくはvueの公式を見て下さい。
<headers>
はヘッダ(メニュー)の部分になります。
※headerというディレクティブがすでに存在するから複数形にせざるを得ない…
<router-view>
はvue-routerを使うと時のお作法なので、気にせず覚えれば良いと思います。
これがないと各画面が描写されないのでご注意を。
詳しくは(ry
<headers>
と<router-view>
を別々に分けたのはヘッダを固定するためです。
メニューを各画面に実装すると非効率なので、別コンポーネントに切り出しました。
app.js
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import locale from 'element-ui/lib/locale/lang/en';
import VueRouter from 'vue-router';
import VueLocalStorage from 'vue-localstorage';
import header from '../components/header/header.vue';
import routes from './routes';
Vue.use(VueRouter);
Vue.use(ElementUI, { locale });
Vue.use(VueLocalStorage);
const router = new VueRouter({
routes: routes
});
const app = new Vue({
el: '#app',
components: {
'headers': header,
},
router,
});
vue-router、ElementUI、vue-localstorageを各コンポーネントで使用したいので、
Vue.useでそれらをVueインスタンスに追加しています。
あとは基本的にライブラリのお作法の処理なので割愛します。
routes.js
import TodoList from '../components/todoList/todoList.vue';
import AddTodo from '../components/addTodo/addTodo.vue';
import EditTodo from '../components/editTodo/editTodo.vue';
import Top from '../components/top/top.vue';
export default [
{ path: "/", component: Top },
{ path: "/TodoList", component: TodoList },
{ path: "/AddTodo", component: AddTodo },
{ path: "/EditTodo/:index", component: EditTodo },
]
こちらもシンプルで、各コンポーネントをimportして、
ルーティング情報を定義しているだけです。
画面を増やす時はこちらの定義も追加しないと画面遷移しないがよく忘れます。。
Todo一覧
大体の流れが分かるtodo一覧画面に焦点を絞って説明します。
他の画面は、リポジトリを見て頂ければと思います。
<div>
<el-card shadow="never" style="margin-right: 30%; margin-left: 30%">
<template>
<el-table :data="todoList" style="width: 100%">
<el-table-column prop="title" label="Todo" width="120px"></el-table-column>
<el-table-column label="Operations" fixed="right" align="center">
<template slot-scope="scope">
<el-button size="mini" @click="handleDetail(scope.row)">Detail</el-button>
<el-button size="mini" @click="handleEdit(scope.$index)">Edit</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.$index)">Delete</el-button>
</template>
</el-table-column>
</el-table>
</template>
</el-card>
<el-dialog :title="todo.title" :visible.sync="centerDialogVisible" width="30%" center>
<pre>{{todo.detail}}</pre>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="centerDialogVisible = false">OK</el-button>
</span>
</el-dialog>
</div>
ElementUIをガッツリ使用しています。
ポイントの部分をかい摘んで行きます。
<el-table>
:data
でtodoの配列を指定すれば、
あとはライブラリ側でうまい具合にテーブルを作ってくれます。<el-table-column>
prop
は各オブジェクトのプロパティを抽出して表示します。<template slot-scope="scope">
各オブジェクトのデータを個別に扱いたいときはscopeを使用します。
scope.row
がtodoオブジェクトです。<el-button>
@click
はボタンを押下した時に呼び出すメソッドを指定します。
scope.rowやらscope.$indedを引数として渡しています。
@click
についてvueの公式サイトを見て下さい。<el-dialog>
Detailを押下した時に表示するダイアログです。
:visible.sync
で指定している変数がtrueなら表示、falseなら非表示という仕組みです。
<template src="./todoList.html"></template>
<script>
export default {
data() {
return {
todo: {
title: "",
detail: "",
},
todoList: [],
centerDialogVisible: false,
}
},
created() {
this.todoList = this.getTodoListFromStorage();
},
methods: {
getTodoListFromStorage() {
let data = this.$localStorage.get("todoList");
// nullの場合空配列を返却
if (!data) {
return [];
}
// jsonを返却
return JSON.parse(data);
},
setTodoListToStorage(data) {
this.$localStorage.set("todoList", JSON.stringify(data));
},
handleDetail(data) {
this.todo = data;
this.centerDialogVisible = true;
},
handleEdit(targetIndex) {
this.$router.push("/EditTodo/" + targetIndex);
},
handleDelete(targetIndex) {
// callbackから直接thisにアクセス出来ないため、ローカル変数経由でアクセスする
let todoList = this.todoList;
// 配列から対象レコードを除去
todoList.some(function(currentData, index) {
if (index == targetIndex) todoList.splice(index, 1);
});
// ストレージ更新
this.setTodoListToStorage(this.todoList);
}
}
}
</script>
<template>
相棒のhtmlを指定しています。
vueはあくまでもロジックを実装する場所なので、htmlを切り出しました。data()
画面で使用するデータをreturnしています。
returnまではお作法なのです。getTodoListFromStorage()
LocalStorageからデータを取得しています。
LocalStorageに保存できるデータは文字列なので、
保存時はオブジェクトから文字列に変換、
取得時はJSONにパースする必要があります。handleDetail()
Detailボタンを押下時に呼ばれるメソッド。
単純に表示するデータをセットして、フラグをtrueにするだけです。handleEdit()
Todo編集画面に遷移するだけです。
app.jsでVue.use(VueRouter)
をしたのはthis.$route
を使用できるようにするためです。handleDelete()
配列から対象データを削除してLocalStorageを更新しているだけです。
1点注意なのが、someで指定しているcallbackからはthisにアクセスできないので、
一度ローカル変数を挟む必要が有るようです。
知らなかった…
最後に
ソースを見つつ、かい摘んで説明しましたが、あんまりぴんと来ない方もいるかと思います。
なので自分で実装してみるのが一番良いと思います。
実装時にぼくが見ていたものはこちらになりますので、
もし改造してみたいとか、作ってみたいとかがあれば参考になればと思います。
雑感
簡単なTodoアプリでも意外と時間がかかりましたが、
もっと短時間で作れるように精進します。。
vue.jsは本当に便利なので、これからもメインで使っていきたいと思っています。
UIコンポーネントは…ちょっとこちらが気になっています。
いずれ使ってみようと思います。
次回の記事は、今作っているサービスについて何か書けたらと思います。