Tim
Tim

Reputation: 8921

Updating knockout model when using foreach binding with array of objects

I am following the example here to create a custom binding. The property to be updated is obtained like this in the documentation:

        var value = valueAccessor();
        value(false);

In my code, a table is bound to an array of objects using knockout's foreach binding.

 <tbody data-bind="foreach: {data: sites, as: 'site'}">
    <td class="siteid" data-bind="text: site.siteid"></td>
    <td class="expirydate" data-bind="datepicker: site.expirydate"></td>
          et cetera

The table seems to be correctly populated with data. The values are all in the right place.

The objects in the array have a few string properties and a couple of dates, so several of the table cells have jQuery UI datepickers attached to them. Here's the custom datepicker binding code I'm using, which I found here on SO, though I don't have the reference handy (it was from a response by Mr Niemeyer, as I recall).

ko.bindingHandlers.datepicker = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            //initialize datepicker with some optional options
            var options = allBindingsAccessor().datepickerOptions || {},
                $el = $(element);

            $el.datepicker(options);

            //handle the field changing
            ko.utils.registerEventHandler(element, "change", function () {
                var observable = valueAccessor();
                var newVal = $el.datepicker("getDate")
                observable(newVal);
               // a breakpoint is set at this location
            });
          .
          .
          . <snip>

The code crashes on observable(newVal);

However, if I change

var observable = valueAccessor(); 

to

 var observable = valueAccessor;

then the code runs without error. But when I examine the value in observable afterwards, at the breakpoint location, it does not contain the date value in newVal; rather it contains the original date value.

And if instead of observable(newVal) I try this instead:

     allBindingsAccessor().value(newVal);

I get an error: " Object doesn't support property or method 'value'".

Upvotes: 1

Views: 990

Answers (2)

super cool
super cool

Reputation: 6045

well yes it wont update the model sadly where Reasons Unknown .

But i made things work for me in different approach .

With CSHTML syntax :

<input type="text"  data-bind="value:$data.CompletionDate,datePicker:true " />

Points i noticed : -In data-bind if you remove value and try keeping some custom binding like below model wont get update

<input type="text"  data-bind="formatdate:$data.CompletionDate,datePicker:true " />
enter code here

-instead i tried using old fashioned way keeping value intact and added Datepicker:true whihc serves my purspose of binding and date selection .

My Datepicker binding handler

ko.bindingHandlers.datePicker = {
        init: function (element, valueAccessor) {
            var value = valueAccessor();
            var value = ko.utils.unwrapObservable(valueAccessor()); //ko.unwrap based on version you use 

            if (value && typeof value === 'object') {
                $(element).datepicker(value);
            }
            else {
                $(element).datepicker(/* your default options here */);
            }
        }
}

Well above approach may or maynot solve your issue but its definitely a cheery in mouth .

Upvotes: 0

Tim
Tim

Reputation: 8921

The individual properties of the objects in my array of objects were not observables.

So, when I add a new site object to my Sites array, I needed to be doing this:

              var Site = function(siteid, sitename, expirydate) {
                  this.siteid=siteid;
                  this.sitename = ko.observable(sitename);
                  this.expirydate = ko.observable(expirydate);

              }


              function SitesViewModel(){

                  var self=this;
                  self.sites = ko.observableArray([]);

                  self.sites.push( new Site ( ......) );

               }

so that valueAccessor() returns an observable, not simply an integer or string or date object, as the case may be.

Upvotes: 1

Related Questions