سؤال

I have a large viewModel, and I would like to post the update of a status ("read"/"unread") to the server without posting the entire model.

What I've done is create a Custom Binding like so:

ko.bindingHandlers.statusUpdater = {
    update: function(element, valueAccessor) {
        console.log(element);
    }
};

In the template:

<div data-bind='template: { name: "contactsListTemplate", data: viewModel.conversations.conversationlist }'> </div>

<script type="text/html" id="contactsListTemplate">
 <table>
  <tbody>
   {{each(i, conversation) $data}}
    <tr>
     <td>
      <input type="checkbox" data-bind="checked: read, statusUpdater: conversation_id" />
     </td>
    </tr>
   {{/each}}
  </tbody>
 </table>
</script>

For the custom binding, I'm only interested in Updates, what I thought that would allow me to do is detect an update with KnockoutJS, and determine which item was updated so I could grab that items ID, and new stats, and then post that to the server.

What's happening is the the customBinding is console.logging every single checkbox on a single checkbox modification. This means I change a checkbox and all 3 checkboxes are logging to the console via the ko.bindingHandlers.statusUpdater.

I thought about adding a click event to the data-binding but that didn't seem as clean as a custom binding. Perhaps what I'm trying to do with custom bindings isn't what they're for?

Thoughts?

هل كانت مفيدة؟

المحلول

The reason its doing that is because the update method is called each the time the model value is updated AND at the start after the init method is called.

The update binding method is for you to set the state of the bound dom element when the viewmodel changes.

If you would like to react to a change and update the viewModel you will need to implement the init method and attach an event (click, change etc) In this handler you can then send the status update to your viewmodel.

ko.bindingHandlers.statusUpdater = {
    'init': function (element, valueAccessor, allBindingsAccessor) {

        var updateHandler = function() {            
            var valueToWrite;
            if (element.type == "checkbox") {
                valueToWrite = element.checked;
            } else if ((element.type == "radio") && (element.checked)) {
                valueToWrite = element.value;
            } else {
                return; // "checked" binding only responds to checkboxes and selected radio buttons
            }

            var modelValue = valueAccessor();

            if (ko.isWriteableObservable(modelValue)) {             
                if (modelValue() !== valueToWrite) { // Suppress repeated events when there's nothing new to notify (some browsers raise them)
                    $.ajax({
                      url: 'someurl',
                      success: function(data) {
                            alert('status update');
                      }
                    });
                    modelValue(valueToWrite);
                }
            } else {
                var allBindings = allBindingsAccessor();
                if (allBindings['_ko_property_writers'] && allBindings['_ko_property_writers']['checked']) {
                    allBindings['_ko_property_writers']['checked'](valueToWrite);
                }
            }
        };

        $(element).click(updateHandler).change(updateHandler);
    },
    'update': function (element, valueAccessor) {
        ko.bindingHandlers.checked(element, valueAccessor);
    }
};

The best way to learn this I found was to look at the debug version of KO on git. What you want to achieve is basically a modified checked binding with an ajax call.

I haven't tested the above but it should get you started.

Cheers,

Ian

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top