我有一个基于数组的ngOptions选择。这个数组可以改变。
如果新数组值不包含所选选项值,则selectController将选项值设置为undefined。有办法防止这种情况吗?
Plunker :https://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>
答案 0 :(得分: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以使用功能:
<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
具有上一个下拉列表的选定颜色。
答案 1 :(得分:0)
在脚本中使用以下代码
$scope.makeSelected=function(){
$scope.selectedColor =$scope.colorsReduced[0];
}
只需在缩小的按钮行中添加此函数调用,如下所示
<button ng-click="colors=colorsReduced;makeSelected()">Reduced</button>
这将实现您想要实现的目标。
答案 2 :(得分:0)
使用Jukebox的答案,我最终编写了一个指令,使用modelCtrl。$ formatters来获取初始值。它还提供了将previousValue存储在范围或局部变量中的可能性:
用法:<select .... select-keep>
或<select .... select-keep="previousColor">
指令:
.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));
}
});
}
};
编辑:它有一个缺陷,即使值没有改变,表单也会变脏。我必须在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);
}
}