Reputation: 43
I got a edit form with a select drop down.
The options of the select are set as
<select name="Status"
ng-model="vm.StatusId"
ng-options="x.Id as x.Name for x in Statuses | filterBy: ['Active']: 1"
>
<option value="" >--Choose status --</option>
</select>
now Some statuses can get marked as Active = 0 from time to time, So the user can not select it for a while. The idea is when they load up the form again they have to select an option which is currently active.
In this case angular selects the option --Choose status -- but the model value remains as is.
How do I set the model value vm.StatusId to null in this scenario( when vm.StatusId is not available in ng-options anymore) with out manually checking if that is available, ie I had a custom directive in my mind where I would check in formatters to check if the modelValue is present in the options and then mark the field as in valid. I cant figure out a way to access the available options nicely.
app.directive('test', ['$log',
function($log) {
return {
restrict: 'A',
priority: 1001,
terminal: false,
require: ['ngModel', 'select'],
link: function link(scope, elem, attrs, ctrl) {
var m = ctrl[0],
s = ctrl[1];
m.$formatters.push(function(modelValue) {
$log.info('formatters ', modelValue);
return modelValue;
});
m.$parsers.push(function(viewValue) {
$log.info('parsers ', viewValue);
return viewValue;
});
}
};
}
]);
Upvotes: 3
Views: 2706
Reputation: 13071
Well, it's been lots of fun to develop this directive
!
This directive ensures that if the value of the ng-model
will be either one of the possible values of the ng-options
or null
. It works both ways:
null
.null
.That's why its name is modelMatchSelectOrNull
.
.directive('modelMatchSelectOrNull',['$parse', function($parse) {
return {
restrict: 'A',
priority: 1001,
terminal: false,
require: ['modelMatchSelectOrNull', 'ngModel', 'select'],
controller: function(){
this.possibleValues = [];
},
link: function link(scope, elem, attrs, ctrls) {
var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
var match = attrs['ngOptions'].match(NG_OPTIONS_REGEXP);
var valuesFn = $parse(match[7]);
var valueName = match[4] || match[6];
var selectAs = / as /.test(match[0]) && match[1];
var selectAsFn = selectAs ? $parse(selectAs) : null;
var keyName = match[5];
var valueFn = $parse(match[2] ? match[1] : valueName);
var viewValueFn = selectAsFn ? selectAsFn : valueFn;
var locals={};
var thisController = ctrls[0];
var ngModelCtrl = ctrls[1];
function callExpression(exprFn, key, value) {
locals[valueName] = value;
if (keyName) locals[keyName] = key;
return exprFn(scope, locals);
}
scope.$watch(attrs['ngModel'], function(newVal, oldVal){
if(!newVal)
return;
if(thisController.possibleValues.indexOf(newVal)==-1)
ngModelCtrl.$setViewValue(null);
});
scope.$watchCollection(valuesFn, function(newVals, oldVals){
thisController.possibleValues=[];
angular.forEach(newVals,function(value, key){
thisController.possibleValues.push(callExpression(viewValueFn, key, value));
});
if(thisController.possibleValues.indexOf(ngModelCtrl.$viewValue)==-1)
ngModelCtrl.$setViewValue(null);
});
}
};
}]);
And you can use it like this:
<select name="Status"
ng-model="vm.StatusId"
ng-options="x.Id as x.Name for x in Statuses | filter:{active:true}"
model-match-select-or-null>
<option value="" >--Choose status --</option>
</select>
Upvotes: 1