简体   繁体   中英

Add class to elements for values of attribute with Backbone.ModelBinder

I'm using Backbone.ModelBinder in a Backbone.js Marionette project. I've a scenario which I can't work out how to use ModelBinder to automatically update my model/UI.

My model has a 'status' string attribute, with multiple states. In this example I'll show the code for two: 'soon', 'someday'

In my UI I have a list on which I use click events to set the model status, and update classes to highlight the relevant link in the UI.

<dd id="status-soon"><a>Soon</a></dd>
<dd id="status-someday" class="active"><a>Someday</a></dd>

events: {
    'click #status-soon': 'setStatusSoon',
    'click #status-someday': 'setStatusSomeday'
},
setStatusSoon: function () {
    this.model.set('status', 'soon');
    this.$el.find('.status dd').removeClass('active');
    this.$el.find('#status-soon').addClass('active');
},
... etc

It feels like I doing this a long-winded and clunky way! The code bloat increases with the number of states I need to support. What's the best way of achieving the same outcome with ModelBinder?

You could probably simplify things with a data attribute, something like this:

<dd data-status="soon" class="set-status"><a>Soon</a></dd>
<dd data-status="someday" class="set-status active"><a>Someday</a></dd>

and then:

events: {
    'click .set-status': 'setStatus'
},
setStatus: function(ev) {
    var $target = $(ev.target);
    var status  = $target.data('status');
    this.model.set('status', status);
    this.$el.find('.status dd.set-status').removeClass('active');
    $target.addClass('active');
}

You might not need the set-status class, just keying things on the <dd> s might be sufficient; I prefer separating my event handling from the nitty gritty element details though.

Unfortunately, it is going to be pretty difficult to do exactly what you want with ModelBinder . The main reason being that ModelBinder wants to provide the same value for all elements that are part of a single selector. So doing this with ModelBinder , while possible, is going to be pretty verbose as well.

The cleanup offered by mu is likely to be better than trying to use ModelBinder . 1) because you need a click handler to do the this.model.set no matter what and 2) you would need individual bindings for ModelBinder because the converter function is called once for a single selector and then the value is set on all matching elements (rather than looping through each one).

But if you do want to try and do something with ModelBinder it would look something like this:

  onRender : function () {
    var converter = function (direction, value) { 
      return (value == "soon" ? "active" : "");
    };
    var bindings = {
      status : {selector : "#status-soon", elAttribute : "class", converter : converter}
    };
    this.modelBinder.bind(this.model, this.el, bindings);
  }

This would do what you want. Of course the down side as I said above is that you will need multiple selector bindings. You could generalize the converter using this.boundEls[0] but you will still need the separate bindings for it to work.

In case you want to access to the bound element, it is possible to declare 'html' as elAttrbute, modify the element and return its html with converter function:

onRender : function () {
  var converter = function (direction, value, attribute, model, els) {
    return $(els[0]).toggleClass('active', value === 'soon').html();
  };
  var bindings = {
    status : {
      selector : "#status-soon", 
      elAttribute : "html", 
      converter : converter
    }
  };
  this.modelBinder.bind(this.model, this.el, bindings);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM