1
1

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.

勉強会JS編<5> yeoman + backbone.collection + backbone.localStorage

Last updated at Posted at 2016-01-26

関連記事

Backbone.Collectionとは

  • 複数のBackbone.Modelを保持し、管理するコレクション
  • extend()を使ってBackbone.Collectionの機能を継承するオブジェクトを作成して使う。

実習

オブジェクトの定義

  • yeomanでcontactというcollectionを生成する。
  [devnote@hooni:~/documents/study/js/sample] % yo backbone:collection contact
     create app/scripts/collections/contact.js
     create test/collections/contact.spec.js
  • コレクションの定義
  // app/scripts/collections/contact.js
  /*global Sample, Backbone*/

  Sample.Collections = Sample.Collections || {};

  (function () {
    'use strict';

    Sample.Collections.Contact = Backbone.Collection.extend({

      // modelプロパティにどのモデルを管理するかを宣言する。
      // この宣言によって、コレクションが保持するモデルはContactのインスタンスとなる。
      model: Sample.Models.Contact,

      // initialize()メソッドを定義できる点はBackbone.Modelと同様
      initialize: function() {
        console.log('ContactCollectionが初期化されました。');
      }

    });

  })();

モデルの追加

  • add()
  // app/scripts/main.js
  collection_ex01: function(contact) {
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.add(contact);

    var ichiro = new Sample.Models.Contact({
      firstname: '一郎',
      lastname: '鈴木',
      email: 'suzuki@example.com'
    });
    contactCollection.add(ichiro);
    console.log(JSON.stringify(contactCollection, null, 2));
    console.log(contactCollection.length);
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex01(c);
  });
  • 同じモデルを追加する場合
  // app/scripts/main.js
  // すでに追加されているモデルが渡された場合は、何も行わない。
  collection_ex02: function(contact) {
    'use strict';
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.add(contact);
    contactCollection.add(contact);
    console.log(JSON.stringify(contactCollection, null, 2));
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex02(c);
  });
  • モデルのインスタンスではなく、オブジェクトを渡す場合
  // app/scripts/main.js
  // オブジェクトを渡しても結果は同様 
  collection_ex03: function(contact) {
    'use strict';
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.add(contact);
    contactCollection.add({
      firstname: '一郎',
      lastname: '鈴木',
      email: 'suzuki@example.com'
    });
    console.log(JSON.stringify(contactCollection, null, 2));
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex03(c);
  });
  • モデルを一括追加する場合
  // app/scripts/main.js
  // オブジェクトの配列を渡すことで一度に複数のモデルを生成する。
  collection_ex04: function(contact) {
    'use strict';
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.add([
      {
        firstname: '太郎',
        lastname: '山田',
        email: 'yamada@example.com'
      }, {
        firstname: '一郎',
        lastname: '鈴木',
        email: 'suzuki@example.com'
      }
    ]);
    console.log(JSON.stringify(contactCollection, null, 2));
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex04(c);
  });
  • コレクションを初期化する際にモデルを渡す場合
  // app/scripts/main.js
  collection_ex05: function(contact) {
    'use strict';
    var ichiro = new Sample.Models.Contact({
      firstname: '一郎',
      lastname: '鈴木',
      email: 'suzuki@example.com'
    });
    var contactCollection = new Sample.Collections.Contact([contact, ichiro]);
    console.log(JSON.stringify(contactCollection, null, 2));

    return contactCollection;
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex05(c);
  });

モデルの削除

  • remove()
  // app/scripts/main.js
  collection_ex06: function(contact, contactCollection) {
    'use strict';
    contactCollection.remove(contact);
    console.log(JSON.stringify(contactCollection, null, 2));
  }, 

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    var cc = Sample.collection_ex05(c);
    Sample.collection_ex06(c, cc);
  });

モデルのリセット

  • reset()
    • 保持しているモデルをすべて新しいものに入れ替える。
  // app/scripts/main.js
  collection_ex07: function(contact, contactCollection) {
    'use strict';
    var tanaka = new Sample.Models.Contact({
      firstname: '田中',
      lastname: '将大',
      email: 'tanaka@example.com'
    });
    contactCollection.reset([tanaka]);
    console.log(JSON.stringify(contactCollection, null, 2));
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    var cc = Sample.collection_ex05(c);
    Sample.collection_ex07(c, cc);
  });

イベント

  • add
  // app/scripts/main.js
  collection_ex08: function(contact, contactCollection) {
    'use strict';
    contactCollection.on('add', function(contact) {
      console.log('モデルが追加されました。', contact.get('firstname'));
    });
    var tanaka = new Sample.Models.Contact({
      firstname: '田中',
      lastname: '将大',
      email: 'tanaka@example.com'
    });
    contactCollection.add(tanaka);
    console.log(JSON.stringify(contactCollection, null, 2));
  }, 

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    var cc = Sample.collection_ex05(c);
    Sample.collection_ex08(c, cc);
  });

