vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

Backbone.jsを使ってみる 3

引き続きBackbone.jsについて。

今回はBackbone.ModelとBackbone.Collectionを使ってこれを置き換えてみます。

やったこと

  • Backbone.ModelとBackbone.Collectionを使って、Ajaxで取得するHTMLファイルの名前を管理する。
  • HTMLファイルは、タブで切り替えるページごとに複数保持できるようにして、データを検索してファイル名を取得する。

データ作成

データを持ったModelを複数管理するのがCollection、ということで、まずModelにどういうデータをもたせるかを考えます。

Model作成

javascript.js

var FileModel = Backbone.Model.extend({
  defaults: {
    page: 0,
    content: 0,
    file: ''
  }
});
  • ページ番号、取得するHTMLファイルの番号、ファイル名を一つのデータとします。
  • 「defaults:」の通り、データのデフォルト値を設定しています。例えば「page」と「file」のみ指定してデータを追加した場合、「content」は自動で0が設定されます。

Collection作成

Collectionが格納するModelを指定します。

javascript.js

var FileCollection = Backbone.Collection.extend({
  model: FileModel
});

データを格納する

上記で作成したCollectionを使って、データを格納してみます。

javascript.js

var ContentFileCollection = new FileCollection([
    {page: 0, content: 0, file: '0.html'},
    {page: 0, content: 1, file: '1.html'},
〜省略〜
    ]);

Collectionからデータを取得する

get(ID)を使う

以下のようにModelにIDを設定している状態で単純にデータを取得するなら、「ContentFileCollection.get(0)」で取得できます(0はIDの番号)。

var FileModel = Backbone.Model.extend({
  defaults: {
    id: 0,
    page: 0,
    content: 0,
    file: ''
  }
});

特定の要素、例えば上記の「file」を取得したい場合は、「ContentFileCollection.get(0).get('file')」で取得できます('file'は要素名)。

準備

前回と同じくボタンを押した時の動作をViewで管理できるよう、Slimの内容を変更します。

viewTab.slim

〜省略〜
section#btn_frame
  input#btn_contents_0.btnContents type="radio" name="btns" data-content-num=0
    label#btn_label_0.btn_label for="btn_contents_0" ボタン0
  input#btn_contents_1.btnContents type="radio" name="btns" data-content-num=1
    label#btn_label_1.btn_label for="btn_contents_1" ボタン1
〜省略〜

データを検索する

今回、データを取得するためにページ番号とコンテンツ(ファイル)番号を指定します。
また、取得するファイルは一回の操作につき一つです。

以下のようなコードを書いてみました。

javascript.js

〜省略〜
var strTargetId = '#' + e.target.id;
var intContentNum = jQuery(strTargetId).data('contentNum');
$.ajax({
  type: 'GET',
  url: '/async/' + ContentFileCollection.findWhere({page: intTabNum, content: intContentNum}).get('file'),
〜省略〜
  • 「findWhere()」は、指定した条件にあった最初のデータを返します。
  • 複数のデータを取得したい場合は、「where()」を使用します。

検索条件の指定で、「findWhere({page: intTabNum}, {content: intContentNum})」のように書いたところ、最初の「{page: intTabNum}」のみの結果が返ってきたためちょっとハマりました。

実行しただけだと特にエラーが表示されないのがちょっとツラいところ。
テスト時には条件が反映されているかなどを出力できるようにしたいですね。

なお、以前は上記のajaxのアクセスをapp.rbでキャッチして、パラメータとして渡した番号に拡張子「.html」を追加していましたが、今回javascript側で拡張子付きのファイル名を渡すよう変更しています。

app.rb

〜省略〜
get '/async/:name' do
  File.open("public/html/" + params[:name].to_s)
end
〜省略〜

javascriptのコードが細切れになったため、まとめて載せておきます。

javascript.js

(function () {
  var intTabNum = 0;
  var aryTabName = ["tab_label_0", "tab_label_1", "tab_label_2", "tab_label_3", "tab_label_4"]
  var intRightTabNum = 2;
  var intLeftTabNum = 0;
  var DEFAULT_RIGHT_TAB_NUM = 2;
  var FileModel = Backbone.Model.extend({
    defaults: {
      page: 0,
      content: 0,
      file: ''
    }
  });
  var FileCollection = Backbone.Collection.extend({
    model: FileModel
  });
  var ContentFileCollection = new FileCollection([
      {page: 0, content: 0, file: '0.html'},
      {page: 0, content: 1, file: '1.html'},
      {page: 1, content: 0, file: '0.html'},
      {page: 1, content: 1, file: '1.html'},
      {page: 2, content: 0, file: '0.html'},
      {page: 2, content: 1, file: '1.html'},
      {page: 3, content: 0, file: '0.html'},
      {page: 3, content: 1, file: '1.html'},
      {page: 4, content: 0, file: '0.html'},
      {page: 4, content: 1, file: '1.html'}]);
  var TabView = Backbone.View.extend({
    el: '#main',
    events: {
      'change .tab_radio': 'movePage',
      'click .button_arrow': 'moveTab',
      'click .btnContents': 'showContents'
    },
    moveTab: function (e){
      switch (e.target.id){
      case 'arrow_button_left':
        // 一つ左隣のTabを表示.
        intRightTabNum--;
        updateTabStyles();
        break;
      case 'arrow_button_right':
        // 一つ右隣のTabを表示.
        intRightTabNum++;
        updateTabStyles();
        break;
      }
    },
    movePage: function (e) {
      var strTargetId = '#' + e.target.id;
      var intNewPageNum = jQuery(strTargetId).data('tabNum');
      location.href='?tab=' + intNewPageNum + '&lrt=' + intRightTabNum;
    },
    showContents: function(e) {
      var strTargetId = '#' + e.target.id;
      var intContentNum = jQuery(strTargetId).data('contentNum');
      getHtml(intTabNum,intContentNum);
    }
  });
  var view = new TabView();

  this.loadTabs = function(intNewTabNum, intLastRightTabNum){
    // ロード時に実行. Tabの表示切り替え、矢印ボタンのEnable/Disable.
    intTabNum = intNewTabNum;
    intRightTabNum = intLastRightTabNum;
    updateTabStyles();
    // デフォルトでボタン0のコンテンツが表示されるようにする
    document.getElementById("btn_contents_0").checked = true;
    getHtml(intTabNum, 0);
  };
  this.updateTabStyles = function(){
    // 矢印ボタンのEnable/Disableを設定.
    document.getElementById('arrow_button_left').disabled = (intRightTabNum <= DEFAULT_RIGHT_TAB_NUM);
    document.getElementById('arrow_button_right').disabled = (intRightTabNum >= 4);
    // Tabの表示切り替え
    document.getElementById("tab_" + intTabNum).checked = true;
    var intTabLeftNum = intRightTabNum - 2;
    for(var i=4;i>=0;i--){
      document.getElementById(aryTabName[i]).style.display = ((i >= intTabLeftNum)&&(i <= intRightTabNum))? 'block': 'none';
    }
  };
  this.getHtml = function(intNewTabNum, intNewContentNum) {
    $.ajax({
      type: 'GET',
      url: '/html/' + ContentFileCollection.findWhere({page: intNewTabNum, content: intNewContentNum}).get('file'),
      dataType: 'html',
      success: function(data) {
        $('#contents_frame').html(data);
      },
      error:function() {
        alert('Error');
      }
    });
  };
}).call(this);

参考