AngularJS,ng-repeat,复选框和toggleAll

时间:2015-06-12 22:16:16

标签: angularjs checkbox angularjs-ng-repeat toggle ionic-framework

毫无疑问。通过互联网搜索,您可以轻松找到ng-repeat和复选框如何工作的示例。所有这些示例仅包含几个复选框。但是,您是否尝试创建数百个复选框,然后使用一些切换按钮来选中/取消选中所有复选框?该应用程序变得完全没有响应。在浏览器中它是好的,但在设备上测试应用程序(iPad4,iPad mini等),该应用程序完全没有响应。

我在这里创建了一个Plunker示例:http://plnkr.co/edit/wfa3TIp3BYaPvzX8ehAf?p=preview

尝试使用至少500个条目来测试切换复选框,以便您可以看到延迟。现在的问题是,有什么方法可以提高性能?检查时间轴的记录,这是我得到500个条目的结果(:

19.131 ms Scripting
150.104 ms Rendering
55.543 ms Painting
138.402 ms Other
2.95 s Idle

正如你所看到的,渲染花费了宝贵的时间,我们无法承受失去的那段时间。

HTML:

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width" />
  <title>Ionic Framework Example</title>
  <link href="//code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet"/>
  <link href="index.css" rel="stylesheet"/>
  <script src="//code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
<ion-view view-title="main module">
  <ion-content ng-controller="StartCtrl">
    <div class="list">
      <div class="item item-divider">
        Options
      </div>

      <a class="item item-icon-left" href="#" ng-click="generateEntries()">
        <i class="icon ion-plus-circled"></i>
        Add more entries
      </a>

      <a class="item item-icon-left" href="#" ng-click="clearEntries()">
        <i class="icon ion-trash-b"></i>
        Clear entries
      </a>
      <div class="item item-icon-left" href="#">
        <i class="icon ion-person-stalker"></i>
        Entries in array
        <span class="badge badge-assertive">{{entries.length}}</span>
      </div>
      <li class="item item-checkbox">
        <label class="checkbox">
          <input type="checkbox" ng-model="checkedAll" ng-click="toggleAll()">
        </label>
        Toggle all
      </li>
      <div class="item item-divider">
        Entries
      </div>
      <li class="item item-checkbox" ng-repeat="entry in entries track by $index">
        <label class="checkbox">
          <input type="checkbox" ng-model="entry.checked" name="entry.id">
        </label>
        {{entry.id}} - {{entry.name}}
        <span class="badge badge-light">{{$index}}</span>
      </li>
    </div>
  </ion-content>
</ion-view>

  </body>

</html>

JS:

// Code goes here
var app = angular.module('myApp', []);

app.controller('StartCtrl', function ($scope) {

  // bind data from service
  // this.someData = Start.someData;
  $scope.entries = [];

  $scope.generateEntries = function () {
    var names = ['Mark', 'John', 'Maria', 'Lea', 'Marco'];
    var obj = {};
    for (var i = 0; i < 50; i++) {
      obj = {'id': Math.floor(Math.random() * 10000), 'name': names[Math.floor(Math.random() * names.length)]};
      $scope.entries.push(obj);
    }
  };

  $scope.clearEntries = function () {
    $scope.entries = [];
  };

  $scope.toggleAll = function () {
    for (var i = 0; i < $scope.entries.length; i++) {
      $scope.entries[i].checked = $scope.checkedAll;
    }
  };
});

感谢所有参与此次讨论的人。

2 个答案:

答案 0 :(得分:1)

使用entry in entries track by entry.id代替$index。有500个条目,这改善了我的统计数据:

使用track by $index

65.660 ms Scripting
246.985 ms Rendering
129.748 ms Painting
1.23 s Other
3.31 s Idle

使用track by entry.id

46.534 ms Scripting
30.827 ms Rendering
17.631 ms Painting
226.515 ms Other
3.18 s Idle

答案 1 :(得分:0)

来自@Numyx和@jaycp的回复都非常有帮助。我对代码做了一些改进: 1.我使用track by entry.id而不是track by $index来加速渲染,因为@Numyx说, 2.我已经使用ion-infinite-scroll因此我不会同时加载所有结果(例如5000),但只能加载约。 25.滚动到底部加载更多。 3.我使用了2个数据集。所有条目均为1,视图条目为1。滚动时填充视图条目数组。滚动越多,ALL数组中的条目就越多,添加到视图数组中, 4.我在渲染完成时使用$ timeout来显示$ ionicLoading并隐藏$ ionicLoading,

因此,当使用切换按钮时,现在我们不会将所有条目设为已选中,而只是那些处于可见数组中的条目。

以下是更新后的代码:

<强>的start.html

<ion-view view-title="main module">
  <ion-content>
    <div class="list">
      <div class="item item-divider">
        Options
      </div>

      <a class="item item-icon-left" href="#" ng-click="start.generateEntries()">
        <i class="icon ion-plus-circled"></i>
        Add more entries
      </a>

      <a class="item item-icon-left" href="#" ng-click="start.clearEntries()">
        <i class="icon ion-trash-b"></i>
        Clear entries
      </a>
      <div class="item item-icon-left" href="#">
        <i class="icon ion-person-stalker"></i>
        Entries in array
        <span class="badge badge-assertive">{{start.entries.length}}</span>
      </div>
      <li class="item item-checkbox">
        <label class="checkbox">
          <input type="checkbox" ng-model="start.checkedAll" ng-click="start.toggleAll()">
        </label>
        Toggle all
      </li>
      <div class="item item-divider">
        Entries {{start.entriesView.length}}
      </div>
      <li class="item item-checkbox" ng-repeat="entry in start.entriesView track by entry.id">
        <label class="checkbox">
          <input type="checkbox" ng-model="entry.checked" name="entry.id">
        </label>
        {{entry.id}} - {{entry.name}}
        <span class="badge badge-light">{{$index}}</span>
      </li>
      <ion-infinite-scroll
      on-infinite="start.loadMore()"
      ng-if="start.canLoadMore()"
      immediate-check="false"
      distance="1%">
      </ion-infinite-scroll>
    </div>
  </ion-content>
</ion-view>

开始-ctrl.js

'use strict';
angular.module('main')
.controller('StartCtrl', function (Utility, $timeout, $scope) {

  // bind data from service
  // this.someData = Start.someData;
  this.entries = [];
  this.entriesView = [];
  this.numEntriesToCreate = 5000;
  this.numEntriesToAdd = 25;
  var self = this;

  $scope.$on('$stateChangeSuccess', function () {
    console.log('START');
    self.loadMore();
  });

  /**
   * generate more entries
   */
  this.generateEntries = function () {
    var names = ['Gregor', 'Mathias', 'Roland', 'Jonas', 'Marco'];
    var obj = {};

    for (var i = 0; i < self.numEntriesToCreate; i++) {
      obj = {'id': Math.random() + Math.random() * 10000, 'name': names[Math.floor(Math.random() * names.length)]};
      self.entries.push(obj);
    }
  };

  this.clearEntries = function () {
    self.entries = [];
  };

  this.toggleAll = function () {
    self.startLoading();

    console.log(self.checkedAll);

    // we wait for spinner to appear (500ms), then start..
    $timeout(function () {
      for (var i = 0; i < self.entriesView.length; i++) {
        self.entriesView[i].checked = self.checkedAll;
      }

      self.finishedLoading();

    }, 500);
  };

  this.loadMore = function () {
    if (self.canLoadMore()) {
      // self.startLoading();
      $timeout(function () {
        self.entriesView = self.entriesView.concat(
          self.entries.slice(self.entriesView.length, self.entriesView.length + self.numEntriesToAdd) // exact items from our original entries
        );

        $scope.$broadcast('scroll.infiniteScrollComplete');
        // self.finishedLoading();
      }, 500);
    }
    //
  };

  this.canLoadMore = function () {
    return (self.entriesView < self.entries) ? true : false;
  };

  this.startLoading = function () {
    Utility.startTimer();
    Utility.showLoading();
  };

  this.finishedLoading = function () {
    $timeout(function () {
      Utility.hideLoading();
      console.log('execution took ' + Utility.endTimer() + 'ms.');
    });
  };

  console.log('init. creating ' + self.numEntriesToCreate + ' entries');
  self.generateEntries();

});

<强>效用serv.js

'use strict';
angular.module('main')
.service('Utility', function ($ionicLoading) {
  this.opt = {
    startTime: null
  };
  this.showLoading = function () {
    $ionicLoading.show({template: '<ion-spinner></ion-spinner>'});
  };
  this.hideLoading = function () {
    $ionicLoading.hide();
  };
  this.startTimer = function () {
    this.opt.startTime = new Date().getTime();
  };
  this.endTimer = function () {
    return ((this.opt.startTime) ? new Date().getTime() - this.opt.startTime : null);
  };
});

我还发布了完整的example on GitHub。项目是使用generator-m生成的。您只需克隆github并使用gulp watch命令运行它。