Slow performance with angular material md-select and ng-repeat

11,450

Solution 1

Angular material has very poor performance, because the objects pinned to the scope are huge, which makes the digest cycle very long and inperformant.

You should try it first with the default select and ng-options (DOCS HERE). If this works better for you, I'd suggest using plain html and then use MaterializeCSS to get the look and feel of Material Design.

Solution 2

Yes, making it all plain old html will speed it up, however then you lose all the eye candy. To have the good parts from both of the worlds you can do some basic optimizations.

  1. Do you really need to watch the collection - are the collections going to change and if so can't you trigger a digest then? As you did with the id you can also one-way bind the repeated collection as well.

    ng-repeat="id in ::serviceReferences"


  1. You don't really need all the options preloaded, right? Since you're using angular-material, the default drop-down will be exchanged with multiple elements, to emulate the drop-down behavior. I did just remove the options list, replaced it with the actually selected element and populate the list only when the control has gained focus. See documentation.

Still I agree the angular-material has a poor performance. It simply does not scale well. 1-2 controls work but if you have more then 10 it starts to fail.

PS.: Don't cook the $scope soup!

Solution 3

For a big amount of items in ng-repeat will cause some issues. When angular use ng-repeat to create nested list , a single watcher will be created for each item. Hundreds of watchers will slow down the performance obviously on moible (and IE probably). We used have this issue with ng-repeat, so the best practice is avoid using ng-repeat if you could, create and attach the watcher when you really need to.

So I think the possible solution is, try to use normal for loop instead of ng-repeat.

Share:
11,450
tkarls
Author by

tkarls

Updated on June 07, 2022

Comments

  • tkarls
    tkarls almost 2 years

    I'm writing an enterprise application using angular and angular material and have problem with the performance of a medium sized (in my opinion) form. Especially in IE.

    (Working demo, see https://codepen.io/tkarls/pen/vGrqWv . Klick on the card title and it pauses slightly before opens. Especially using IE and mobile. Desktop chrome works pretty well.)

    The worst offenders in the form seem to be some md-selects with ng-repeat on them.

    <md-select ng-model="form.subchannelId" ng-disabled="vm.readOnly">
        <md-option ng-repeat="id in subchannelIds" value="{{::id}}">{{::id}}</md-option>
    </md-select>
    <md-select ng-model="form.serviceReference" ng-disabled="vm.readOnly">
        <md-option ng-repeat="id in serviceReferences" value="{{::id}}">{{::countryId}}{{::id}}</md-option>
    </md-select>
    <md-select ng-model="form.audioCodec" ng-disabled="vm.readOnly">
        <md-option ng-repeat="audioCodec in audioCodecs | orderBy:'toString()'" value="{{audioCodec}}">{{::systemVariables.encoders.aac[audioCodec].displayName}}</md-option>
    </md-select>
    <md-select ng-model="form.audioSource" ng-disabled="vm.readOnly">
        <md-option ng-repeat="audioSource in audioSources | orderBy:'toString()'" value="{{audioSource}}">{{audioSource}}</md-option>
    </md-select>
    <md-select ng-model="form.padSource" ng-disabled="vm.readOnly">
        <md-option ng-repeat="padSource in padSources | orderBy:'toString()'" value="{{::padSource}}">{{::padSource}}</md-option>
    </md-select>
    <md-select ng-model="form.lang" ng-disabled="!form.generateStaticPty || vm.readOnly">
        <md-option ng-repeat="langKey in langKeys | orderBy:'toString()'" value="{{::langs[langKey]}}">{{::langKey}}</md-option>
    </md-select>
    <md-select ng-model="form.pty" ng-disabled="!form.generateStaticPty || vm.readOnly">
        <md-option ng-repeat="ptyKey in ptyKeys | orderBy:'toString()'" value="{{::ptys[ptyKey]}}">{{::ptyKey}}</md-option>
    </md-select>
    

    The data model looks like:

    $scope.subchannelIds = [0, 1, 2]; //up to 63 in real life
    $scope.serviceReferences = ["000", "001", "002"]; //up to 999 in real life
    $scope.ptys = {
      "No programme type": 0,
      "News": 1,
      "Current Affairs": 2}; //Up to ~30 in real life
    $scope.ptyKeys = Object.keys($scope.ptys);
    $scope.langs = {
      "Unknown": "00",
      "Albanian": "01",
      "Breton": "02"}; //Up to ~100 in real life
    $scope.langKeys = Object.keys($scope.langs);
    

    The other ng-repeats are small with 3-5 items each. I think that a modern browser should handle datasets of this size and render it very quickly. So hopefully I'm doing something wildly wrong with my HTML code. The data is fetched from the server in real life but I do pre-fetch it so once the form is ready to be displayed it is already in the $scope.

    I tried to pre-generate HTML after I fetched the data using normal js loops. And then insert just the html snippet like: {{::preGeneratedHtmlHere}}

    But then angular would not treat it as html but text...

    Any help on how to optimize this is appreciated!

  • tkarls
    tkarls about 8 years
    Right, I did try to reduce the number of watcher by using the {{::id}} syntax. But it does not seem to matter for the inital load time. If doing normal loop. How do I attach the html. ng-bind-html as @Murwa suggests?
  • tkarls
    tkarls about 8 years
    I see, I'll try and convert it to plain html selects and see what happens with the performance
  • David Tao
    David Tao about 8 years
    Yeah, I saw you use ::id which is one-way binding. If you want to get rid of the watchers you need to write the directive yourself and that means a lots of coding.
  • tkarls
    tkarls about 8 years
    I tested it now without any css styling. And in chrome the form is now blazingly fast. And IE is improved but still not good enough.
  • tkarls
    tkarls about 8 years
    I'm selecting this as the accepted answer since doing it greatly improved the performance. However, to get the final user experience good enough I also combined ng-if and ng-show. So that ng-if = true on moseover and then ng-show = true on click. That created very rapid forms in both explorer and chrome!
  • tkarls
    tkarls about 8 years
    I've explained my technique here: stackoverflow.com/a/36795321/1226268 for anyone finding this on google.
  • rainabba
    rainabba about 7 years
    This made just enough difference that the timing is acceptable for me. I didn't have a large number of items and actually thought the animation timing might just have been too slow, but this JUST brought the performance to an acceptable point and works since my lists won't be updating.
  • p0stm
    p0stm almost 7 years
    Actually the objects are not necessary huge, it depends on what you define. Also the digest cycle is all about updates, while the original question complains about initial (compile + link) performance. As I wrote in my answer stackoverflow.com/questions/36764414/… this is because md-select, compiles all options. This can be deferred until the dropdown has been clicked. Fine-tune it by getting the list on focus maybe.