使用&$ 39;跟踪$ index'时达到摘要迭代次数在ngRepeat中

时间:2017-10-01 12:44:33

标签: angularjs angularjs-ng-repeat

Angular 1.6.6

有一个带小部件的仪表板。我需要能够在仪表板上安装多个相同类型的小部件。

小部件对象的示例,类型 - 时钟:

"clock": {
  "title": "World Clock",
  "group": "Tools",
  "name": "clock",
  "description": "Display date and time",
  "templateUrl": "widgets/clock/view.html",
  "sizeX": 1,
  "sizeY": 1,
  "refresh": false,
  "config": {
    "title": "World Clock",
    "timePattern": "HH:mm:ss",
    "datePattern": "YYYY-MM-DD",
    "location": "Europe/Amsterdam",
    "showseconds": false
  },
  "api": {}
}

HTML模板

<div gridster="gridsterOptions">
    <ul>
        <li gridster-item="widget" ng-repeat="widget in $ctrl.dashboard.widgets track by $index">
            <p>{{$index}}</p>
            <clock-widget ng-if="widget.name==='clock'" widget=widget
              on-delete="$ctrl.deleteWidget(widget)" on-update="$ctrl.updateWidget(widget)">
            </clock-widget>
        </li> <!-- END gridster-item, widget -->
    </ul>
</div> <!-- END gridsterOptions -->

小工具clock-widgetcomponent

两次添加相同的小部件后,我在浏览器控制台中看到以下错误。

Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!

如果我没有使用track by $index我有重复错误:

Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. 

此外,我尝试为每个小部件id属性添加一个唯一值,并按id跟踪

<li gridster-item="widget" ng-repeat="widget in $ctrl.dashboard.widgets track by widget.id">

我再次遇到Duplicates ...错误。

这里有什么问题?

仪表板组件控制器:

/*global angular*/
import { findIndex, forEach, cloneDeep } from 'lodash';
import widgets from '../data/widgets';

import addWidgetTemplate from '../dialogs/addWidget/addWidget.dialog.template.html';
import addWidgetController from '../dialogs/addWidget/addWidget.dialog.controller';

const injectParams = [
  '$stateParams',
  '$log',
  'storeService',
  'authService',
  'eventbus',
  'EVENTS',
  'SweetAlert',
  '$state',
  'dialogs',
  '$uibModal'
];

const MainDashboardCtrl = function($stateParams, $log, storeService, authService,
  eventbus, EVENTS, SweetAlert, $state, $dialogs, $uibModal) {

  const self = this;

  self.$onInit = function () {
    storeService.get($stateParams.boardID)
      .then(function (_dashboard_) {
        self.dashboard = _dashboard_;

        if (!self.dashboard) {
          self.dashboard = {};
        }
        if (!self.dashboard.widgets) {
          self.dashboard.widgets = [];
        }

        const currentUser = authService.getCurrentLoginUser();
        if (currentUser.permissions && currentUser.permissions.indexOf('admins') > -1) {
          self.dashboardEditDisable = false;
        }

        if (status.uuid && status.uuid != currentUser.uuid) {
          self.dashboardEditDisable = true;
        } else {
          self.dashboardEditDisable = false;
        }
      })
      .catch(function (error) {
        $log.error('[mainDashboardCtrl]', error);
      });
  };

  self.deleteWidget = function (widget) {
    const index = findIndex(self.dashboard.widgets, w => w.name === widget.name);
    self.dashboard.widgets.splice(index, 1);
  };

  self.updateWidget = function (widget) {
    const index = findIndex(self.dashboard.widgets, w => w.name === widget.name);
    self.dashboard.widgets[index] = cloneDeep(widget);
  };

  // add widget
  self.addWidget = function () {
    $uibModal
      .open({
        controllerAs: '$ctrl',
        controller: addWidgetController,
        template: addWidgetTemplate,
        resolve: {
          widgets: function () {
            return widgets;
          }
        }
      })
      .result.then(function (widget) {
        self.dashboard.widgets.push(widget);
      });
  };

};

MainDashboardCtrl.$inject = injectParams;
export default MainDashboardCtrl;

仪表板模板:

<div class="search-results" access="users" access-permission-type="AtLeastOne">
    <div class="header-action-area">
        <h1 class="lath">
            <small class="text-header-sip">
                <i class="fa fa-h-square" aria-hidden="true" style="margin-right:5px;"></i>
                {{$ctrl.dashboard.name}}
            </small> <!-- END text-header-sip -->
            <button ng-disabled="dashboardEditDisable" ng-click="$ctrl.addWidget()" class="btn btn-whiter btn-primary"
                type="submit" ng-mouseover="hoverA=true" ng-mouseout="hoverA=false">
                <i title="Add New Widget" class="glyphicon glyphicon-plus"></i>
            </button>
        </h1> <!-- END lath -->
    </div> <!-- END header-action-area -->
    <div gridster="gridsterOptions">
        <ul>
            <li gridster-item="widget" ng-repeat="widget in $ctrl.dashboard.widgets">
                <clock-widget ng-if="widget.controllerAs=='clock'" widget=widget
                  on-delete="$ctrl.deleteWidget(widget)" on-update="$ctrl.updateWidget(widget)">
                </clock-widget>
            </li> <!-- END gridster-item, widget -->
        </ul>
    </div> <!-- END gridsterOptions -->
</div> <!-- search-results -->

小组件组件控制器:

import controller from './clock-widget.settings.controller.js';
import template from '../templates/clock-widget.settings.template.html';

import 'angular-clock/dist/angular-clock.css';
import '../style/clock-widget.css';

import timezones from '../data/timezones';

const injectParams = ['$scope', '$timeout', '$uibModal', '$log'];
const ClockWidgetCtrl = function($scope, $timeout, $uibModal, $log) {
  const self = this;

  const initLocation = function (widget) {
    self.timezones = timezones;
    self.gmtOffset = timezones[widget.config.location];
    self.displayLocation = widget.config.location.split('/')[1].toUpperCase();
  };

  self.delete = function () {
    self.onDelete({ widget: self.widget });
  };

  self.update = function (widget) {
    initLocation(widget);
    self.onUpdate({ widget });
  };

  self.openSettings = function () {
    $uibModal
      .open({
        controllerAs: '$ctrl',
        controller,
        template,
        resolve: {
          widget: function () {
            return self.widget;
          },
          timezones: function () {
            return self.timezones;
          }
        }
      })
      .result.then(function (widget) {
        self.update(widget);
      });
  };

  self.$onInit = function () {
    initLocation(self.widget);
  };

};

ClockWidgetCtrl.$inject = injectParams;
export default ClockWidgetCtrl;

1 个答案:

答案 0 :(得分:0)

ngRepeat指令doesn't like相同的对象在数组中重复。

在仪表板组件控制器MainDashboardCtrl中,a reference指向导入的widgets对象,其中clock对象已解析为$uibModal模态的输入。

import widgets from '../data/widgets';
...
$uibModal
  .open({
    ...
    resolve: {
      widgets: function () {
        return widgets;
      }
    }
...

作为模态的结果,返回对clock小部件的引用。然后将它添加到self.dashboard.widgets数组中,该数组已包含与clock对象相同的引用。

  ...
  .result.then(function (widget) {
    self.dashboard.widgets.push(widget);
  });
  ... 

我们需要的是添加一个添加到self.dashboard.widgets数组的新唯一对象。

例如,clonning clock对象:

  ...
  .result.then(function (widget) {
    self.dashboard.widgets.push(cloneDeep(widget));
  });
  ...