26
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MonacaAdvent Calendar 2017

Day 5

Monaca + Onsen UI for Vueを触ってTodoアプリを作ってみた

Posted at

今までは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 のミニマムテンプレートをベースに作ってみます。

001.png

プロジェクトを作成して monaca preview でプレビューしてみるとこんな画面が表示されます。

ミニマムテンプレートの中身を覗いてみる

002.png

何やらたくさんファイルが入ってます。www フォルダがファイルのコンパイル先で最終的にアプリになる部分です。
src フォルダにコンパイル前のファイルが入っているので今回触るのはこっちです。

main.js
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 }
});
App.vue
<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で繰り返し処理します。

app.vue
<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>
003.png

表示されました!
ほぼ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したら先程作った配列に追加されるようにしました。
それだけで画面に反映してくれるので便利ですね。

004.png

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 の記載がないと、配列の要素が増減した時は反応してくれるのですが、配列の中身の値が変わった時に反応してくれません。少しハマりました。

005.png

これで、タスクを追加した時や完了した時に、ローカルストレージにデータを保存してくれるようになりました。

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>
006.png

CSSが適用されました。

コードまとめ

App.vue
<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は学習コストが低めで使いやすいと思ったので、またアプリを作ってみようと思います。

26
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
22

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?