1

I want to use md-select to display a lot of data to the user. When opening the md-select, the browser freezes. So as a solution i want to use md-virtual repeat inside md-select for better performance. But the code isn(t just working out for me. Am I doing something wrong here?

<md-input-container flex>
  <label>test</label>
  <md-select ng-model="$ctrl.haha">
    <md-virtual-repeat-container id="vertical-container">
      <md-option md-virtual-repeat="item in ctrl.infiniteItems" md-on-demand ng-value="item" ng-selected="$first">
        {{item}}
      </md-option>
    </md-virtual-repeat-container>
  </md-select>
</md-input-container>

#vertical-container {
  height: 256px;
}

this.infiniteItems = {
  numLoaded_: 0,
  toLoad_: 0,
  items: [],

  // Required.
  getItemAtIndex(index) {
    if (index > this.numLoaded_) {
      this.fetchMoreItems_(index);
      return null;
    }
    return this.items[index];
  },

  // Required.
  getLength() {
    return this.numLoaded_ + 5;
  },

  fetchMoreItems_(index) {
    if (this.toLoad_ < index) {
      this.toLoad_ += 20;
      for (let i = 0; i < 10000; i++) {
        this.items.push(i);
      }
      this.numLoaded_ = this.toLoad_;
    }
  }
};
Andrei Roba
  • 2,156
  • 2
  • 16
  • 33
Dev Db
  • 740
  • 4
  • 10
  • 30

2 Answers2

1

In order for this combination to work you need to make sure your virtual-repeat-container is kept in sync. If you write a simple 'refresh' function that gets called on open select:

function () {
    return $timeout(function () {
        $scope.$broadcast("$md-resize");
    }, 100);
};

it should be enough. Working example:

angular.module("app", ["ngMaterial", "ngSanitize", "ngAnimate"])
  .controller("MainController", function($scope, $timeout) {

    // refresh virtual container
    $scope.refresh = function() {
      return $timeout(function() {
        $scope.$broadcast("$md-resize");
      }, 100);
    };

    $scope.infiniteItems = {
      _pageSize: 10000,
      toLoad_: 0,
      items: [],

      getItemAtIndex(index) {
        if (index > this.items.length) {
          this.fetchMoreItems_(index);
          return null;
        }
        return this.items[index];
      },

      getLength() {
        return this.items.length + 5;
      },

      fetchMoreItems_(index) {
        if (this.toLoad_ < index) {
          this.toLoad_ += this._pageSize;

          // simulate $http request
          $timeout(angular.noop, 300)
            .then(() => {
              for (let i = 0; i < this._pageSize; i++) {
                this.items.push(i)
              }
            });
        }
      }
    };

  });
#vertical-container {
  height: 256px;  
}
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>select with md-virtual-repeat</title>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.9/angular-material.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-aria.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-sanitize.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.0.9/angular-material.min.js"></script>
  </head>
  <body>
    <div class="main" ng-app="app" ng-controller="MainController" layout="column" layout-align="center center" layout-fill>
      <md-input-container>
        <label>Select an option</label>
        <md-select ng-model="haha" md-on-open="refresh()">
          <md-virtual-repeat-container id="vertical-container">
            <md-option md-virtual-repeat="item in infiniteItems" md-on-demand="" ng-value="item" ng-selected="haha==item">{{item}}</md-option>
          </md-virtual-repeat-container>
        </md-select>
      </md-input-container>
    </div>
    </script>
  </body>

</html>

You can also check out this similar answer.

Andrei Roba
  • 2,156
  • 2
  • 16
  • 33
  • Thanks! It works if i use javaScript, but I am having some problems with converting the Refresh() function to a typescript function... Any idea on how to use the $broadcast in typescript? Because the elements inside the select statement are only shown if I press f12. I think it's because of a bad implementation of $broadcast – Dev Db Nov 25 '17 at 18:59
  • it shouldn't be much different than this example, maybe you forgot to inject `$scope` in your controller class? hard to tell from just the code you posted.. maybe you should take a look at [this](https://stackoverflow.com/a/43542420/5173530) answer or related ones, good luck! – Andrei Roba Nov 25 '17 at 19:30
  • That was the problem.. Thanks again! – Dev Db Nov 25 '17 at 19:47
  • 1
    Is it possible to use filter with md-virtual-repeat? I am trying to use it but getting an error " Duplicate md-option values are not allowed in a select". – pvnarula May 06 '18 at 09:14
0

According to https://github.com/angular/material/issues/10868 this post, different angularjs version has different behaviour. Return $timeout function should have also window.dispatchEvent(new Event('resize')); statement.

Final $timeout function looks like this.

return $timeout(function() {
   $scope.$broadcast("$md-resize");
   window.dispatchEvent(new Event('resize'));
},100);
ultdevchar
  • 132
  • 4