I am working with system that is using Knockout.js and Jquery Mobile UI combination. One of the issues I ran into was getting Jquery Mobile being applied on elements that have knockout :if
bindings. Initial solution we were using was in page cycle fire events when ko.applyBindings
and ko.valueHasMutated
are being called and then in event handlers calling the following
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
Problem was this sometimes would get called to many times or sometimes someone would forget to trigger required events and would produce same bugs over and over again so business has decided move this mechanism to custom binding with following logic: If element is bound (becomes visible or some other way ends up on a page) then apply JQuery mobile UI. init
of custom binding would be equivalent of ko.applyBindings
and update
equivalent of ko.valueHasMutated
.
It worked fine for when list 'appears' on the page however when pagination or filtering is being used model is not being 're-bound' only viewModel array is being changed and ko.valueHasMutated
is being called. I have just found out that update
is not getting called when value has mutated is being fired. I have done some reading around and this seems to be a known issue however none of the solutions seem to work in my case
ko.bindingHandlers.expandableListJQM = {
init: function (element, valueAccessor) {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
var val = ko.utils.unwrapObservable(valueAccessor());
if (val!= undefined) {
valueAccessor().subscribe(function (element, valueAccessor) {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
});
}
},
update: function (element, valueAccessor) {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
}
};
even if I am subscribing to valueAccessor()
like in example function (element, valueAccessor) {...
it's not of much use to me as variable that is being passed as element
is empty array and not something usefull.
I could start applying .collapsibleset().collapsibleset
by using JQuery selector as I know what ID list has, but it does not feel like a proper solution, we might as well leave that hack we had previously.
How can I on ko.valueHasMutated
make knockout.js custom binding update
fire with proper arguments being passed?
For the time being I am using this solution as I know someone is going to propose it. But this is only a hack and I would prefer a 'proper' solution from someone.
ko.bindingHandlers.expandableListJQM = {
init: function (element, valueAccessor) {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
var element = $(element);
var val = ko.utils.unwrapObservable(valueAccessor());
if (val!= undefined) {
valueAccessor().subscribe(function() {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
});
}
},
update: function (element, valueAccessor) {
$(element).find('div[data-onthemovecollapsible="true"]:not(div[data-role="collapsible"])').attr("data-role", "collapsible");
$(element).collapsibleset().collapsibleset("refresh");
}
};