Underscore.jsの機能

  • JavaScriptを拡張するユーティリティライブラリ
  • オブジェクトを操作するための便利な機能を提供する。
  • 本来は_.each(someArray, function() {...})ように使用するが、コレクションに対して直接呼び出すこともできる。
  ContactCollection.each(function(contact) {
    // ...
  })
  • 使用例:filter()
    • 条件に合致するモデルを配列にいて返す。
  // app/scripts/main.js
  collection_ex09: function() {
    'use strict';
    var ichiro = new Sample.Models.Contact({
      firstname: '一郎',
      lastname: '鈴木',
      age: 42,
      email: 'suzuki@example.com'
    });
    var tanaka = new Sample.Models.Contact({
      firstname: '田中',
      lastname: '将大',
      age: 27,
      email: 'tanaka@example.com'
    });
    var aoki = new Sample.Models.Contact({
      firstname: '青木',
      lastname: '宣親',
      age: 34,
      email: 'aoki@example.com'
    });
    var contactCollection = new Sample.Collections.Contact([ichiro, tanaka, aoki]);
    var filtered = contactCollection.filter(function(contact) {
      // Contactモデルがage(年齢)属性を持っていたとして、それ年齢が30以上のモデルだけを抽出した配列を返す。
      return contact.get('age') >= 30;
    });
    console.log(JSON.stringify(filtered, null, 2));
  }

  ...snip...

  $(document).ready(function () {
    'use strict';
    Sample.collection_ex09();
  });

