Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout not updating view, when a template binds a handler using $data

Tags:

knockout.js

I'm trying to write my KO templates in a certain manner, and it seems to be causing issues with Knockout, which stops updating the view. I wish to avoid too many explicit dependencies in my views as possible, so when I'm writing a template for adding to an files attachments list, I thought I could make use of the $data variable:

<script id="attachments-template" type="text/html">
    <input type="button" data-bind="attachments: $data" value="add">
</script>

And the template binding:

<div data-bind="template: {name: 'button-add-data', data: attachments}"></div>

This keeps the actual property mapped in the usage site, instead of off in random templates. The attachments binding handler in my actual case wraps the jQuery fileupload plugin, but just calling push(i++) shows the issue anyway.

var i = 0;
ko.bindingHandlers.attachments = {
    init: function(element, valueAccessor) {
        var files = valueAccessor();
        $(element).click(function() {
            files.push(i++);
        });
    }
};

var list = ko.observableArray();
var model= {
    attachments: list
};

A fiddle which shows this, using KO 2.2.0: http://jsfiddle.net/stofte/sWGkJ/ The fiddle also shows that binding against an explicit property works fine.

Obviously there's alot of stuff on Google and SO on KO and binding contexts, but I can't find anything on the usage of $data in binding handlers, I'm not sure what KO law I'm breaking with my usage of $data, but seems like it would make good sense to be able to do what I want?

like image 392
Svend Avatar asked Nov 12 '22 14:11

Svend


1 Answers

It appears that knockout doesn't eaxactly expect you to pass an observableArray into the data paramater on a template binding. Usually that's what a foreach is for. It seems that data expects a regular object to behave correctly (citation needed, couldn't find any docs other than the fact that it appears to behave this way).

Using the same JS code that you have, the easiest solution seems like it would be to wrap the observable array straight in your template binding:

<script id="button-add-data" type="text/html">
    isObservable: <span data-bind="text: ko.isObservable(items)"></span><br>
    toJSON: <span data-bind="text: ko.toJSON(items)"></span><br>
    <input type="button" data-bind="attachments: items" value="doesnt update">
</script>
<div data-bind="template: {name: 'button-add-data', data: { items: attachments }}"></div>

Alternatively you could override your template bindingHandler and create a new parameter you can pass in which simplifies this similar behaviour. Link: knockoutjs overriding bindinghandlers

like image 183
Richard Rout Avatar answered Jan 04 '23 01:01

Richard Rout