【问题标题】:Keep selected option when new options array does not contain the value当新选项数组不包含该值时保留选定选项
【发布时间】:2017-06-16 05:40:04
【问题描述】:

我有一个基于数组的 ngOptions 选择。这个数组可以改变。

如果新数组值不包含选定的选项值,则选项值由 selectController 设置为 undefined。有没有办法防止这种情况发生?

Plunkerhttps://plnkr.co/edit/kao3h5ivHXlP1Wrdx1Ib?p=preview

场景:

  • 选择蓝色/红色或绿色
  • 点击缩小为只有黑白选项
  • 看到模型值留空

想要的行为:模型值保持在蓝色/红色或绿色

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
</head>
<body ng-app="selectExample">
  <script>
angular.module('selectExample', [])
  .controller('ExampleController', ['$scope', function($scope) {
    $scope.colorsFull = [
      {id:"bk", name:'black'},
      {id:"w", name:'white'},
      {id:"r", name:'red'},
      {id:"be", name:'blue'},
      {id:"y", name:'yellow'}
    ];
    $scope.colors = $scope.colorsFull;
    $scope.selectedColor =$scope.colorsFull[0];
    $scope.colorsReduced = [
      {id:"bk", name:'black2'},
      {id:"w", name:'white2'}];
  }]);
</script>
<div ng-controller="ExampleController">
  <button ng-click="colors=colorsReduced">Reduced</button>
  <button ng-click="colors=colorsFull">Full</button>
  <br/>
  Colors : {{colors}} 
  <hr/>
  <select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id">
  </select>
  selectedColor:{{selectedColor}}

</div>
</body>
</html>

【问题讨论】:

  • 好吧,我打赌这个对象被设置为未定义,所以它没有什么可追踪的。当你点击减少的颜色时,我会调用一个函数,然后将你的颜色设置为某种空白颜色,我不会处理 html 代码,我会在控制器中处理它......你能有颜色是'空白”
  • 我敢打赌它试图通过 id 找到新颜色,并且该 id 已被删除,因此它发现未定义......

标签: angularjs


【解决方案1】:

您可以通过跟踪在全色下拉列表中选择的颜色并将其插入到减少的颜色数组中来实现此目的。首先,添加一个ng-change 指令,以便我们可以跟踪选择的颜色:

<select ng-model="selectedColor" ng-options="color.name for color in colors track by color.id" ng-change="setColor(selectedColor)">

在你的控制器中:

$scope.setColor = function(color) {
    if(color !== null) {
        // Keep track of the color that is selected
        $scope.previousColor = color;
    }
    else {
        // Changed arrays, keep selected color in model
        $scope.selectedColor = $scope.previousColor;
    }
}  

现在ng-model 在数组更改时设置为正确的颜色,但由于该选项不存在,因此在减少颜色下拉列表中它将显示为空白。因此,我们需要将该选项插入到数组中。但是,在下拉菜单之间来回切换会导致减少的颜色数组继续添加更多选项,我们只想记住我们从完整颜色数组中选择的选项。因此,我们需要创建一组初始颜色,以便在切换时恢复。

// Keep a copy of the original set of reduced colors
$scope.colorsReducedInitial = [
    {id:"bk", name:'black2'},
    {id:"w", name:'white2'}];

最后,我们需要将选中的选项插入到减少的颜色数组中。更改 ng-click 上的 Reduced 按钮以使用功能:

<button ng-click="setColorsReduced()">Reduced</button>

现在,我们可以在将缩减的颜色数组重置为其初始状态后插入选项:

$scope.setColorsReduced = function() {
    // Revert back to the initial set of reduced colors
    $scope.colors = angular.copy($scope.colorsReducedInitial);

    if($scope.previousColor !== undefined) {
        var found = false;
        angular.forEach($scope.colorsReducedInitial, function(value, key) {
            if(value.id == $scope.previousColor.id) {
                found = true;
            }
        });

        // If the id is found, no need to push the previousColor
        if(!found) {
            $scope.colors.push($scope.previousColor);
        }
    }
}

请注意,我们正在循环通过减少的颜色数组,以确保我们不会重复任何颜色,例如黑色或白色。

现在,减少的颜色 ng-model 具有先前下拉菜单的选定颜色。

Updated Plunkr Demo

【讨论】:

  • 这正是我所需要的。除了在初始化时没有调用 ng-change,因此没有设置 previousColor 并且如果你之前按下 reduce 会出错。我将用可能的补充写另一个答案。随意将其合并到您的答案中。如果你这样做,我会选择你的答案。
  • 你是对的,如果你点击减少按钮,previousColor 没有设置。我添加了一项检查以确保在运行循环之前定义了 previousColor。
【解决方案2】:

在脚本中使用下面的代码

$scope.makeSelected=function(){
    $scope.selectedColor =$scope.colorsReduced[0];
  }

只需将这个函数调用添加到如下所示的缩减按钮行中

<button ng-click="colors=colorsReduced;makeSelected()">Reduced</button>

这将完成您想要实现的目标。

【讨论】:

  • 我很抱歉,但我不明白这是如何做到我想要的。它将值替换为 colorsReduced[0]。请看@Jukebox 的回答
【解决方案3】:

使用 Jukebox 的答案,我最终编写了一个指令,使用 modelCtrl.$formatters 来获取初始值。它还提供了将 previousValue 存储在作用域或局部变量中的可能性:

用法:&lt;select .... select-keep&gt;&lt;select .... select-keep="previousColor"&gt;

指令:

 .directive('selectKeep', function($parse) {
    return {
      require: 'ngModel',
      link: function (scope, element, attrs, modelCtrl) {
        var previousValueGetter;
        var previousValueSetter;
        if (attrs.selectKeep) { //use a scope attribute to store the previousValue
              previousValueGetter = $parse(attrs.selectKeep);
              previousValueSetter = previousValueGetter.assign;
        }
        else { //use a local variable to store the previousValue
              var previousValue;
              previousValueGetter = function(s) { return previousValue;};
              previousValueSetter = function(s, v) { previousValue = v;};
        }

        //store the initial value
        modelCtrl.$formatters.push(function(v) {
              previousValueSetter(scope, v);
          return v;
        });

        //get notified of model changes (copied from Jukebox's answer)
        modelCtrl.$viewChangeListeners.push(function() {
          if (modelCtrl.$modelValue !== null) {
            previousValueSetter(scope, modelCtrl.$modelValue);
          } else {
            modelCtrl.$setViewValue(previousValueGetter(scope));
          }
        });
      }
    };

Plunker

编辑:有一个缺陷,即使值不变,表格也会变脏。我不得不在 viewChangeListener 的 else 中添加这些行,但它看起来不太好。有什么想法吗?:

...
} else {
  modelCtrl.$setViewValue(previousValueGetter(scope));
  //set pristine since this change is not a real change
  modelCtrl.$setPristine(true);
  //check if any other modelCtrl is dirty. If not, we will have to put the form as pristine too
  var oneDirty =_.findKey(modelCtrl.$$parentForm, function(otherModelCtrl) {
    return otherModelCtrl && otherModelCtrl.hasOwnProperty('$modelValue') && otherModelCtrl !== modelCtrl && otherModelCtrl.$dirty;
  });
  if (!oneDirty) {
   modelCtrl.$$parentForm.$setPristine(true);
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-01
    相关资源
    最近更新 更多