mbest / knockout-repeat

REPEAT binding for Knockout
http://mbest.github.io/knockout-repeat/
131 stars 18 forks source link

Templating support in knockout-repeat? #15

Closed LtSchmidt closed 10 years ago

LtSchmidt commented 10 years ago

Hi,

This binding works really much more faster than foreach binding in my case. What I'm missing (or just don't understand), if it is possible to use this binding together with templating?

Currently I have something like

<table>
    <tbody data-bind="template: { name: getProductTemplate, foreach: productList }"></tbody>
</table>

<script type="text/html" id="productType1">
    <tr>
        <td>Product of Type1</td>
        <td>
            <a data-bind="text: $data.name, attr:{href:prodHref}"></a>
        </td>
    </tr>
</script>

<script type="text/html" id="productType2">
    <tr>
        <td>Product of Type2</td>
        <td>
            <span data-bind="text: $data.name"></span>
        </td>
    </tr>
</script>

Is it possible to transform this use case to repeat-binding base?

mbest commented 10 years ago

You can combine Repeat with templates. It works best in cases where the array you're iterating over won't change. If it does change, the Repeat binding will cause the template for each item to re-render, which usually isn't desirable.

Because Repeat acts on the element it's bound to, you'll have to adjust your template organization:

<table>
    <tbody>
        <tr data-bind="repeat: productList" 
            data-repeat-bind="template: { data: $item(), name: getProductTemplate($item()) }"></tr>
    </tbody>
</table>

<script type="text/html" id="productType1">
        <td>Product of Type1</td>
        <td>
            <a data-bind="text: $data.name, attr:{href:prodHref}"></a>
        </td>
</script>

<script type="text/html" id="productType2">
        <td>Product of Type2</td>
        <td>
            <span data-bind="text: $data.name"></span>
        </td>
</script>
LtSchmidt commented 10 years ago

Thank you so much for quick and very helpful response! Indeed, it works that way. I will close this issue now. And well, it was not an issue with the repeat binding. It was more my misunderstanding of usage.

LtSchmidt commented 10 years ago

I would really appreciate, if someone could give me one more advice regarding provided sample.

My problem is (additionally), that the productList is a paged list. It means, at some time, when the next page is clicked the productList content will be completely replaced through the Ajax call.

        var newItems = $.map(data.list, function (item) {
            if (item.Grp) {
                return new GroupModel(item, self);
            }
            return new ProductModel(item);
        });
        self.productList(newItems);

Now, data-binding is replacing rendered content of the table, but as it looks like, removal of all previously rendered table rows is taking same time as the rendering itself.

So, when productList contains previous page (200 elements), the replacing of the content is taking twice as long as when the productList is initially empty.

Is there some way to do the cleanup of table (productList) faster?

@mbest : you said, repeat binding would work best, if the array isn't changing. But it is changing in the way, that the content will be completely replaced (there are no adds/removes of single items). Is there the way to re-render data-bidden table better?

mbest commented 10 years ago

Is it possible for you to replicate this on jsFiddle or Plunker, etc.?

LtSchmidt commented 10 years ago

It seem that the problem is occurring only in case of really complex and many bindings. Those kind of complexity is not easy to replicate on jsFiddle. Recently I've worked on some simpler web page and used almost the same approach as for the complex site. Complex page has about 30 bound fields and is templated. Simpler page is very similar but has only 10 fields and is not templated. Both pages are loading 200 elements.

The simple page is working really at acceptable performance (renders under 1 sec). The complex one needs about 3 secs for rendering and additional 3 secs for cleanup operation, if I'm replacing the content of the bound observableArray.

I will try to reproduce this behavior on jsFiddle, but I'm not sure, when I will be able to do that :-)

mbest commented 10 years ago

Even a simplified example could help me understand the issue.

LtSchmidt commented 10 years ago

Well, I've started to write simplified example of the behavior.

Here you can see the approach how I'm loading/reloading the data http://jsfiddle.net/LtSchmidt/CEf8B/15/

In this simplified example it's not really detectable, that the reload takes more time. First click on "Load next" loads 200 elements into array and second click replaces those 200 with new 200.

My real case has much more complicated bindings (many combinations with visibility, and css-bindings and it is temlated based on advice given by you). I will try to extend the example to make it as near as possible to the real one.

When I'm investigating my performance issues in the profiler, I see in case of "second click" (when the data will be replaced) excessive use of "removeChild()" DOM operation.

So, it's first example. I'll extend it as soon as I'll get new time slot :-)

LtSchmidt commented 10 years ago

Now, after many attempts, I got an example, where the time for destroying bound elements of an table is at least definitely measurable.

http://jsfiddle.net/LtSchmidt/CEf8B/32/

As it looks like, the more interactive data-bound elements are in the table (like buttons or inputs bound to observables/computed values), the more time needs knockout for releasing data-bound metadata during emptying of the data array

In my case, I have very interactive table and the disposing time is comparable with the rendering time of the table.

As it looks like, considerable time will be spent in disposeAllSubscriptionsToDependencies() of the knockout engine.

cleararrayprofile

IE behaves here much worse than FF/Opera.

So, I'm looking for the way to do this re-rendering of the table more performant :-)