Setting ViewModel data value in afterRender callback function breaks Knockout.js bindings
I have a very complex jquery template that I bind with javascript-based ViewModel
data via the knockout.js framework. Actually, I have an observableArray
of ViewModel
objects within another ViewModel
that gets bound to my template.
Within my template, I have several DOM elements that trigger certain events as their values change. For instance, I have a radio button set that changes the binding of a select list (as well as enablement) when a value is selected, which in turn clears the value of a textbox (as well as enablement), by altering various observable properties within my bound ViewModel
. These triggers help guide the UX flow for the end-user, as well as ensuring certain v开发者_如何学JAVAalues are saved or cleared as needed. All this works fine.
However...
When I retrieve initial data from my database and populate my ViewModels
, then bind the ViewModels
to the template, the triggers fire, as they should. However, I do not want the underlying observable values cleared due to the triggers firing. So, what I devised is a way to initially set my ViewModel
's IsInitialData
observable value to true
, which my triggers use to determine whether to clear certain values. However, I want to set these properties to false
after my template binding is completed, and before any user interaction is performed.
What made most sense to me was to use the afterRender
callback to set the IsInitialData
property to false
. This way, my triggers would fire normally after the initial data has been bound. My afterRender
callback function looks like this:
function resetIsInitialDataAttribute(nodesArray, dataValue) {
alert(dataValue.IsInitialData()); // Displays 'true'
dataValue.IsInitialData(false);
alert(dataValue.IsInitialData()); // Displays 'false'
}
Interesting, I can read the value fine within the above function, even after setting the value.
However...
Once I set the value in the afterRender
callback function above, my observable binding seems to break. To test, I have placed a div within my template to serialize my observableArray
so I can display all of my ViewModel
's values:
<div data-bind="text: ko.toJSON(locationsViewModel.locations)"></div>
If I don't set the IsInitialData
property within my afterRender
callback, then as I modify my DOM elements that have been bound to my ViewModel
's observable properties, the above div instantly displays the modified values. But, if I do set my IsInitialData
property in the callback function, the above div no longer displays any changed values in my ViewModel
.
Does anyone know why setting my ViewModel
's property within the afterRender
callback function causes problems? Is there an alternative means for me to set the IsInitialData
ViewModel
property after my initial database binding is completed? I looked through the jQuery.tmpl documentation for any sort of callback that I can use that takes place after binding my data, but found nothing usable.
Your problem is essentially a version of this issue: https://github.com/SteveSanderson/knockout/issues/133. Also described here.
During afterRender
the elements are not actually in the DOM yet. Setting the value of an observable in this code (or in a binding) that was just bound to an element will cause the dependentObservable used in that element's binding to be re-evaluated. It will see that the element is not in the DOM and think that it should dispose of itself.
An easy workaround is to set your value in your afterRender
like:
setTimeout(function() { dataValue.IsInitialData(false); }, 0);
This will let the current execution finish at which time your elements will be in the DOM.
精彩评论