theironcook / Backbone.ModelBinder

Simple, flexible and powerful Model-View binding for Backbone.
1.42k stars 159 forks source link

binding with Select2 #172

Open kinergy opened 11 years ago

kinergy commented 11 years ago

Hi, I'm having difficulty getting ModelBinder to bind to selects enhanced with Select2 - http://ivaynberg.github.io/select2/

Picking a value in the Select2 select will propagate to the model, but when a model is updated, my form doesn't update accordingly.

Has anybody done this? I tried googling, to no avail

theironcook commented 11 years ago

@kinergy How do you programatically set the value for a select2 control?

kinergy commented 11 years ago

Finally, this is what I did - wrote a custom converter:

  Converters.select2 = function(direction, value, attributeName, model, els) {
    var $elem = $(els[0]),
        result;

    if (direction === 'ViewToModel') {
      if ($elem.hasClass('select2'))
        result = $elem.select2('val');
      else
        result = $elem.val();
    } else {
      result = value;
      if ($elem.hasClass('select2'))
        $elem.select2('val', value);
      else
        $elem.val(value);
    }

    return result;
  };

I'm somewhat new to using ModelBinder, so if there are any suggestions on how to improve this converter, or use a different approach, I'd be interested in hearing :)

Thanks

theironcook commented 11 years ago

@kinergy

Yes, that's probably how I would have handled that situation. Otherwise I would put a case statement in for the select2 class in the _setElAttribute() function. But that's a bit weird because I'm not sure if this is a heavily used library. You could have also replaced the _setElAttribute function with your own handler that handled select2 and then called the base _setElAttribute() if there was no select2 class...

kinergy commented 11 years ago

It would be great if you'd be willing to modify the _setElAttribute() function. Select2 is quite popular (over 8800 stars on Github: https://github.com/ivaynberg/select2). Would you accept a pull request?

theironcook commented 11 years ago

Yes, I didn't realize the component was that popular.

But is there a better way to check if a component is a select2?

something like...

if(element.select2){

}

I'm not sure if there is a better way. I just don't want other components getting messed up that accidentally share the class 'select2'...

kinergy commented 11 years ago

My original method of checking for class 'select2' is a bad decision, I realized that I am the one applying that class in my templates so I can automatically know which selects should get the library applied (...I don't apply it to selects if on a mobile device so I can leverage the native select UI). Doing some more digging, the author of Select2 recommends doing:

$(..).data("select2")!==undefined

(reference: https://github.com/ivaynberg/select2/issues/1635)

If this is acceptable, let me know if you'd like a pull request or whether you'd like to implement it.

Thanks!

mikebridge commented 10 years ago

I think it's easier if you bind the ModelBinder to the original input element rather than what select2 generates. The current version of select2 says that a "change" event needs to be triggered on the original jquery element's val when it is updated from code in order to update the corresponding select2 dropdown. (http://ivaynberg.github.io/select2/#event_ext_change):

   <input id="myinput" />
   $("#myinput").select2(/* ... */):
   $("#myinput").val("123").trigger("change");

That way the ModelBinder and select2 don't have to know about each other.

nick4fake commented 9 years ago
void function (obj, name) {
        var old = obj[name];
        obj[name] = function (el, convertedValue) {
            old.apply(this, arguments);
            if (el.data('select2')) {
                el.select2('val', convertedValue);
            }
        };
    }(Backbone.ModelBinder.prototype, '_setElValue');

This could be done without modifying the library.

nick4fake commented 8 years ago

For select2 v4:

void function (obj, name) {
  var old = obj[name];
  obj[name] = function (el) {
    old.apply(this, arguments);
    if (el.data('select2')) {
      el.trigger('change.select2');
    }
  };
}(Backbone.ModelBinder.prototype, '_setElValue');