I have a Knockout custom binding handler that I want to call the foreach bindings functionality on within it and then call a callback function afterwards. I keep getting a "Uncaught Error: You cannot apply bindings multiple times to the same element. " error now as I try to do this.
My custom binding is pretty simple (typescript):
/// <reference path="knockout.d.ts" />
ko.bindingHandlers["postForeach"] = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (!allBindingsAccessor().postForeachCallback)
throw "Callback not defined for postForeach binding!";
//call foreach init functionality
ko.bindingHandlers['foreach'].init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
//call foreach update functionality
ko.bindingHandlers['foreach'].update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
//call callback
allBindingsAccessor().postForeachCallback();
}
};
Is there something I am missing in constructing this?
Thanks!
EDIT:
Callback Function
self.populateMainContentWindow = function () {
var dataTable = $(this.tableId).dataTable();
dataTable.fnDestroy();
// create the datatable
var actualTable = this.jQuery(this.tableId);
if (actualTable.length == 0) {
return false;
}
// create the data table with options
var newDataTable = actualTable.dataTable(this.options);
// always set the width afterwards
actualTable.css("width", "100%");
};
Data Bind Signature (which is within a 'with' binding):
postForeach: array, postForeachCallback: $parent.viewModel().populateMainContentWindow
I have a Knockout custom binding handler that I want to call the foreach bindings functionality on within it and then call a callback function afterwards. I keep getting a "Uncaught Error: You cannot apply bindings multiple times to the same element. " error now as I try to do this.
My custom binding is pretty simple (typescript):
/// <reference path="knockout.d.ts" />
ko.bindingHandlers["postForeach"] = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (!allBindingsAccessor().postForeachCallback)
throw "Callback not defined for postForeach binding!";
//call foreach init functionality
ko.bindingHandlers['foreach'].init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
//call foreach update functionality
ko.bindingHandlers['foreach'].update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
//call callback
allBindingsAccessor().postForeachCallback();
}
};
Is there something I am missing in constructing this?
Thanks!
EDIT:
Callback Function
self.populateMainContentWindow = function () {
var dataTable = $(this.tableId).dataTable();
dataTable.fnDestroy();
// create the datatable
var actualTable = this.jQuery(this.tableId);
if (actualTable.length == 0) {
return false;
}
// create the data table with options
var newDataTable = actualTable.dataTable(this.options);
// always set the width afterwards
actualTable.css("width", "100%");
};
Data Bind Signature (which is within a 'with' binding):
postForeach: array, postForeachCallback: $parent.viewModel().populateMainContentWindow
Share
Improve this question
edited Sep 18, 2013 at 4:37
nobody
asked Sep 13, 2013 at 22:37
nobodynobody
8,29913 gold badges62 silver badges98 bronze badges
4
- I haven't used TypeScript, which you appear to be using, but it looks like your binding is calling it's own init again. Have you tried putting a console.log in there to see if it is calling it's own init? – PW Kad Commented Sep 14, 2013 at 1:26
- Please create a JSFiddle which repros your issue! Your code with Michael answer works fine: jsfiddle/94epu – nemesv Commented Sep 16, 2013 at 7:38
-
What is
this
inpopulateMainContentWindow
? Based on the code, it would be the binding object returned fromallBindingsAccessor
, which won't havetableId
,jQuery
, oroptions
. So that function shouldn't be working. – Michael Best Commented Sep 16, 2013 at 20:15 -
If your binding is
foreach: BatchDefinitions, postForeach: { postForeachCallback: $parent.etlViewModel().populateMainContentWindow }
, it won't work. Your other version,postForeach: BatchDefinitions, postForeachCallback: parent.etlViewModel().populateMainContentWindow
looks like it should work. – Michael Best Commented Sep 16, 2013 at 20:17
3 Answers
Reset to default 6Knockout uses the return value of init
to determine whether it should process the element's descendants. You can either just return the value of the foreach.init
function or specifically return { controlsDescendantBindings: true }
from your init
function:
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (!allBindingsAccessor().postForeachCallback)
throw "Callback not defined for postForeach binding!";
//call foreach init functionality
return ko.bindingHandlers['foreach'].init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
Reference: http://knockoutjs./documentation/custom-bindings-controlling-descendant-bindings.html
Variant 1
I think your current binding looks like
data-bind="foreach: someArray, postForeach: ..."
You can setup value
for foreach
binding inside postForeach
binding. e.g:
data-bind="postForeach :
{postForeachCallback : function()
{
alert('After')
},
foreach: someArray}" // this is your old foreach
binding:
ko.bindingHandlers["postForeach"] = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (!valueAccessor().postForeachCallback)
throw "Callback not defined for postForeach binding!";
//call foreach init functionality
return ko.bindingHandlers['foreach'].init(element, valueAccessor().foreach, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
//call foreach update functionality
ko.bindingHandlers['foreach'].update(element, valueAccessor().foreach, allBindingsAccessor, viewModel, bindingContext);
//call callback
valueAccessor().postForeachCallback();
}
};
JSFiddle DEMO
Variant 2
This varian view model (innerViewModel
) inside view model. This view model is binded with with
binding.
Now data-bind
lookls like
data-bind="postForeach : true, postForeachCallback : $parent.postCallback, foreachEx: values"
View looks like
<div data-bind="with: innerViewModel">
<ul data-bind="postForeach : true, postForeachCallback : $parent.postCallback, foreachEx: values">
<li data-bind="text: val"></li>
</ul>
</div>
JSFiddle DEMO
It doesn't look like it has anything to do with the code you posted. Rather, it looks like there are multiple calls to applyBindings, and two of them are hitting the same element.
Example: http://jsfiddle/tlarson/bFKuL/ (look in the console for the error)
var vm = {
Name: "George"
}
ko.applyBindings(vm);
ko.applyBindings(vm);
Markup:
<div data-bind="text:Name"></div>
To get around this, don't call applyBindings on the same element twice. To help you figure out exactly how to do this with your code, we'd need to see more of your code, such as a fiddle that demonstrates the problem.