Fel
Fel

Reputation: 4828

Optimizing Angular component with lots of subcomponents

A while ago, I developed a grid component to use in an Angular application. Each Grid has a number of rows, each of them being a Row component, and each Row has cells, that are also components, named Cell. Each cell can have a number of "objects" that basically are div tags with some styling and event listeners attached. Visually:

components layout

The reason of this design was the different functionalities that the components should provide to the user. For example, the grid component is responsible of getting the bulk data from the API, parsing it and build as many rows as needed. The row components can be disabled, tagged, moved above/below, create a new row, etc and they receive the data from the parent grid via @Input properties and build the cell components, also using @Input to pass their content. These cell components have any number of objects (these are not components) that can be dragged and dropped, deleted, cloned, etc.

This approach has been working fine until we had to manage big grids. In this case, the application becomes quite sluggish and it's almost unusable. I know that the reason is the enormous number of components being created, with their corresponding event listeners and all the stuff. To avoid this problem, it's mandatory to implement some sort of virtual scrolling, but I can't find how to do it, as the rows can have very different heights. So my question is, is there any mechanism that allows me to "sleep" the created componentes while they're not being displayed on screen? The Grid, Row and Cell components all use the OnPush change detection strategy, but it's being triggered constantly, when the user hovers a cell, an object, etc.

I'm implementing a pagination system, but there are cases when we'd need to display the sequence of all the rows and I don't know if it'll be possible with the current approach.

Any suggestion? Thanks in advance!

Upvotes: 2

Views: 879

Answers (2)

Jeffrey Roosendaal
Jeffrey Roosendaal

Reputation: 7157

I was stuck on the same problem.. a grid like view with a component for each cell. When the number of cells grew, the app essentially crashed.


I managed to solve it like this:

Add a property to the data for each cell, such as readOnly, and set it to true by default:

public items = [
    ...
    {
        _id: 'some-id',
        value: 'some-value',
        readOnly: true
        ...
    },
    ...
];

Then, in your template, do it like this:

<td (mouseover)="item.readOnly=false">

    <ng-container *ngIf="item.readOnly">
        {{ item.value }}
    </ng-container>
    
    <ng-container *ngIf="!item.readOnly">
        <ng-container *ngTemplateOutlet="edit; context: { item: item }">
        </ng-container>
    </ng-container>

</td>

...

<!-- Place template at the root/bottom of the HTML -->
<ng-template #edit let-item="item">
    <app-component [content]="item"></app-component>
</ng-template>

The way this works, is by completely skipping the loading of the component by default, and display a flat value as text. As soon as you hover the cell, it lazy loads a template with the component. Every time the template is called, it returns a new instance of the template, so there can be many different cells active at the same time too.

All the others solutions I found were making use of templateRefs, QueryLists, ComponentLoaders, etc, which adds a whole (unnecessary) layer of complexity. That's not needed at all.


Watch out with deactivating cells with (mouseout) though, since hovering the newly loaded component may count as a mouseout, thus switching back to read-only.

Upvotes: 1

Fel
Fel

Reputation: 4828

After some tests, I've come to the conclusion that I have to refactor the code to integrate the rows and cells into the grid without instantiating them as components. I think that this change will improve the performance a lot, as well as facilitating other tasks as the pagination and the value passing between elements.

It will be a big refactoring, but I believe it's the best solution for a case like this, where the UI has to deal with thousands of components.

Cheers,

Upvotes: 0

Related Questions