Custom binding for when foreach has finished rendering

大兔子大兔子 提交于 2019-12-10 23:18:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!