angular / material

Material design for AngularJS
https://material.angularjs.org/
MIT License
16.55k stars 3.39k forks source link

select: performance issue when using large sets of options #8861

Open alexkh13 opened 8 years ago

alexkh13 commented 8 years ago

There is a major degrade in performance when assigning a large set of options (1K and above) using ngRepeat, probably the cost of rendering.

It seems that the right solution for this problem is to use mdVirtualRepeat to limit the rendered set of options.

I think it will be nice to have this feature inside mdSelect, but for now I'm trying to implement it with a workaround.

First of all, I've attached a handler when opening the select box (mdOnOpen) in order to broadcast the $md-resize event after the container has shown up (had to add at least 100ms to make sure the container is visible). Besides the bad timeout value, I used an undocumented scope event. I think it is bad practice in general.

Next, I had to explicitly declare how options are selected by specifying ng-selected. Otherwise the display gets pretty messy after the first selection.

The only problem I didn't manage to solve yet is the fact that after closing it tries to find the selected option(s) in the DOM and put the display value inside the label. Probably not a good idea because no one guarantees that the selected element will be in the rendered set of options (thanks to mdVirtualRepeat).

An almost good solution for the last problem is to manipulate the label externally by triggering the setLabelText of the controller upon closing (mdOnClose). What seems to be an almost working solution, I found that the floating label doesn't agree with this solution and overlays the label text on blur. I didn't go any further to try to overcome this.

Here is an almost working codepen: https://codepen.io/alexkh13/pen/XKpvgp

This is the try to fix the selected label as I proposed: https://codepen.io/alexkh13/pen/yJgmdg

Is there an easier solution I miss?

UPDATE: The label float down because mdSelect flags the container with setHashValue(false) because there are no selectedLabels (inputCheckValue method found in select.js:478). I have no idea how to override this with a workaround.

UPDATE 2: You can trick it by accessing the inputContainer controller on blur inside a timeout to make sure you will override the false decision made by mdSelect. Problem solved. Now need to solve scrollTo and searching capabilities. Yay.

crisbeto commented 8 years ago

We have an issue about this at https://github.com/angular/material/issues/8534. The problem is that the dropdown stays active in the background, even when it's not being used. I've tried looking at it, but it's a little complicated to refactor (as you mentioned), because the select queries the DOM in order to determine the selected label.

Can you post your comment in #8534 so it's all tracked in one place?

alexkh13 commented 8 years ago

@crisbeto thanks for bringing it up, but unfortunately this is not the same issue.

Even if you don't pre-render 1K DOM elements per dropdown, you'll still encounter a massive performance degrade when you'll try to interact with a single dropdown. I'm talking about at least 5 seconds of rendering.

In your case, you can try to use virtual repeat to limit the number of table rows which will limit the number of pre-rendered dropdowns. Just a theory. I'll try to look into it.

Splaktar commented 6 years ago

FYI, we have added a new IE11 Performance Guide that should help mitigate this.