【问题标题】:AngularJS, ng-repeat, checkboxes and toggleAllAngularJS、ng-repeat、复选框和toggleAll
【发布时间】:2015-08-29 01:06:27
【问题描述】:

毫无疑问。通过互联网搜索,您可以轻松找到有关 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;
    }
  };
});

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

【问题讨论】:

  • 问题:为什么一个页面需要 500 个元素,而在小屏幕的移动设备上也是如此?
  • 这是客户请求@jaycp。他们将在 iPad 上使用上面复制的部分代码(如果你问我,iPad 没有这么小的屏幕)
  • 分页在这种情况下会有所帮助。几乎您使用的任何其他前端框架,即使是原生 JS 来渲染如此大量的元素也需要花费大量时间。

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


【解决方案1】:

@Numyx 和 @jaycp 的两个回复都非常有帮助。我对代码做了一些改进: 1.我使用track by entry.id而不是track by $index,正如@Numyx所说,它加快了渲染速度, 2. 我使用了ion-infinite-scroll,所以我不会一次加载所有结果(例如 5000),但只加载大约。 25.滚动到底部加载更多。 3. 我正在使用 2 个数据集。 1 表示所有条目,1 表示查看条目。当我们滚动时,View Entries 数组被填充。我们滚动的越多,来自 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>

start-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();

});

utility-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 命令运行它。

【讨论】:

    【解决方案2】:

    使用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
    

    【讨论】:

    • 虽然我做了更多改进,但仍被接受为答案,这可以被视为下一个答案。谢谢@Numyx。
    猜你喜欢
    • 2013-01-27
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2016-11-11
    • 2017-09-13
    • 2016-07-26
    相关资源
    最近更新 更多