Open jelbourn opened 7 years ago
@crisbeto I think we should tackle this the same way we're dealing with tabs- to add an additional API where the options are in an ng-template
so that they aren't eagerly instantiated. Thoughts?
Makes sense, I'll see what I can do.
@jelbourn I did some exploration into it. The problem with using an ng-template
is that we count on the individual options to provide their text which we then use to display inside the trigger. Since they're not in place on load, it means that we won't be able to show the trigger text for preselected options until the first open. We could do something where the user provides the text, but it won't be very elegant.
We could add an extra API to the trigger for that case; the trade-off ends up being simplicity vs performance
We do have the mat-select-trigger
component already which lets you override the trigger.
Thinking about it more, perhaps we need to go the route we took with the table here. Something like
<mat-select-trigger [panel]="panel">
{{panel.selectedOption.name}}
</mat-select-trigger>
<mat-select-panel #panel [dataSource]="myOptions">
<mat-option *matOptionDef="let option; when">{{option.name}}</mat-option>
</mat-select-panel>
It's a pretty dramatic departure from the current API- maybe we consider this for the next major release and instead say that lazily instantiated option-lists need to be explicitly given trigger text.
An alternative approach can be to hand over all rendering to Material. e.g. the user provides the data source and a function that determines what the output should look like:
<mat-select [options]="arrayOfOptions" [displayWith]="functionThatReturnsAString"></mat-select>
The issue there is that you lose the ability to custom-template the options. Even with the datasource approach you lose the ability to control how the options are expanded declaratively, but that might be a reasonable trade-off (we decided it was necessary for the table so it could have control over scrolling)
I have such error for every item in mat-select
zone.js:1666 [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event.
This https://github.com/angular/material2/issues/4221#issuecomment-300097265 helps, and the performance is much better (I have 900 items list)
Any update on this?
I presume it will hopefully resolve #7749 and #4221 aswell.
I've tried the suggestion from #4221 with default passive events but I it hasn't helped the speed of loading my form which is taking around 20 seconds!
Any ideas on roughly when this might be resolved?
I'm running into this same issue with Select and cannot find a workaround. If I have 100s of select options the view hangs on load (setting values in onInit). Anyone found a workaround?
@timharris777 I've worked around it by incrementally searching and loading a subset of the data. Code:
https://github.com/OasisDigital/angular-material-obs-autocomplete
demo:
https://oasisdigital.github.io/angular-material-obs-autocomplete/
What I like about this code: it works, yields a decent user experience, today. What I don't like about it: It doesn't "fix" the problem so much as avoided by implementing a different set of features, and it requires more work and understanding behind the scenes. See demo and demo code.
Was just hit by this. @jelbourn @crisbeto do you think cdk virtual-scroll should be used here now that it exists?
I don't know the specifics, but it sounds like it would work great!
In IE / EDGE if I have a high number of mat-selects on a page, the opening of one of them is very slow. This happens if I have only one option too. Can anyone help me?
Angular 6.1.2 Angular material 6.4.5 Typescript 2.9.2
with material 5 this not happen !!!
@swftvsn I've been working with some MdSelect and MdAutocomplete elements that have large lists (sometimes can reach 25.000 items), and they both become extremely slow (both for opening and searching through them). For example, the ng-select lib has support for both auto-complete and virtual scrolling and on it, these lists behave quite well. So, support for virtual-scroll on MdSelect and MdAutocomplete will be more than welcome.
As suggested by crisbeto this is giving me error.
<mat-select [options]="arrayOfOptions" [displayWith]="functionThatReturnsAString"></mat-select>
Can't bind to 'options' since it isn't a known property of 'mat-select'.
Lazy loading could have other benefits.
For example I'm facing when I have:
<mat-form-field>
<mat-label>{{ type }}</mat-label>
<mat-autocomplete #autocomplete>
<mat-option *ngFor="let option of obsThatMakesHttpRequest | async">
{{ option.DisplayName }}
</mat-option>
</mat-autocomplete>
<input matInput [matAutocomplete]="autocomplete" />
</mat-form-field>
50 times, makes 50 request immediately, rather then one by one when you interact with them.
This is my work around
<mat-form-field>
<mat-label>{{ type }}</mat-label>
<mat-autocomplete #autocomplete>
<ng-template [ngIf]="autocomplete.isOpen">
<mat-option *ngFor="let option of obsThatMakesHttpRequest | async">
{{ option.DisplayName }}
</mat-option>
</ng-template>
</mat-autocomplete>
<input matInput [matAutocomplete]="autocomplete" />
</mat-form-field>
Any news on this?
This is so slow in development, I switched to react along with material-UI for my new project. It offers highly advanced multi-select menus by default no third-party components are needed.
I won't recommend anybody using angular material, it's very slow in development. I got fooled by the 'Angular' keyword like its official UI for angular.
+1
As @jelbourn mentioned before, fixing this in Angular Material would require a breaking API change, but for really bad cases with lots of <mat-option>
s, I've been experimenting with a custom pipe that will let the currently-selected option(s) through until the select is either opened or the page has been idle long enough to load the rest.
import {
Pipe,
PipeTransform,
OnDestroy,
ChangeDetectorRef,
} from '@angular/core';
import { MatSelect } from '@angular/material/select';
import { Subscription } from 'rxjs';
/**
* Initializes only the selected `<mat-option>`s to remove the up-front cost of
* initializing and rendering off-DOM all of the select options, which can
* number in thousands and make initial page rendering slow.
*/
@Pipe({ name: 'lazyOptions', pure: false })
export class LazyOptionsPipe implements PipeTransform, OnDestroy {
private selectedOptionsOnly = true;
private subscription: Subscription;
constructor(
private readonly select: MatSelect,
private readonly cdr: ChangeDetectorRef
) {
this.subscription = this.select.openedChange.subscribe(() => {
this.selectedOptionsOnly = false;
});
// Preload after some time.
requestIdleCallback(
() => {
this.selectedOptionsOnly = false;
this.cdr.markForCheck();
},
{ timeout: 1000 }
);
}
transform<T>(values: T[]): T[] {
if (!this.selectedOptionsOnly) {
return values;
}
return values.filter((value) => {
return this.select.compareWith(value, this.select.value);
});
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
To use, just specify it in your template:
<mat-form-field appearance="fill">
<mat-label>Select an option</mat-label>
<mat-select [(value)]="selected">
<mat-option>None</mat-option>
<mat-option *ngFor="let value of values | lazyOptions" [value]="value"
>{{value}}</mat-option
>
</mat-select>
</mat-form-field>
Bump. Are there any updates on this?
For both
MdSelect
andMdAutocomplete
, the initial render of the component (in the closed state) is very slow even though the panels are closed. We should ensure that any work related to the options is deferred until the panel is opened.To reproduce this, simply modify the demos to render 500
md-select
each with 500 options each (there is no problem if there are 500 selects with 1 option each).