ui-bootstrap typeahead的数组与底层数组不同步

时间:2014-10-20 18:36:12

标签: angularjs angularjs-directive angular-ui-bootstrap

尝试在ng-repeat块中使用UI-Bootstrap typeahead时遇到同步问题。当我的预先输出由另一个指令呈现时,问题出现了。

基本上,我正在尝试在表单中创建一个动态的typeahead数组。用户可以随意添加和删除预先输入。

HTML

<div class='container-fluid' ng-controller="TypeaheadCtrl">
<h4>Array of typeaheads</h4>
<div class="row" ng-repeat="item in items track by $index">
  <ng-form>
    <div class="col-xs-5">
      <autocomplete ng-model="items[$index]" options="options" />
    </div>
    <div class="col-xs-3">
      <button type="button" ng-click="removeItem($index)" class="btn btn-danger">
        Remove Item
      </button>
    </div>
  </ng-form>

</div>
<button type="button" ng-click="addItem()" class="btn btn-primary">
  Add Item
</button>
<pre>{{items}}</pre>
</div>

JS

angular.module('ui.bootstrap.demo', ['ui.bootstrap']);
angular.module('ui.bootstrap.demo').directive('autocomplete', function() {
  return {
    restrict: 'E',
    scope: {
      ngModel: '=',
      options: '='
    },
    controller: function($scope) {
      if ($scope.ngModel) {
        $scope.ngModelName = $scope.ngModel.name;
      }

      $scope.onSelect = function($item, $model, $label) {
        $scope.ngModel = $item;
        $scope.ngModelName = $item.name;
      };
    },
    template: '<input type="text" class="form-control" ng-model="ngModelName" typeahead="o.name for o in options | filter:$viewValue" typeahead-on-select="onSelect($item, $model, $label)" />'

  };
});
angular.module('ui.bootstrap.demo').controller('TypeaheadCtrl', function($scope, $http) {

  $scope.options = [{
    name: 'Alabama',
    type: 'A'
  }, {
    name: 'Alaska',
    type: 'B'
  }, {
    name: 'Arizona',
    type: 'C'
  }, ];
  $scope.items = [];

  $scope.addItem = function() {
    $scope.items.push({});
  };

  $scope.removeItem = function(index) {
    $scope.items.splice(index, 1);
  };

});

这是一个演示此问题的Plunkr:http://plnkr.co/edit/lugLKkgp8GkUf2Kjdewg?p=preview

基本上,添加额外的typeaheads工作正常。我们假设我添加了三个输入并为所有输入选择输入。我的州变成了:

Initial (good) state

但是现在,如果我尝试删除第二个类型(具有&#34; Alaska&#34;选择的那个),我的基础状态会正确更新(items数组只选择了Alabama和Arizona用户界面显示阿拉巴马州和阿拉斯加州。

Bad state after the removal of the middle item

好像每当我删除一个项目时,基础状态都会正确更新,但UI总会删除列表中的最后一项。

有人可以帮我理解这里发生了什么吗?

注意:我有充分的理由在我自己的指令中包装typeahead,但我只是展示了一个简化版本,所以这个问题可能有一个MRE。

1 个答案:

答案 0 :(得分:1)

问题在于:

<div class="row" ng-repeat="item in items track by $index">

由于您执行track by $index - 您没有为模型分配一些唯一值,您只需通过它的id引用数组项。

因此,例如,您只有2 typeahead秒。第一个模型是items[0],第二个模型是items[1]。当您删除第一个 - 引用停留时,但模型本身将被删除。如果你要做一些改变 - everythig将会很好,因为你在items数组中仍然有1个元素,但它已经是另一个模型了。

解决问题 - 只需删除track by $index即可。然后一切都会完美运行,直到你选择2个相同的值。

有关详细信息,请查看ngRepeat documentation

你会在这里看到:

<强> item in items is equivalent to item in items track by $id(item) . This implies that the DOM elements will be associated by item identity in the array.

item in items track by $id(item) . A built in $id() function can be used to assign a unique $$hashKey {{1} }

工作演示:http://plnkr.co/edit/ZdNdb1KkR7s6lOqE9rwq?p=preview

更新

如果您希望所有内容都使用相同的值 - 您只需要在项目中添加一些额外的字段,例如:

property to each item in the array. This property is then used as a key to associated DOM elements with the corresponding item in the array by identity. Moving the same object in array would move the DOM element in the same way in the DOM.

现在您的 $scope.addItem = function() { $scope.items.push({random: +new Date()}); }; 会有所不同,这就是您不需要在item中添加track by的原因。

但是现在你还有一个问题:当你从ng-repeat中选择一些值时 - 这个值会改变。这就是你需要做下一件事的原因:

typeahead

现在,每当您从 $scope.onSelect = function($item, $model, $label) { angular.extend($scope.ngModel, $item); $scope.ngModelName = $item.name; }; 中选择一些值时,一切都会完美无缺。

更新了演示:http://plnkr.co/edit/ZdNdb1KkR7s6lOqE9rwq?p=preview