0

I'm currently building a table for a fairly complex report. It is working but performance is very poor, building the table takes 4-5s.


The requirements for the table are as follows:

  1. The table's columns are dynamically determined. There is a fixed set of columns to begin with, then a dynamic section, and then another fixed set.
  2. The contents of each cell are also dynamically determined. By this I mean not just that the data for the cell changes, but the way it is presented also changes. Some cells simply output a value, some provide inputs etc.

The current solution works roughly as follows:

  1. The column definitions are built and loaded into the table's scope.
  2. ng-repeat is called over every row, and then over every column

  3. Within each cell I make a call to a special directive to display that cell, which compiles another directive.


This special directive looks like this, it compiles the (programmatically determined) directive for the cell and replaces the 'cellDirective' node's contents:

directives.directive('cellDirective', ['$compile', function ($compile) {

    return {
        link: function (scope, elem, attrs) {
            scope.$watch(
                function (scope) {
                    return scope.$eval(attrs.cellDirective);
                },
                function (value) {
                    // profiler suggests that this is the bottleneck
                    elem.html(value);
                    $compile(elem.contents())(scope);
                }
            )
        }
    }

}]);

And the template calling it:

<tr ng-repeat="rows in rows">
    <td ng-repeat="column in columns">
        <div cell-directive="column.getDirective(row)"></div>
    </td>
</tr>

The problem I'm facing is poor performance. Generating the table takes 4-5 seconds, which is a problem because the user has options to filter the data displayed (including changing the columns) and has to go through the lag every time they do anything. I'm also worried about what will happen with larger data sets.

I've done some digging and found that:

  1. For ~100 rows the application is binding 4500 watches. It seems that the suggested limit is 2000, so obviously I'm well over.
  2. Looking at this in Chrome's profiler, I can see that the bulk of the execution time takes place within the directive compilation step

I'm looking for any suggestions on improving the performance of this table.

I have considered constructing a template for the whole row and compiling it in one step, but haven't been able to successfully implement such a system.

Thanks for any help.

2
  • Have you tried to remove cell-directive attr before $compile? I know it doesn't make "any" sense, but, sometimes things are not as they were told. Commented Feb 24, 2015 at 15:07
  • I'm not sure what you mean, but as I understand it I'm not compiling the element that holds cell-directive, only its children. I can't really remove it as it will change as the data changes. Commented Feb 24, 2015 at 15:09

1 Answer 1

1

There are several things that you can do to decrease watchers while building your table. Here are a few suggestions:

1) One-time bindings on anything that does not need to always be watched (for example, perhaps name fields won't change per id, in which case you could use {{::item.name}}).

2) Use a table library. UI-Grid is pretty popular and currently undergoing plenty of development.

3) Virtualization might help, but at only 100 rows it won't be a great benefit.

For filtering, simply using trackBy on the row repeat can greatly improve time for re-rendering elements.

You also mention that there are several static columns before and after the dynamic ones. Consider pulling those out of your ngRepeat so that they don't have to ne checked on every digest cycle. That would remove hundreds of watchers alone.

Sign up to request clarification or add additional context in comments.

4 Comments

1. I've got some one-time bindings on the go where possible. 2. I'm not sure how useful this will be in my case where I need per-cell directives? 3. I'll look into this, but my problem is actually very similar to the lag their when you change the row count. The ng-repeat suggestion is a good once, I'll see if I can make a set template for the fixed columns.
Just to clarify, the ui-grid library (and possibly others) allow custom templates on a per-cell basis. I'm fairly sure that something along the lines of cellTemplate: '<div cell-directive="col.getDirective(row)"></div>' should work, as long as you can get the getDirective function into each column's definition.
Sorry, I didn't mean support for per-cell directives, more just that a per-cell directive seems to be the source of my performance pain.
I'll look at the source of UI-Grid, see how they do it though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.