質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

Backbone.jsでボタンのactive状態を変化させる方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,277

tarotarosu

score 111

前提・実現したいこと

Backbone.js初学習者です。
JavaScript(jQuery)でコーディングしていたのですが、結構な量のDOM操作とAjax通信になってきたため、そのような状態からでも導入できそうなフレームワークを探していたところ、Backbone.jsにたどり着きました。
そこで、Backbone.js導入の第一歩として、以下のような処理を実装しよう試みました。
①id="btn-get-size"のボタンをクリック
②idをもとにxmlファイルからサイズ(数値)を取得し、そのサイズを描画する
③描画されたサイズをクリックするとchosenプロパティが変化し、activeクラスが付与され、色が変化する
④その状態でほかのボタンをクリックするとそのサイズにactiveクラスが付与され、先ほどクリックしたサイズの色が消える(つまり、クリックしたサイズ以外は色変化させたくないということです)

上記の中で問題となっているのが③、④です。同じサイズを連続でクリックした場合は色が消えないようにしたいのです。
(簡単に言うと、色がつくのはクリックしたものだけ。ということです)
私の書いたコードですと、同じものをクリックすると色が消えてしまいます。(当たり前といえば当たり前なのかもしれませんが...)

個人的な考えでは、「サイズがクリックされたとき描画されている全モデルのchosenプロパティをfalseにし、その後でクリックしたもののchosenプロパティをtureにし、renderしなおす。」
という方法で実現できるのではないかと考えているのですが、Backbone.jsでそれをどう記述してやればいいのかがわかりません...
(そもそもBackbone.jsを使っていて、$(".size-selector").find(".btn-size").removeClass("active"); などとしている時点でかなりナンセンスですよね...)

どなたか、解決策を教えていただけると非常に助かります_(._.)_

該当のソースコード

(function(){
  //Model
  var Size = Backbone.Model.extend({
    defaults: {
      size: "",
      chosen: false
    }
  });

  //Collection
  var Sizes = Backbone.Collection.extend({
    model: Size
  });

  //ModelのView
  var SizeView = Backbone.View.extend({
    tagName: 'li',
    template: _.template($('#size-template').html()),
    initialize: function(){
      this.model.on('change', this.render, this)
    },
    events: {
      'click .btn-size': 'toggle'
    },
    toggle: function(){
      //ここで全部falseにする必要あり?
      $(".size-selector").find(".btn-size").removeClass("active");

      if (this.model.get('chosen') == false) {
        this.model.set('chosen', true);
      }
      else {
        this.model.set('chosen', false);
      }
    },
    render: function(){
      var template = this.template(this.model.toJSON());
      this.$el.html(template);
      return this;
    }
  });

  //CollectionのView
  var SizesView = Backbone.View.extend({
    tagName: 'ul',
    render: function(){
      this.collection.each(function(size){
        var sizeView = new SizeView({model: size});
        this.$el.append(sizeView.render().el);
      }, this);
      return this;
    }
  });

  //ボタンのView
  var ShapeBtnView = Backbone.View.extend({
    el: '#show-size',
    events: {
      'click #btn-get-size': 'getSize'
    },
    getSize: function(){
      //シェイプidを付与
      SHAPE.id = 'test01';
      //サイズCollectionを生成
      var sizes = new Sizes();

      ajax_processing('size.xml', function(xml){
        $(xml).find('shape[code="' + SHAPE.id + '"] > SizeList > Size').each(function(){
          //サイズ格納
          var size = $(this).attr('size');

          //取得したサイズをプロパティに設定して、sizeインスタンスを生成
          var size = new Size({size: size, chosen: false});

          //sizeをCollectionに追加
          sizes.add(size);
        });
        //Collectionをrender
        var sizesView = new SizesView({collection: sizes});
        $('.size-selector').append(sizesView.render().el);
      });
    }
  });

  //インスタンス生成
  var shapeBtnView = new ShapeBtnView();
})();
<div id="show-size">
  <button type="button" id="btn-get-size" class="btn btn-primary">Size一覧表示</button>
</div>
<h3>Backbonejs Demo</h3>
<div class="size-selector">
  <!-- ここに挿入 -->
</div>

<script type="text/template" id="size-template">
  <div class="btn-size <%= chosen ? 'active' : '' %>">
    <%- size %>
  </div>
</script>

追記

ajax_processing関数とは以下のようなものです。

function ajax_processing(url, fn) {
  $.ajax({
    url: url,
    type: "GET",
    dataType: "xml",
    timeout: 1000,
    error: function(){
      alert("ロード失敗");
    },
    success: fn
  });
}

追記2

追記2のようにSizesViewのコードを修正したところ、chosenプロパティがtrueのものをfalseにして、renderし直すことはできました。
おそらく、追加したhogeメソッドの中で、クリックされたmodelのchosenプロパティをtrueにしてrenderし直すことができれば、考えていたことが実現できそうなのですが…

var SizesView = Backbone.View.extend({
    tagName: 'ul',
    events: {
      'click .btn-size': 'hoge'
    },
    hoge: function(){
      var sizes = this.collection.where({'chosen': true});
      _.each(sizes, function(size){
        size.set('chosen', false);
        var sizeView = new SizeView({model: size});
        sizeView.render();
      });
    },
    render: function(){
      this.collection.each(function(size){
        var sizeView = new SizeView({model: size});
        this.$el.append(sizeView.render().el);
      }, this);
      return this;
    }
  });

追記3

何度も追記してすみません。
実現したいこととしては、下記サイトのようなことです。
しかし、CoffeeScriptがさっぱりなのと、このような複雑そうなことをしなければならないのか疑問で…
Backbone.jsでUIの状態を管理するみたいなやつ

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

下記のコードのように、Sizeモデルからchosenプロパティを削除し、SizesView内にactive状態を管理するactiveOperationメソッドを追加することで、何とか実現したいことは実現しました。
初期のコードから結構修正を加えた上に、これがBackbone.jsを使う上でスマートなやり方なのかわからない(おそらくプロパティ値から変更する方法のほうがスマートなのだろうとは思います)ので、その他の解決策がありましたら教えていただけますと非常に助かります_(._.)_

//サイズModel
var Size = Backbone.Model.extend({
  defaults: {
    size: ""
    //chosenプロパティを削除
  }
});

//サイズCollection
var Sizes = Backbone.Collection.extend({
  model: Size
});

//サイズModelのView
var SizeView = Backbone.View.extend({
  tagName: 'li',
  template: _.template($('#size-template').html()),
  render: function(){ //toggleした場合もrenderし直す
    var template = this.template(this.model.toJSON());
    this.$el.html(template);
    return this;
  }
});

//サイズCollectionのView
var SizesView = Backbone.View.extend({
  tagName: 'ul',
  //ここから修正
  events: {
    'click .btn-size': 'activeOperation'
  },
  activeOperation: function(){
    //一旦active状態を解除
    this.$('.active').removeClass('active');
    //クリックされたものをactive状態に
    var target = $(event.target);
    target.addClass('active');
  },
  //ここまで修正
  render: function(){
    this.collection.each(function(size){
      var sizeView = new SizeView({model: size});
      this.$el.append(sizeView.render().el);
    }, this);
    return this;
  }
});

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

同じサイズを連続でクリックした場合は色が消えないようにしたい

上記に対する回答のみになってしまいますが、toggle メソッドのなかで model の chosen が true なときは return してしまうとよいのではないでしょうか。

toggle: function(){
  // 追記
  if(this.model.get('chosen')) {
    return;
  }
  // 追記ここまで

  //ここで全部falseにする必要あり?
  $(".size-selector").find(".btn-size").removeClass("active");

  if (this.model.get('chosen') == false) {
    this.model.set('chosen', true);
  }
  else {
    this.model.set('chosen', false);
  }
},

return 以降の処理は走らなくなるので色が元に戻ることもなくなりそうです

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/13 18:37

    確かにその点だけで言うと意図した動きになるのですが...
    やりたいこととしては下記サイトのようなものなのです。
    http://hamalog.tumblr.com/post/19008960974/backbonejs%E3%81%A7ui%E3%81%AE%E7%8A%B6%E6%85%8B%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B%E3%81%BF%E3%81%9F%E3%81%84%E3%81%AA%E3%82%84%E3%81%A4
    しかし、CoffeeScriptはさっぱりわからないのと、こんな複雑そうなことをいなければならないのかと疑問に思ってしまって...

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる