Knockout.js Template not updating UI binding on a dependantObservable

2,919 views
Skip to first unread message

Chris

unread,
May 20, 2011, 7:09:05 AM5/20/11
to KnockoutJS
Hi,

The application is written using ASP.NET MVC 3 in vs2010.

I have a knockout template that updates some css and visible bindings
using a
dependantObservable.

The issue ONLY occurs when I bind the value of the select element to
the IntervalID. If this is not bound the UI is updated as expected.

I have ripped the code out from my main app and created a sample page
that does the same binding using standard markup and also templates.

The dependantObservable is called HasChanged.

<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"
type="text/javascript"></script>
<script src="../../Scripts/jquery.tmpl.js" type="text/
javascript"></script>
<script src="../../Scripts/knockout-1.2.0.js" type="text/
javascript"></script>

<div data-bind="template: { name: 'intervalTemplate',
for:viewModel }">
</div>
<h2>
Not Template</h2>
<div data-bind="style: { color: HasChanged() ? 'red' : 'black' }">
IntervalID: <span data-bind="text: IntervalID"></span>
<br />
Start:
<input type="text" data-bind="value: Start">
<br />
End:
<input type="text" data-bind="value: End">
<br />
Interval Type:
<select data-bind="value: IntervalTypeID">
<option value="1">Shift</option>
<option value="2">Break (Paid)</option>
<option value="3">Break (Unpaid)</option>
</select><br />
HasChanged: <span data-bind="text: HasChanged"></span>
</div>

<script id="intervalTemplate" type="text/html">
<div data-bind="style: { color: HasChanged() ? 'red' :
'black' }">
<h2>Template</h2>
IntervalID: <span data-bind="text: IntervalID"></span>
<br />
Start:
<input type="text" data-bind="value: Start">
<br />
End:
<input type="text" data-bind="value: End">
<br />
Interval Type:
<select data-bind="value: IntervalTypeID">
<option value="1">Shift</option>
<option value="2">Break (Paid)</option>
<option value="3">Break (Unpaid)</option>
</select><br />
HasChanged: <span data-bind="text: HasChanged"></span>
</div>
</script>

<script type="text/javascript">
function IntervalModel(data) {
var _this = this;

_this.IntervalID = ko.observable(data.IntervalID);
_this.Start = ko.observable(data.Start);
_this.End = ko.observable(data.End);
_this.IntervalTypeID = ko.observable(data.IntervalTypeID);

_this.OriginalStart = ko.observable(data.Start);
_this.OriginalEnd = ko.observable(data.End);
_this.OriginalIntervalTypeID =
ko.observable(data.IntervalTypeID);


_this.HasChanged = ko.dependentObservable(function () {
return !(_this.OriginalStart() == _this.Start() &
_this.OriginalEnd() == _this.End() &
_this.OriginalIntervalTypeID() ==
_this.IntervalTypeID());
});
}

var viewModel;

$(function () {
var viewModel = {};

viewModel = new IntervalModel({ IntervalID: 1, Start:
"09:00", End: "10:00", IntervalTypeID: 2 });
ko.applyBindings(viewModel);
});
</script>


Any help would be much appreciated... I need to use templates as i
have lots of these intervals that need to be displayed.

Thanks!

rpn

unread,
May 20, 2011, 12:07:59 PM5/20/11
to knock...@googlegroups.com
I answered this one on StackOverflow here: http://stackoverflow.com/questions/6070716/knockout-js-template-not-updating-ui-binding-on-a-dependantobservable

I could not yet determine the underlying issue, but here is a very simplified reproduction: http://jsfiddle.net/rniemeyer/65eZM.  Maybe someone has more time to look at it quick.

It is definitely related to the observable getting updated from 1 to "1" during the first 'update' of the value binding called when it is first set up.

// For SELECT nodes, you're not allowed to have a model value that disagrees with the UI selection, so if there is a
// difference, treat it as a change that should be written back to the model
if (element.tagName == "SELECT"{
    elementValue ko.selectExtensions.readValue(element);
    if(elementValue !== newValue)
        ko.utils.triggerEvent(element"change");
}


So, the eventHandler set in 'init' will update our observable.  However, any bindings set up prior to that in the template that depend on that observable will no longer receive updates.

rpn

unread,
May 20, 2011, 2:51:38 PM5/20/11
to knock...@googlegroups.com
Looked at this quick again.  It appears that it is hitting the disposal logic that checks whether the element is actually part of the document.  In this case, the dependentObservable was set, but the element is not in the document yet.  So, it is re-evaluated and then dropped.

options["disposeWhen"] = function () {
return (!ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved))
|| ((typeof existingDisposeWhenFunction == "function") && existingDisposeWhenFunction());
}

Boris Rosenow

unread,
May 22, 2012, 7:32:58 AM5/22/12
to knock...@googlegroups.com
I'll just go ahead and revive this thread! :)

I have a similar problem right now and it definitly boils down to the disposal logic, as Ryan mentioned already. Does anybody has any ideas about this issue?

Boris Rosenow

unread,
May 30, 2012, 10:24:50 AM5/30/12
to knock...@googlegroups.com
There should be a way for the template binding to register the topmost element, which will be inserted into the document. Afterwards, the  ko.utils.domNodeIsAttachedToDocument(disposeWhenNodeIsRemoved) should be replace by a new function, cross-checking in each iteration, whether the current element is such a registered template element. Personally I don't see any other option, where KO itself does not have to be modified.

This is quite an urgent issue for us, as we currently implemented bindings, which manipulate the bound observables based on certain parameters. I hope, somebody has any ideas or a simple solution.

Please note: We are currently still using KO 1.2.1. When inspecting the KO2.1 code, I did not notice anything, which would prevent this "error", but I probably may be mistaken.

Boris Rosenow

unread,
Jun 27, 2012, 3:53:02 AM6/27/12
to knock...@googlegroups.com
Sorry for just replying to this thread to bring it back to focus, but I am pretty desperate here and just don't seem to find a solution on my own.

Ryan Niemeyer

unread,
Jun 27, 2012, 9:07:35 AM6/27/12
to knock...@googlegroups.com
Can you post a jsFiddle or sample of your code?  Maybe we can figure out a workaround or fix for your scenario.

Boris Rosenow

unread,
Jun 27, 2012, 10:24:54 AM6/27/12
to knock...@googlegroups.com
Sure!


Most of the code may seem a bit confusing, but I tried to align the example as much as possible to our actual code. We need a custom binding similar to the one in the code to set some values in a data row, which will be sent back to the server, by another row which contains the original data. We have to do this in a binding to make sure, only those property values are copied, which will actually be used in the view.

We are working with quite complex data and view models and thus need such a dynamic approach.

Thanks in advance!

Boris

rpn

unread,
Jun 27, 2012, 10:48:19 AM6/27/12
to knock...@googlegroups.com
Hi Boris-
Are you able to upgrade to a newer version of KO in your project?  With 2.1 it looks like:  http://jsfiddle.net/rniemeyer/3NFNR/17/ 

I am not sure if I am quite on top of your scenario, but I feel like there might be another pattern that could be beneficial to you.  Are you looking to keep a copy for editing that you eventually commit to the real value or maybe allow editing the real value with an option to revert?  Do either of those match your scenario?

Boris Rosenow

unread,
Jun 27, 2012, 11:33:56 AM6/27/12
to knock...@googlegroups.com
Hey Ryan

Sadly we will not be able to upgrade to KO 2.1 in the near future and it is quite frustrating to see the problem vanishing with the that version.

I am not sure if I am fully understanding the the first choice in your question, but it definitely is not the latter.
Basically we need to supply a data row, where only those values are populated (either by the original value or modified by the user), which will be editable in the view. Our backend is a SAP system and I underwent major discussions regarding the data schema, but apparently it has to look that way.

Thanks!

rpn

unread,
Jun 27, 2012, 1:41:58 PM6/27/12
to knock...@googlegroups.com
One option would be to use a setTimeout in your binding like:  http://jsfiddle.net/rniemeyer/3NFNR/18/ 

Another option might be to populate the editable value in your subscription rather than in a binding.  
Here is a sample that assumes you are always dealing with just "property": http://jsfiddle.net/rniemeyer/3NFNR/20/ 
Here is a sample that would look for any observables on the original data and copy them over: http://jsfiddle.net/rniemeyer/3NFNR/21/ 

Boris Rosenow

unread,
Jun 28, 2012, 4:42:12 AM6/28/12
to knock...@googlegroups.com
I already tried the setTimeout option once and I think we got timing problems there with some other functions, which are utilizing the observable's value. But I will try it again for further specification and will get back to you.

I would really like to populate the values in the subscription, but that would not be an option, as we get data rows with multiple properties and only the properties, which are used in the view, should be populated. It's very strange and I don't understand the reasons for that completely, but that's how it is. So the best way to populate the properties would be through a binding, as we would otherwise need lots and lots of code in the subscription to cover our bases.

rpn

unread,
Jun 28, 2012, 9:17:06 AM6/28/12
to knock...@googlegroups.com
OK- if you have more information about the setTimeout issue, then I will be happy to look again.  I don't know how complex that your data is, but this sample showed looping through the top-level observables on an object and copying them in the subscription generically: http://jsfiddle.net/rniemeyer/3NFNR/21/.  You might do more than what is shown in the view, but you could at least cover your bases the generic code to look at all observable properties.

Boris Rosenow

unread,
Jul 2, 2012, 11:12:50 AM7/2/12
to knock...@googlegroups.com
setTimeout does the trick!

I was having some problems in the past with this solution, as other code parts were using the property being set and I could not fully wrap my head around the full amount of such dependencies. After further inspection that amount turned out to be manageable and I wrapped those parts in setTimeouts as well and voilà: Everything works just fine! :)

I was hoping for a more elegant solution, but that seems to be an upgrade to KO2.1. Hopefully, we can do that in the near future, for various reasons! I want control flow bindings! :)

Thanks again Ryan, for your help!
Reply all
Reply to author
Forward
0 new messages