40
40

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 1 year has passed since last update.

Onsen UIAdvent Calendar 2016

Day 13

Onsen UI+Vue.js を使ってみた

Last updated at Posted at 2016-06-26

はじめに

HTML+JavaScript でアプリ開発するにあたって、UI ライブラリをどうするか悩みました。モバイルアプリは Onsen UI がよさそうです。
Onsen UI は、当初 AngularJS をベースにしたフレームワークでしたが、2 になって別のフレームワークと一緒に使うのが容易になりました。
以前に Backbone.js+Marionette.js と一緒に使ってみましたが、簡単なアプリでも記述量が少なくありません。そこで、Vue.js と一緒に使ってみたいと思います。

参考

Onsen UI+Vue.js を使ってみた

  • Onsen UI 2.0
  • Vue.js 2.0

Onsen UI を使う

以下のページからダウンロードします。

Onsen UI 2
(https://onsen.io/v2/)

以下のファイルをワークスペースにコピーします。

lib
    onsen
        css
            onsenui.css
            onsen-css-components.css
        js
            onsenui.js

使用します。

app.html
<link href="lib/onsen/css/onsenui.css" rel="stylesheet" />
<link href="lib/onsen/css/onsen-css-components.css" rel="stylesheet" />    
<script src="lib/onsen/js/onsenui.js" type="text/javascript"></script>

Onsen UI は独自タグを使います。

app.html
<ons-navigator id="main-navi" page="list-page">
</ons-navigator>

<ons-template id="list-page">
    <ons-page id="list-page">
        <ons-toolbar id="list-toolbar">
            <div class="center">Try Onsen+Vue.js</div>
        </ons-toolbar>
        <ons-list id="contact-list">
        </ons-list>
    </ons-page>
</ons-template>

ツールバーがついたリストビューが本体のページを準備します。

JavaScript のコードは ons.ready() 関数に記述します。

app.js
ons.ready(function(){
    ....
});

ドキュメント

Vue.js を使う

以下のページからダウンロードします。

Vue.js
(https://jp.vuejs.org/)

以下のファイルをワークスペースにコピーします。

lib
    vue.js

使用します。

app.html
<script src="lib/vue.js"></script>

データを準備する

まず、データを準備します。
Vue.js で扱うデータは通常の JavaScript オブジェクトです。ただし配列データを扱うときは、配列そのものでなく、配列データを含むオブジェクトを用意します。

app.js
var app = {};
app.data = {
    contacts: [
        { 
            firstName: "Alice", lastName: "Arten", hasPhone: true, phoneNumber: "555-0184" 
        },{ 
            firstName: "Bob", lastName: "Brigham", hasPhone: true, phoneNumber: "555-0163" 
        },{ 
            firstName: "Charlie", lastName: "Campbell", hasPhone: true, phoneNumber: "555-0129" 
        }
    ]
};

データをレンダリングする

<ons-list id="content-list"> にデータがリスト表示されるようテンプレートを追記します。
リストを表示するので v-for ディレクティブを使います。

app.html
    <ons-list id="contact-list">
        <template v-for='item in list'>
            <ons-list-item>
                <div class="center">{{ item.firstName }} {{ item.lastName }}</div>
            </ons-list-item>
        </template>        
    </ons-list>

Vue のインスタンスを生成します。

app.js
new Vue({
    el: '#contact-list',
    data: {
        list: app.data.contacts
    }
});

これをいつ実行すべきかですが、
el に指定する <ons-list id="contact-list"> が生成されてからでないといけません。
これが含まれる <ons-page id="list-page"> が生成されるタイミングで実行するにはこうします。↓

app.js
$(document).on('init', '#list-page', function(){
    console.log("#list-page.init");
    new Vue({
        el: '#contact-list',
        data: {
            list: app.data.contacts
        }
    });
});

※ el に指定するのに Vue.js 1.0 では <ons-page id="list-page"> でも問題なかったのですが、Vue.js 2.0 では問題あります。

この記述するために、jQuery を使います。

app.html
<script src="lib/jquery.js"></script>

ユーザ入力をハンドリングする

リストビューに「編集」と「削除」ボタンを追加します。
v-on ディレクティブで実行したいメソッドを指定します。

app.html
    <template v-for="item in list">
        <ons-list-item>
            中略
            <div class="right">
                <ons-button modifier="quiet" v-on:click='showDetail(item)'><ons-icon icon="pencil"></ons-icon></ons-button>
                <ons-button modifier="quiet" v-on:click='deleteItem(item)'><ons-icon icon="trash-o"></ons-icon></ons-button>
            </div>

実行するメソッドを設定します。

app.js
new Vue({
    el: '#contact-list',
    中略
    methods: {
        showDetail: function(item){
            console.log("showDetail:", JSON.stringify(item));
        },
        deleteItem: function(item){
            console.log("deleteItem:", JSON.stringify(item));
        },
    }
});

データを削除できるようにする

app.js
new Vue({
    中略
    methods: {
        deleteItem: function(item){
            中略
            var index = this.list.indexOf(item);
            this.list.splice(index, 1);
        }

データを操作しただけだが、画面も変わります。

※ Vue.js 1.0 では、配列から指定したデータを削除するのに、$remove() メソッドを使いましたが、Vue.js 2.0 ではこれがなくなりました。

詳細ページを表示する

app.html
<ons-template id="detail-page">
    <ons-page id="detail-page">
        <ons-toolbar>
            <div class="left"><ons-back-button>Back</ons-back-button></div>
            <div class="center">Detail</div>
        </ons-toolbar>
        <ons-list id="detail">
            <ons-list-item>
                <ons-input placeholder="First Name"></ons-input>
            </ons-list-item>
            <ons-list-item>
                <ons-input placeholder="Last Name"></ons-input>
            </ons-list-item>
        </ons-list>
    </ons-page>
</ons-template>
app.js
new Vue({
    中略
    methods: {
        showDetail: function(item){
            中略
            document.querySelector('#main-navi').pushPage('detail-page'); 
        },

$(document).on('init', '#detail-page', function(){
    console.log("#detail-page.init");
});

Onsen UI の機能でページ遷移します。

詳細ページに内容を表示する

app.js
new Vue({
    中略
    methods: {
        showDetail: function(item){
            中略
            app.selected = item;
            document.querySelector('#main-navi').pushPage('detail-page'); 
        },

app オブジェクトに selected を追加して別のページに渡せるようにします↑

app.html
<ons-template id="detail-page">
    中略
        <ons-list id="detail">
            <ons-list-item>
                <ons-input v-model='firstName' placeholder="First Name"></ons-input>
            </ons-list-item>
            <ons-list-item>
                <ons-input v-model='lastName' placeholder="Last Name"></ons-input>
            </ons-list-item>

データバインディングのため v-model ディレクティブを追記します。↑

app.js
new Vue({
    el: '#detail',
    data: app.selected,
});

新たに Vue のインスタンスを生成します。data プロパティに app.selected をセットします。↑
これを実行するのもページ初期化のタイミングです。

app.js
$(document).on('init', '#detail-page', function(){
    中略
    new Vue({
        el: '#detail',
        data: app.selected,
    });
});

※ el に指定するのに Vue.js 1.0 では <ons-page id="detail-page"> でも問題なかったのですが、Vue.js 2.0 では問題あります。

これで app.selected の内容が画面に表示される・・はずですが、
そうなりません。
v-model<input> でないと使えないようです。
なので <ons-input><input> に書換します。

app.html
<ons-template id="detail-page">
    中略
        <ons-list id="detail">
            <ons-list-item>
                <input v-model='firstName' class="text-input" placeholder="First Name">
            </ons-list-item>
            <ons-list-item>
                <input v-model='lastName' class="text-input" placeholder="Last Name">
            </ons-list-item>

双方向データバインディングする

app.selected の内容が画面に表示されるだけでなく、
画面の要素の内容で app.selected の内容が更新されるようになります。

app.selected はポインタです。値がコピーされているわけではありません。
詳細画面の要素の内容を変えると、app.data の内容も変わっています。

さらに、リストビューの画面の内容も変わっています。

Vue.js のおかげで、双方向データバインディングが簡単にできます。

カスタムディレクティブを使う

v-model<input> でないと使えないので <ons-input><input> に書換しましたが、不本意です。
Vue.js のカスタムディレクティブを使えばいいのではないかと、試してみました。

app.js
Vue.directive('model-ex', {
    bind: function(el, binding, vnode){
        this.handler = function(){
            Vue.set(vnode.context, binding.expression, el.value);
        }.bind(this);
        el.addEventListener('input', this.handler);
        el.value = binding.value;
    },
    unbind: function(el){
        el.removeEventListener('input', this.handler);
    },
    update: function(el, binding){
        el.value = bindig.value;
    }
});

※ Vue.js 1.0 から 2.0 になったため書換しました。

app.html
<ons-template id="detail-page">
      中略
            <ons-list-item>
                <ons-input v-model-ex='firstName' placeholder="First Name"></ons-input>
            </ons-list-item>
            <ons-list-item>
                <ons-input v-model-ex='lastName' placeholder="Last Name"></ons-input>
            </ons-list-item>

データを追加できるようにする

ツールバーに「+」ボタンを追加して、データを追加できるようにします。

app.html
<ons-template id="list-page">
    <ons-page id="list-page">
        <ons-toolbar id="list-toolbar">
            中略
            <div class="right">
                <ons-toolbar-button v-on:click='addItem'><ons-icon icon="fa-plus"></ons-icon></ons-toolbar-button>
            </div>
app.js
new Vue({
    el: '#list-toolbar',
    methods: {
        addItem: function(){
            console.log("addItem:");
            var contact = {
                firstName: "", lastName: "", hasPhone: false, phoneNumber: "" 
            }; 
            app.data.contacts.push(contact);
            app.selected = contact;
            document.querySelector('#main-navi').pushPage('detail-page'); 
        }

※ el に指定するのに Vue.js 1.0 では <ons-page id="list-page"> でも問題なかったのですが、Vue.js 2.0 では問題あります。

データが保存されるようにする

変更したり追加したりしたデータが、実行し直すとなくなってしまいます。
localStorage を使って保存できるようにします。

起動時に読込します↓

app.js
    app.data = JSON.parse(localStorage.getItem('data')) || app.data;

いつ保存すればいいでしょうか。

app.js
new Vue({
    el: '#list-page',
    中略
    methods: {
        deleteItem: function(item){
            中略
            localStorage.setItem('data', JSON.stringify(app.data));
        },

データを削除したとき↑

app.js
$(document).on('destroy', '#detail-page', function(){
    console.log("#detail-page.destroy");
    localStorage.setItem('data', JSON.stringify(app.data));
})

詳細画面から一覧画面に戻るとき↑

40
40
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
40
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?