4imble
4imble

Reputation: 14416

Custom knockout binding not updating underlying observable

I have a textbox bound to a custom binding. For the sake of an example all it does it wrap the control with a bordered div. I then pass the bound observable to the value binding but it is not updating the underlying observable as I'd expect.

=Html=

  <body data-bind="with: model">
    <textarea data-bind="wrapbox: someval" ></textarea>
    <textarea data-bind="value: someval" ></textarea>
    <div data-bind="text: someval"></div>
  </body>

=Js=

ko.bindingHandlers.wrapbox = {
  update: function(element, valueAccessor)
  {
    var value = ko.unwrap(valueAccessor());
    $(element).wrap("<div class='border' />");
    return ko.bindingHandlers.value.update(element, 
       function(){return value; });
    //return ko.bindingHandlers.value.update(element, 
    //   valueAccessor()); <---- tried this too.
  }
};  


var viewmodel = function() {
  var model = {};
    model.someval = ko.observable();

  return {
    model: ko.observable(model)
  };
}();

ko.applyBindings(viewmodel);

=Example=

http://liveweave.com/QmJeaH

Upvotes: 2

Views: 979

Answers (2)

janfoeh
janfoeh

Reputation: 10328

Make sure that you set up your binding in an init handler, which in contrast to update runs only once and should contain your $.wrap.

Along these lines, if you wrap another binding, make sure to call both its init and update where appropriate, and pass along all arguments it may require:

ko.bindingHandlers.wrapbox = {
  init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    $(element).wrap("<div class='border' />");
    
    return ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
  },
  update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    return ko.bindingHandlers.value.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
  }
};

var viewmodel = function() {
  var model = {};
    model.someval = ko.observable('');
  
  return {
    model: ko.observable(model)
  };
}();

ko.applyBindings(viewmodel);
.lw { font-size: 60px; }

.border{
  border:  3px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<div data-bind="with: model">
    <textarea data-bind="wrapbox: someval" ></textarea>
    <textarea data-bind="value: someval" ></textarea>
    <div data-bind="text: someval"></div>
</div>

Upvotes: 1

Jamiec
Jamiec

Reputation: 136074

Your binding handler should

  • set up its state in init
  • pass everything through to the value binding directly in both init and update

Code:

ko.bindingHandlers.wrapbox = {
  init: function (element, valueAccessor, allBindingsAccessor, 
                    viewModel, bindingContext) {
        $(element).wrap("<div class='border' />");
        ko.bindingHandlers.value.init(element, valueAccessor, 
             allBindingsAccessor, viewModel, bindingContext);
    },
    update: function (element, valueAccessor, allBindingsAccessor, 
                     viewModel, bindingContext) {
        ko.bindingHandlers.value.update(element, valueAccessor, 
             allBindingsAccessor, viewModel, bindingContext);
    }
};

Updated your example: http://liveweave.com/Yq1UPl

Upvotes: 1

Related Questions