简体   繁体   中英

knockoutjs afterRender function in foreach binding

I'm trying to specify an entrance effect on elements being inserted using a knockoutjs foreach binding. Very simple setup:

myViewModel.myObservableArray.push({enter:function() { ... });

and in the markup:

foreach:{data:myObservableArray, afterRender:enter}

seems like it should work... right? But it doesn't find the enter function on the item. What I've found does work is:

myViewModel.enter = function(something, item) { item.enter(); };

foreach:{data:myObservableArray, afterRender:$root.enter}

adding an enter function to the root view model and binding afterRender to $root.enter. Enter is then passed the item as its second param so can in turn call the item's enter function, but it feels like a hack.

Can anyone explain what's going on here?

Thanks.

EDIT:

To clarify I've created a fiddle .

What this does is very simple, and is covered in more depth in the animated transitions example . It's running a function in the root view model for each dom element that's inserted using the foreach binding.

So the question is: what if I want item specific afterRender, afterAdd or beforeRemove functions? I could see this being useful. Especially if using the template binding to dynamically select a template ( note 4 ). Is there a clean way of doing this? Right now I've got an enter function in the view model's root that simply calls the enter function on the item, but like I said above this feels like a hack.

Nope, this is the way it was designed .

From the Documenation :

Note 3: Using “afterRender”, “afterAdd”, and “beforeRemove”

Sometimes you might want to run custom post-processing logic on the DOM elements generated by your templates. For example, if you're using a JavaScript widgets library such as jQuery UI, you might want to intercept your templates' output so that you can run jQuery UI commands on it to transform some of the rendered elements into date pickers, sliders, or anything else.

Generally, the best way to perform such post-processing on DOM elements is to write a custom binding , but if you really just want to access the raw DOM elements emitted by a template, you can use afterRender.

Pass a function reference (either a function literal, or give the name of a function on your view model ), and Knockout will invoke it immediately after rendering or re-rendering your template.

(Emphasis mine)


As it says, a custom binding is another way to do it, and may be better depending on what that enter() function does.

underscore debounce (_.debounce) is a great solution in such case.

template

data-bind=" template: {foreach:myObservableArray, afterRender: runWhenAllRenderDone }

debounce function will be executed if afterRender is not fired in last 100 milisecond.

var runWhenAllRenderDone = _.debounce(myFunction, 100);

function myFunction(){
    //do some task but do it for once only
}

is't it awesome?

Found another workaround without timeout , this technique is based on virtual element <!-- ko if: $parent.afterRender($index()) --><!-- /ko -->

function ReservationsViewModel() {
    // Editable data
    this.seats = ko.observableArray([
        { name: "Steve", meal: "Standard (sandwich)", price: 343},
        { name: "Steve", meal: "Premium (lobster)", price: 10},
        { name: "Steve", meal: "Ultimate (whole zebra)", price: 290}
    ]);

    this.afterRender = (i) => {
       // checking element rendered is last
       if (i === this.seats().length - 1) { 
           console.log('rendered');
           // our after rendered logic goes here...
       }
    };
}

And it's template is

<tbody data-bind="foreach: seats">
    <tr>
        <td data-bind="text: name"></td>
        <td data-bind="text: meal"></td>
        <td data-bind="text: price"></td>
    </tr>    
    <!-- ko if: $parent.afterRender($index()) --><!-- /ko -->
</tbody>

This extra logic i === this.seats().length - 1 , will check last row is rendered.. then we can execute our afterRender logic inside.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM