4
5

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 UI+Backbone.js+Marionette.js を使ってみた

Last updated at Posted at 2016-06-18

はじめに

HTML+JavaScript でアプリ開発するにあたって、UI ライブラリをどうするか悩みました。モバイルアプリは Onsen UI がよさそうです。
Onsen UI は、当初 AngularJS をベースにしたフレームワークでしたが、2 になって別のフレームワークと一緒に使うのが容易になりました。
個人的に AngularJS より、Backbone.js+Marionette.js の方が好みです。Onsen UI も Backbone.js+Marionette.js と一緒に使ってみたいと思います。

Onsen UI+Backbone.js+Marionette.js を使ってみた

  • Onsen UI 2.0
  • Backbone.js 1.1.2
  • Marionette.js 2.4.1
  • backbone.stikit 0.8.0
  • backbone.localStorage 1.1.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 var="naviMain" page="list-page">
</ons-navigator>

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

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

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

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

ドキュメント

Backbone.js を使う

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

Backbone.js
(http://backbonejs.org/)

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

lib
    jquery.js
    json2.js
    underscore.js
    backbone.js

使用します。

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

Backbone.Model と Backbone.Collection

以上を参考にしてデータを準備します。

app.js
var app = {};
app.Contact = Backbone.Model.extend({
    defaults: {
        firstName: "",
        lastName: "",
        phoneNumber: ""
    }
});

app.ContactList = Backbone.Collection.extend({
    model: app.Contact,
});
app.contacts = new app.ContactList([
    { 
        firstName: "Alice", lastName: "Arten", phoneNumber: "555-0184" 
    },{ 
        firstName: "Bob", lastName: "Brigham", phoneNumber: "555-0163" 
    },{ 
        firstName: "Charlie", lastName: "Campbell", phoneNumber: "555-0129" 
    }
]);

Backbone.View

これはわかりづらいので Marionette.js を使います。

Marionette.js を使う

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

Marionette.js
(http://marionettejs.com/)

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

lib
    ↑以上が Backbone.js に必須
    backbone.marionette.js

使用します。

app.html
↑以上が Backbone.js に必須
<script src="lib/backbone.marionette.js"></script>

Marionette.ItemView と Marionette.CollectionView

以上を参考にして

app.html
<script type="text/template" id="contact-item-template">
    <ons-list-item class="contact-item">
        <div class="center"><%= firstName %> <%= lastName %></div>
    </ons-list-item>
</script>        
app.js
app.ContactItemView = Marionette.ItemView.extend({
    template: '#contact-item-template',
});
app.ContactListView = Marionette.CompositeView.extend({
    el: '#list-page',
    template: '#list-page',
    childViewContainer: '#contact-list',
    childView: app.ContactItemView,
});

app.listView = new app.ContactListView({
    collection: app.contacts
});
app.listView.render();

Marionette.View のイベント

リストビューに「編集」と「削除」ボタンを追加します。

app.html
<script type="text/template" id="contact-item-template">
    <ons-list-item class="contact-item">
        中略
        <div class="center">
            <ons-button modifier="quiet" class="js-detail"><ons-icon icon="pencil"></ons-icon></ons-button>
            <ons-button modifier="quiet" class="js-delete"><ons-icon icon="trash-o"></ons-icon></ons-button>
        </div>

それぞれイベントハンドラを設定します。

app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    events: {
        'click .js-delete': 'onDeleteClick',
        'click .js-detail': 'onDetailClick'
    },
    onDeleteClick: function(e){
        e.preventDefault();
        e.stopPropagation();
        console.log("ContactItemView.onDeleteClick");
    },
    onDetailClick: function(e){
        e.preventDefault();
        e.stopPropagation();
        console.log("ContactItemView.onDetailClick");
    },

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

app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    onDeleteClick: function(e){
        中略
        this.model.destroy();
    },

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

詳細ページを表示する

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 input-id="firstName" placeholder="First Name"></ons-input> 
            </ons-list-item>
            <ons-list-item>
                <ons-input input-id="lastName" placeholder="Last Name"></ons-input>
            </ons-list-item>
        </ons-list>
    </ons-page>
</ons-template>
app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    onDetailClick: function(e){
        中略
        naviMain.pushPage('detail-page');
    },

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

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

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

app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    onDetailClick: function(e){
        中略
        app.selected = this.model;
        naviMain.pushPage('detail-page');

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

app.js
app.ContactDetailView = Marionette.ItemView.extend({
    el: '#detail-page',
    ui: {
        firstName: '#firstName',
        lastName: '#lastName'
    },
    template: false,
    render: function(){
        $(this.ui.firstName).val(this.model.attributes.firstName);
        $(this.ui.lastName).val(this.model.attributes.lastName);
        return this;
    }
});

app.detailView = new app.ContactDetailView({
    model: app.selected
});
app.detailView.render();

ContactDetailView の model プロパティに app.selected をセットします↑

これを実行すると、new app.ContactDetailView したときエラーになります。
el で指定した <ons-page id="detail-page"> が、この時点で生成されていないからです。
Onsen UI では pushPage() されてから DOM 生成されます。したがって、

app.js
$(document).on('init', '#detail-page', function(){
    中略
    app.detailView = new app.ContactDetailView({
        model: app.selected
    });
    app.detailView.render();
});

Backbone.stickit を使う

詳細ページは簡単にデータバインディングしたいと思います。

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

Backbone.stickit
(https://github.com/NYTimes/backbone.stickit)

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

lib
    ↑以上が Backbone.js に必須
    backbone.stickit.js

使用します。

app.html
↑以上が Backbone.js に必須
<script src="lib/backbone.stickit.js"></script>

上記を参考にして

app.js
app.ContactDetailView = Marionette.ItemView.extend({
    中略
    bindings: {
        '#firstName': 'firstName',
        '#lastName': 'lastName'
    },
    render: function(){
        /*
        $(this.ui.firstName).val(this.model.attributes.firstName);
        $(this.ui.lastName).val(this.model.attributes.lastName);
        ↓入替 */
        this.stickit();

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

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

でもリストビューの画面の内容は変わっていません。

model の内容の変化に合わせて view を更新する

app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    modelEvents: {
        'change': 'onChange'
    },
    onChange: function(){
        console.log("ContactItemView.onChange");
        this.render();
    },

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

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

app.html
<ons-template id="list-page">
    <ons-page id="list-page">
        <ons-toolbar>
            中略
            <div class="right">
                <ons-toolbar-button class="js-add"><ons-icon icon="fa-plus"></ons-icon></ons-toolbar-button>
            </div>
app.js
app.ContactListView = Marionette.CompositeView.extend({
    中略
    events: {
        'click .js-add': 'onAddClick',
    },
    onAddClick: function(e){
        var contact = new app.Contact();
        this.collection.add(contact);
        app.selected = contact;
        navMain.pushPage('detail-page');
    }

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

変更したり追加したりしたデータが、実行し直すとなくなってしまいます。

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

Backbone.localStorate
(https://github.com/jeromegn/Backbone.localStorage)

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

lib
    ↑以上が Backbone.js に必須
    backbone.localStorage.js

使用します。

app.html
↑以上が Backbone.js に必須
<script src="lib/backbone.localStorage.js"></script>

事前設定↓

app.js
app.ContactList = Backbone.Collection.extend({
    中略
    localStorage: new Store('ContactList.TryMarionetteJs')

起動時に読込します↓

app.js
app.contacts.fetch();
app.listView = new app.ContactListView({

追加時に保存します↓

app.js
app.ContactListView = Marionette.CompositeView.extend({
    中略
    onAddClick: function(e){
        var contact = new app.Contact();
        /*
        this.collection.add(contact);
        ↓入替*/
        this.collection.create(contact);

削除時に保存します。これで削除されたことが保存されます↓

app.js
app.ContactItemView = Marionette.ItemView.extend({
    中略
    onDeleteClick: function(e){
        中略
        this.model.destroy();

画面で変更したのを保存するのはどこにセットするのがいいでしょうか↓

app.js
app.ContactDetailView = Marionette.ItemView.extend({
    中略
    modelEvents: {
        'change': 'onChange'
    },
    onChange: function(){
        console.log("ContactDetailView.onChange");
        this.model.save();
    },

こんな手もあります↓

app.js
app.ContactList = Backbone.Collection.extend({
    中略
    initialize: function(){
        this.on('change', this.onChange);
    },
    onChange: function(model){
        console.log("ContactList.onChange")
        model.save();
    }
4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?