问题
I have an observable array with items bound to an ul
. I need to calculate the width of the entire set of li
items once the items have been added so I can set the width of the ul
element to the total width of the child li
elements.
How can I go about doing this with a foreach
binding?
<div>
<h2 data-bind="visible: items().length">Test</h2>
<ul data-bind="foreach: { data: items, afterAdd: $root.myAfterAdd }">
<li data-bind="text: name"></li>
</ul>
</div>
And the JavaScript:
var viewModel = {
items: ko.observableArray([
{ name: "one" },
{ name: "two" },
{ name: "three" }
]),
myAfterAdd: function(element) {
if (element.nodeType === 1) {
alert("myAfterAdd");
}
},
addItem: function() {
this.items.push({ name: 'new' });
}
};
ko.applyBindings(viewModel);
// once foreach has finished rendering I have to set the width
// of the ul to be the total width of the children!
I tried using afterAdd
so I can 'update' the width of the ul
after each element is added, unfortunately I have discovered that afterAdd
does not fire for the initial items! It will only fire when pushing additional items...
Note that the content inside the li
items will be of unknown width :)
See this fiddle for a sample scenario.
回答1:
I had a similar problem. You can use this to make that initial calculation:
ko.bindingHandlers.mybinding {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var foreach = allBindings().foreach,
subscription = foreach.subscribe(function (newValue) {
// do something here (e.g. calculate width/height, use element and its children)
subscription.dispose(); // dispose it if you want this to happen exactly once and never again.
});
}
}
回答2:
After much 'googling' I have found the solution. The way to achieve this with a foreach is too register a custom event handler:
ko.bindingHandlers.runafter = {
update: function (element, valueAccessor, allBindingsAccessor) {
var value = valueAccessor();
setTimeout(function (element, value) {
if (typeof value != 'function' || ko.isObservable(value))
throw 'run must be used with a function';
value(element);
}, 0, element, value);
}
};
This handler will fire whenever an update occurs (or just once if value is not an observable).
More importantly it will fire once the foreach loop has completed!
See here.
回答3:
You could do something like this :
var viewModel = {
items: ko.observableArray([
{ name: "one" },
{ name: "two" },
{ name: "three" }
]),
myAfterAdd: function(event) {
if (event.originalEvent.srcElement.nodeType === 1) {
alert("myAfterAdd");
}
},
addItem: function() {
this.items.push({ name: 'new' });
}
};
$('#bucket').bind('DOMNodeInserted DOMNodeRemoved', viewModel.myAfterAdd);
ko.applyBindings(viewModel);
Which bucket is the ul element
See fiddle
I hope it helps
来源:https://stackoverflow.com/questions/16592237/custom-binding-for-when-foreach-has-finished-rendering