データの永続化

  • データの取得

    • fetch()
      • Backbone.ModelとBackbone.Collectionのインスタンスから使用できる。
      • fetch()は内部で$.ajaxメソッドを使うので、successやerrorオプションにコールバック関数を渡すことができる。
      • 戻り値はjQueryのPromiseオブジェクト
      • fetch()完了後、コレクションはsyncイベントを発生させる。
  • データの保存

    • save()
      • Backbone.Modelのインスタンスから使える。
    • create()
      • Backbone.Collectionのインスタンスから使える。
      • モデルの生成、コレクションへの追加、サーバへの送信をまとめて行う。
  • データ保存の仕組み

    • モデルがid属性を持っていない場合、コレクションに設定されているurlプロパティに対してPOST形式のHTTPリクエストが行われる。
    • モデルがid属性を持っている場合、コレクションのurlプロパティとモデルのid属性値を連結したパスにPUTリクエストが行われる。
  • データの削除

    • destroy()
      • Backbone.Modelのインスタンスから使える。
      • 該当モデルはコレクションやサーバから削除される。
      • 該当モデルと対応するURLに対して実際にDELETEのHTTPリクエストが送信される。
      • 該当モデルを保持していたコレクションがremoveとdestroyイベントを発生させる。
  • backbone.localStorage

    • RESTインターフェイスを通じてサーバ側にデータを保持することではなくBackbone.Syncを上書きしデータストアとしてHTML5ローカルストレージを使う。
    • 設置(localStorageではなくbackbone.localStorageである)
  [devnote@hooni:~/Documents/study/js/sample] % bower install backbone.localStorage --save
  bower backbone.localStorage#*   cached git://github.com/jeromegn/Backbone.localStorage.git#1.1.16
  bower backbone.localStorage#* validate 1.1.16 against git://github.com/jeromegn/Backbone.localStorage.git#*
  bower backbone.localStorage#~1.1.16          install backbone.localStorage#1.1.16

  backbone.localStorage#1.1.16 app/bower_components/backbone.localStorage
  └── backbone#1.1.2
  [devnote@hooni:~/Documents/study/js/sample] % bower list
  bower check-new     Checking for new versions of the project dependencies...
  sample#0.0.0 /Users/devnote/Documents/study/js/sample
  ├─┬ backbone#1.1.2 (latest is 1.2.3)
  │ └── underscore#1.8.3
  ├─┬ backbone.localStorage#1.1.16 extraneous
  │ └── backbone#1.1.2 (1.2.3 available)
  ├─┬ bootstrap-sass-official#3.3.6
  │ └── jquery#2.1.4 (3.0.0-beta1 available)
  ├── chai#3.4.2
  ├── jquery#2.1.4 (latest is 3.0.0-beta1)
  ├── lodash#2.4.2 (latest is 4.0.1)
  └── mocha#2.3.4
  • bower.jsonを確認する。dependenciesにbackbone.localStorageが追加されている。
  {
    "name": "sample",
    "version": "0.0.0",
    "dependencies": {
      "bootstrap-sass-official": "~3.3.1",
      "jquery": "~2.1.0",
      "backbone": "~1.1.0",
      "lodash": "~2.4.1",
      "backbone.localStorage": "~1.1.16"
    },
    "devDependencies": {
      "chai": "~3.4.2",
      "mocha": "~2.3.4"
    }
  }
  • app/index.htmlにてbackbone.localStorageを読み込む。
  <!-- build:js scripts/vendor.js -->
  <script src="bower_components/jquery/dist/jquery.js"></script>
  <script src="bower_components/lodash/dist/lodash.compat.js"></script>
  <script src="bower_components/backbone/backbone.js"></script>
  <script src="bower_components/backbone.localStorage/backbone.localStorage.js"></script>
  <!-- endbuild -->
  • app/scripts/collections/contact.jsにてlocalStorageのオブジェクトを生成する。
  // app/scripts/collections/contact.js
  ...snip...

  model: Sample.Models.Contact,

  // localStorage
  localStorage: new Backbone.LocalStorage("Sample.Collections.Contact"),

  ...snip...
  • Backbone.Collectionのcreate()
  // app/scripts/main.js
  collection_ex10: function() {
    'use strict';
    var ichiro = new Sample.Models.Contact({
      firstname: '一郎',
      lastname: '鈴木',
      age: 42,
      email: 'suzuki@example.com'
    });
    var tanaka = new Sample.Models.Contact({
      firstname: '田中',
      lastname: '将大',
      age: 27,
      email: 'tanaka@example.com'
    });
    var aoki = new Sample.Models.Contact({
      firstname: '青木',
      lastname: '宣親',
      age: 34,
      email: 'aoki@example.com'
    });
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.create(ichiro);
    contactCollection.create(tanaka);
    contactCollection.create(aoki);
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex10();
  });
  • Local Storageに保持されているデータは、developer toolsにて Resources → Local Storage → http://localhost:9000 順でクリックすることで確認できる。
  • grunt serveが動作されている状態でLocal Storageを確認すると、データが重複で保持されている。
  • Local Storage中のデータを全部削除しページをリロードしたら、予想通りデータが保持されていた。
  Sample.Collections.Contact  0c375b69-007a-a992-c8dd-eb9c1c4aa49e,f7aca131-ed31-7d66-0715-eb86d9865c5f,e6aab275-ebbb-9d72-13d6-3545c71bfe41
  Sample.Collections.Contact-0c375b69-007a-a992-c8dd-eb9c1c4aa49e {"firstname":"一郎","lastname":"鈴木","age":42,"email":"suzuki@example.com","id":"0c375b69-007a-a992-c8dd-eb9c1c4aa49e"}
  Sample.Collections.Contact-e6aab275-ebbb-9d72-13d6-3545c71bfe41 {"firstname":"青木","lastname":"宣親","age":34,"email":"aoki@example.com","id":"e6aab275-ebbb-9d72-13d6-3545c71bfe41"}
  Sample.Collections.Contact-f7aca131-ed31-7d66-0715-eb86d9865c5f {"firstname":"田中","lastname":"将大","age":27,"email":"tanaka@example.com","id":"f7aca131-ed31-7d66-0715-eb86d9865c5f"}
  • Backbone.Collectionのfetch()
  // app/scripts/main.js
  collection_ex11: function() {
    var contactCollection = new Sample.Collections.Contact();
    contactCollection.localStorage = new Backbone.LocalStorage("Sample.Collections.Contact");
    contactCollection.fetch();
    // console.log(contactCollection.pluck('lastname'));
    contactCollection.each(function(contact) {
      console.log(JSON.stringify(contact, null, 2));
    });
  },

  ...snip...

  $(document).ready(function () {
    'use strict';
    var c = Sample.init();
    Sample.collection_ex10();
    Sample.collection_ex11();
  });
  • developer tools → Consoleにて確認する。
  Hello from Backbone!
  contact.js:19 ContactCollectionが初期化されました。
  contact.js:19 ContactCollectionが初期化されました。
  main.js:285 {
    "firstname": "一郎",
    "lastname": "鈴木",
    "age": 42,
    "email": "suzuki@example.com",
    "id": "07e8e44a-55f8-ff5e-860a-9a8d1b763b39"
  }
  main.js:285 {
    "firstname": "田中",
    "lastname": "将大",
    "age": 27,
    "email": "tanaka@example.com",
    "id": "100c3d73-ddd4-59ff-a299-cc800bbb649d"
  }
  main.js:285 {
    "firstname": "青木",
    "lastname": "宣親",
    "age": 34,
    "email": "aoki@example.com",
    "id": "2cb06f4e-33cf-fb91-cc8e-fe390ac47e29"
  }

参考書籍

  • JavaScript徹底攻略
  • JavaScriptエンジニア養成読本
  • 入門Backbone.js
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?