Angular.js 1.4.7 динамические ng-опции, вызывающие множественные вызовы методов и ошибки infdig

0

У меня есть список выбора, в котором содержимое динамически генерируется из содержимого, выбранного в первом списке выбора. Кажется, я не могу найти, почему я получаю эти ошибки при каждом изменении первого списка выбора:

Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: [[{"msg":"fn:
regularInterceptedExpression","newVal":42,"oldVal":37},{"msg":"fn:       regularInterceptedExpression","newVal":"16","oldVal":"14"}],[{"msg":"fn: regularInterceptedExpression","newVal":47,"oldVal":42},{"msg":"fn: regularInterceptedExpression","newVal":"18","oldVal":"16"}],[{"msg":"fn: regularInterceptedExpression","newVal":52,"oldVal":47},{"msg":"fn: regularInterceptedExpression","newVal":"20","oldVal":"18"}],[{"msg":"fn: regularInterceptedExpression","newVal":57,"oldVal":52},{"msg":"fn: regularInterceptedExpression","newVal":"22","oldVal":"20"}],[{"msg":"fn: regularInterceptedExpression","newVal":62,"oldVal":57},{"msg":"fn: regularInterceptedExpression","newVal":"24","oldVal":"22"}]]
http://errors.angularjs.org/1.4.7/$rootScope/infdig?p0=10&p1=%5B%5B%7B%22ms…Expression%22%2C%22newVal%22%3A%2224%22%2C%22oldVal%22%3A%2222%22%7D%5D%5D

Плюс каждый раз, когда список изменяется, "makeOptions", используемый для создания второго списка, запускается несколько раз за одно изменение списка.

Эти два списка настроены так:

    <select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
    <select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in makeOptions()"></select>

и контроллер:

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

  $scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
  $scope.makeOptionsCallCount = 1;
  $scope.selected = {};


  $scope.makeOptions = function() {

    console.log($scope.makeOptionsCallCount++);

    var options = [];

    for (var i = 0; i < 5; i++) {
        options.push({ label: 'Value = ' + i + $scope.selected.level1, value: i + $scope.selected.level1 });
    }

    return options;
  };

});

Вот пример того, что происходит странно:

http://plnkr.co/edit/mKv7nMjot5XqBj4mkvhR?p=preview

Любые идеи, в которых я ошибся?

ОБНОВИТЬ

Возможно, я упростил пример, когда у меня были проблемы. Решение становится более сложным, потому что у меня есть список элементов, где для каждого элемента вы можете выбрать набор значений. Прошу прощения, я забыл этот элемент, задавая свой первоначальный вопрос.

Я создал новый пример, который более точно отражает это:

http://plnkr.co/edit/I62WepJVLuuqN0YbCafD?p=preview

  • 0
    не использовать функцию в поле зрения
  • 0
    Привет, Гранди, спасибо за твой комментарий. Можешь рассказать о решении для динамического создания связанных данных?
Теги:
angularjs-select

2 ответа

1
Лучший ответ

Когда вы используете внутреннее представление функции, оно выполняется в каждом цикле дайджеста.

Кроме того, угловой добавляет наблюдателя, чтобы вызвать эту функцию, и если вы, например, возвращаете новый массив при каждом вызове - вы идете в бесконечный цикл, потому что [] != []

В вашем случае у вас есть еще одна ошибка: внутри функции, вызываемой в поле зрения, вы меняете переменную, что также показано в поле зрения. Так что для этой переменной также добавлен наблюдатель.

Вы меняете значение в вызове функции, смотрите, как изменилась переменная, снова запустите дайджест, запустите функцию, измените значение, увидите, что переменная изменена и т.д....

Итак, для решения лучше выбрать лучшие варианты сохранения при изменении.

var app = angular.module('demoApp', [])
    
    app.controller('DemoController', function($scope) {
      
      $scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
      $scope.makeOptionsCallCount = 1;
      $scope.selected = {};
      
      
      $scope.makeOptions = function(lvl) {
        
        $scope.options = []; 
        console.log(++$scope.makeOptionsCallCount);

        for (var i = 0; i < 5; i++) {
            $scope.options.push({ label: 'Value = ' + i + lvl, value: i + lvl });
        }
        
      };

    });
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
    <div ng-app="demoApp" ng-controller="DemoController">
      <div>
        
        <h2>Infinite Digest 2</h2>
        
        <select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="makeOptionsCallCount=0;makeOptions(selected.level1)"></select>
        
        <select ng-model="selected.level2" ng-options="opt.value as opt.label for opt in options"></select>

        <br/>
        <br/>
        <div>Level 1: {{selected.level1}}</div>
        <div>Level 2: {{selected.level2}}</div>
        <div>makeOptions() Called: {{makeOptionsCallCount}} times</div>
      </div>
    </div>

ОБНОВИТЬ:
В вашем случае из комментария вы можете воспользоваться тем фактом, что ng-repeat создает собственную область и сохраняет в нем параметры списка, созданные на событии ng-change.

Кроме того, вы можете даже использовать функцию makeOptions.

Для ng-change вы можете передать выражение:

ng-change="optionsLvLs = makeOptions(option.level1)"

здесь, в области ng-repeat для каждого параметра, в событии изменения будут созданы optionsLvLs которые не загрязняют ваш объект-вариант, если это имеет значение.

    var app = angular.module('demoApp', [])
    
    app.controller('DemoController', function($scope) {
      
      $scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
      $scope.selected = {};
      $scope.options = [{ name: "A" }];

      $scope.makeOptions = function(level1) {
        
        var options = [];
        
        for (var i = 0; i < 5; i++) 
        { 
          options.push({ label: 'Value = ' + i + level1, value: i + level1 }); 
        }
        
        return options;
      };

      $scope.addQuery = function(idx) {
        $scope.options.splice(idx + 1, 0, {});
      };

      $scope.removeQuery = function (idx) {
        $scope.options.splice(idx, 1);
      };


    });
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.js"></script>
<div ng-controller="DemoController" ng-app="demoApp">
  <div>

    <h2>Infinite Digest 2</h2>

    <div ng-repeat="option in options">

      <input type="button" value="+" ng-click="addQuery($index)" />
      <input type="button" value="-" ng-click="removeQuery($index)" />

      <select ng-model="option.level1" ng-options="lvl.name as lvl.name for lvl in level1options" ng-change="optionsLvLs = makeOptions(option.level1)"></select>
      <select ng-model="option.level2" ng-options="opt.value as opt.label for opt in optionsLvLs"></select>

      <br/>
      <br/>

    </div>

  </div>

  {{ options }}

</div>
  • 0
    Спасибо за отличный пример, Гранди. Как бы вы справились с этим, если бы у нас было несколько наборов опций, например, так: jsfiddle.net/shanidkv/rnnb32rm/2/light
  • 0
    @Rtype, можешь немного объяснить? я не совсем понимаю, что вы имеете в виду и как это относится к вашей скрипке
Показать ещё 7 комментариев
0

Выполняя следующие изменения, для динамического создания данных не требуется вызывать функцию,

$scope.level1options = [{ name: "A", value: "A" }, { name: "B", value: "B" }, { name: "C", value: "C" }];
$scope.level2options = [0,1,2,3,4];

<select ng-model="selected.level1" ng-options="lvl.name as lvl.name for lvl in level1options"></select>
<select ng-model="selected.level2" ng-options="i+selected.level1 as 'Value = '+i+selected.level1 for i in level2options"></select>

Здесь плункер: http://plnkr.co/edit/GQhnjTpB0sLHoiSnchhn?p=preview

  • 0
    Спасибо за код, мой пример несколько надуманный. Этот пример работает, но если бы код был более сложным, что в моем случае намного сложнее, это стало бы несостоятельным.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню