今まではAngularの1.4とOnsen UIでMonacaアプリを作っていたのですが、新しくMonacaアプリを作る際のフレームワークとしてOnsen UI for Vueを使ってみたかったので、試しに簡単なTODOアプリを作ってみました。
Monaca CLIでプロジェクト作成
Onsen UI for Vueはコードのコンパイルが必要なのでクラウドIDEでは作成できません。
コンパイルが必要なMonacaの新規プロジェクトの作成には、MonacaCLIが便利です!
MonacaCLIの詳しい紹介は省きますが、今回は Onsen UI and Vue.js
のミニマムテンプレートをベースに作ってみます。
プロジェクトを作成して monaca preview
でプレビューしてみるとこんな画面が表示されます。
ミニマムテンプレートの中身を覗いてみる
何やらたくさんファイルが入ってます。www
フォルダがファイルのコンパイル先で最終的にアプリになる部分です。
src
フォルダにコンパイル前のファイルが入っているので今回触るのはこっちです。
import 'onsenui';
import Vue from 'vue';
import VueOnsen from 'vue-onsenui';
// Onsen UI Styling and Icons
require('onsenui/css-components-src/src/onsen-css-components.css');
require('onsenui/css/onsenui.css');
import App from './App.vue';
Vue.use(VueOnsen);
new Vue({
el: '#app',
template:'<app></app>',
components: { App }
});
<template>
<v-ons-page>
<v-ons-toolbar>
<div class="center">{{ title }}</div>
<div class="right">
<v-ons-toolbar-button>
<v-ons-icon icon="ion-navicon, material: md-menu"></v-ons-icon>
</v-ons-toolbar-button>
</div>
</v-ons-toolbar>
<div style="text-align: center; padding-top:10px">Hello World!</div>
<p style="text-align: center">
<v-ons-button @click="alert">Click Me!</v-ons-button>
</p>
</v-ons-page>
</template>
<script>
export default{
data() {
return {
title: 'My app'
};
},
methods: {
alert() {
this.$ons.notification.alert('This is an Onsen UI alert notification test.');
}
}
};
</script>
main.jsの中でOnsen UIのモジュールのインポートやVueインスタンスの作成を行っていて、HTMLのテンプレート部分などはインポートしているApp.vueに書かれているので、今回はこちらのApp.vueファイルをさわってTodoアプリを作ってみようと思います。
今回はかなり簡易的なものなので、ルーティングや細かいコンポーネントごとにファイルを分けるなどはしないです。
Todoリストの繰り返しを作成
Onsen UIのドキュメントを参考にしながら、<template>
の中をこんな感じにしてみました。
dataには todos
という配列を作って、v-for
で繰り返し処理します。
<template>
<v-ons-page>
<v-ons-toolbar>
<div class="center">{{ title }}</div>
</v-ons-toolbar>
<div class="listTodos">
<v-ons-list-item v-for="(todo, $index) in todos" :class="{ isFinished: todo.finished }">
<label class="left">
<v-ons-checkbox
:input-id="'checkbox-' + $index"
:value="todo.text"
v-model="todo.finished"
>
</v-ons-checkbox>
</label>
<label class="center" :for="'checkbox-' + $index">
{{ todo.text }}
</label>
</v-ons-list-item>
</div>
</v-ons-page>
</template>
<script>
export default{
data() {
return {
title: 'Todo Sample',
todos: [
{
'text': 'タスク名',
'finished': false
},
{
'text': '完了したタスク',
'finished': true
}
]
};
},
};
</script>
表示されました!
ほぼHTMLで、OnsenUIのタグも直感的に書けて良い感じです。
タスクを追加するフォームを作成
フォームを作って、新規タスクを追加できるようにします。
<div class="formNewTodo">
<v-ons-input
type="text"
placeholder="新しいタスクを追加"
v-model="newTodo"
>
</v-ons-input>
<v-ons-button type="submit" @click="addNewTodo()">追加</v-ons-button>
</div>
newTodo
というデータを作って、 入力エリアにバインディングします。
data() {
return {
// 省略
newTodo: '',
};
},
methods: {
addNewTodo () {
// 入力されてない時は何もしない
if (this.newTodo === '') return;
var todo = {
'text': this.newTodo,
'finished': false
}
this.todos.push(todo);
this.newTodo = '';
},
},
submitしたら先程作った配列に追加されるようにしました。
それだけで画面に反映してくれるので便利ですね。
CSSは後で調整します。
データをローカルストレージに保存する
これだけではデータの保存ができないので、ローカルストレージにデータを保存するようにします。
var STORAGE_KEY = 'vue-todo';
export default{
data() {
return {
title: 'Todo Sample',
todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'),
newTodo: '',
};
},
methods: {
addNewTodo () {
// 省略
},
},
watch: {
todos: {
handler: function (todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
},
deep: true
}
}
};
todos
をウォッチして、変更があるたびにローカルストレージに保存してくれるようにしました。
deep: true
の記載がないと、配列の要素が増減した時は反応してくれるのですが、配列の中身の値が変わった時に反応してくれません。少しハマりました。
これで、タスクを追加した時や完了した時に、ローカルストレージにデータを保存してくれるようになりました。
CSSで見た目を調整する
同じApp.vueの中に <style>
ブロックを作成してCSSを書きます。
scoped
の属性をつけると、このコンポーネントの中だけで適用されるスタイルになってくれるので管理しやすくなります。
<style scoped>
.formNewTodo {
padding: 14px;
display: flex;
justify-content: space-between;
}
ons-input {
flex: 1;
border-bottom: 1px solid #ddd;
margin-right: 14px;
}
.list-item {
padding-right: 14px;
}
.list-item.isFinished {
color: #ccc;
text-decoration: line-through;
}
</style>
CSSが適用されました。
コードまとめ
<template>
<v-ons-page>
<v-ons-toolbar>
<div class="center">{{ title }}</div>
</v-ons-toolbar>
<div class="formNewTodo">
<v-ons-input
type="text"
placeholder="新しいタスクを追加" float
v-model="newTodo"
>
</v-ons-input>
<v-ons-button type="submit" @click="addNewTodo()">追加</v-ons-button>
</div>
<div class="listTodos">
<v-ons-list-item v-for="(todo, $index) in todos" :class="{ isFinished: todo.finished }">
<label class="left">
<v-ons-checkbox
:input-id="'checkbox-' + $index"
:value="todo.text"
v-model="todo.finished"
>
</v-ons-checkbox>
</label>
<label class="center" :for="'checkbox-' + $index">
{{ todo.text }}
</label>
</v-ons-list-item>
</div>
</v-ons-page>
</template>
<script>
var STORAGE_KEY = 'vue-todo';
export default{
data() {
return {
title: 'Todo Sample',
todos: JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'),
newTodo: '',
};
},
methods: {
addNewTodo () {
// 入力されてない時は何もしない
if (this.newTodo === '') return;
var todo = {
'text': this.newTodo,
'finished': false
}
this.todos.push(todo);
this.newTodo = '';
},
},
watch: {
todos: {
handler: function (todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
},
deep: true
}
}
};
</script>
<style scoped>
.formNewTodo {
padding: 14px;
display: flex;
justify-content: space-between;
}
ons-input {
flex: 1;
border-bottom: 1px solid #ddd;
margin-right: 14px;
}
.list-item {
padding-right: 14px;
}
.list-item.isFinished {
color: #ccc;
text-decoration: line-through;
}
</style>
まとめ・所感
- テンプレート構文で、ほぼHTMLの中に属性やタグを書く形で使えるので学習コストが低い。OnsenUIのタグも直感的に使える。
- フォームの双方向バインディング便利。 テンプレート構文は、Angularの1系に 近い感じがするので慣れてる人は 使いやすそう。
- .Vueファイルの中に、HTML/CSS/JSが、 まとめて書けるのがわかりやすい。
Todoの削除や更新ができない簡易的なアプリですが、比較的簡単に作成することが出来ました。
ビルドまではしてませんが、MonacaCLIでコンパイルして、そのままビルドすればアプリとして使えるようになるはずです(未確認ですみません)
Vue.jsは学習コストが低めで使いやすいと思ったので、またアプリを作ってみようと思います。