在集合中进行第二级更改后重新呈现ng-options

use*_*038 15 javascript refresh angularjs ng-options

问题

我有一个组合框,基本上select是一个由复杂对象数组填充的元素ng-options.当我在二级更新集合的任何对象时,此更改不会应用于组合框.

这也记录在AngularJS网站上:

请注意,$watchCollection对象的属性(如果模型是数组,则为集合中的项)进行浅层比较.这意味着更改比对象/集合内的第一级更深的属性不会触发重新呈现.

角度观点

<div ng-app="testApp">
    <div ng-controller="Ctrl">
        <select ng-model="selectedOption"
                ng-options="(selectedOption.id + ' - ' + selectedOption.name) for selectedOption in myCollection track by selectedOption.id">
        </select>
        <button ng-click="changeFirstLevel()">Change first level</button>
        <button ng-click="changeSecondLevel()">Change second level</button>
        <p>Collection: {{ myCollection }}</p>
        <p>Selected: {{ selectedOption }}</p>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

角度控制器

var testApp = angular.module('testApp', []);

testApp.controller('Ctrl', ['$scope', function ($scope) {

    $scope.myCollection = [
        {
            id: '1',
            name: 'name1',
            nested: {
              value: 'nested1'
            }
        }
    ];

    $scope.changeFirstLevel = function() {
        var newElem = {
            id: '1',
            name: 'newName1',
            nested: {
              value: 'newNested1'
            }
        };
        $scope.myCollection[0] = newElem;
    };

    $scope.changeSecondLevel = function() {
        var newElem = {
            id: '1',
            name: 'name1',
            nested: {
              value: 'newNested1'
            }
        };
        $scope.myCollection[0] = newElem;
    };

}]);
Run Code Online (Sandbox Code Playgroud)

你也可以在这个JSFiddle中实时运行它.

我确实理解AngularJS ng-options出于性能原因不会在其中观察复杂的对象.但有没有解决方法,即我可以手动触发重新渲染吗?有些帖子提到$timeout$scope.apply作为解决方案,但我既不能利用也不能利用.

fro*_*975 5

我以前用过的快速黑客是将你的选择放在ng-if中,将ng-if设置为false,然后在$ timeout为0后将其设置回true.这将导致angular重新呈现控件.

或者,您可以尝试使用ng-repeat自己渲染选项.不确定这是否有效.


K S*_*ett 3

是的,它有点丑陋,需要一个丑陋的解决方法。

$timeout解决方案的工作原理是,如果您将该集合设置为 ,则为 AngularJS 提供更改以识别当前摘要周期中的浅层属性已更改[]

下次有机会,通过$timeout,您将其设置回原来的状态,AngularJS 会识别出浅层属性已更改为新的属性并ngOptions相应地更新它。

我在演示中添加的另一件事是在更新集合之前存储当前选定的 ID。$timeout然后,当代码恢复(更新的)集合时,它可以用于重新选择该选项。

演示: http: //jsfiddle.net/4639yxpf/

var testApp = angular.module('testApp', []);

testApp.controller('Ctrl', ['$scope', '$timeout', function($scope, $timeout) {

  $scope.myCollection = [{
    id: '1',
    name: 'name1',
    nested: {
      value: 'nested1'
    }
  }];

  $scope.changeFirstLevel = function() {
    var newElem = {
      id: '1',
      name: 'newName1',
      nested: {
        value: 'newNested1'
      }
    };
    $scope.myCollection[0] = newElem;
  };

  $scope.changeSecondLevel = function() {

    // Stores value for currently selected index.
    var currentlySelected = -1;

    // get the currently selected index - provided something is selected.
    if ($scope.selectedOption) {
      $scope.myCollection.some(function(obj, i) {
        return obj.id === $scope.selectedOption.id ? currentlySelected = i : false;
      });
    }

    var newElem = {
      id: '1',
      name: 'name1',
      nested: {
        value: 'newNested1'
      }
    };
    $scope.myCollection[0] = newElem;

    var temp = $scope.myCollection; // store reference to updated collection
    $scope.myCollection = []; // change the collection in this digest cycle so ngOptions can detect the change

    $timeout(function() {
      $scope.myCollection = temp;
      // re-select the old selection if it was present
      if (currentlySelected !== -1) $scope.selectedOption = $scope.myCollection[currentlySelected];
    }, 0);
  };

}]);
Run Code Online (Sandbox Code Playgroud)