Reputation: 1625
Say I have a model object with the attribute favoriteColors
{
...
favoriteColors: ['red', 'green', 'blue']
....
}
I expose them to the user with an ng-repeat
<form name="userForm">
...
<ul>
<li ng-repeat="color in user.favoriteColors">
<input type="text" ng-model="color" />
<a href="" ng-click="delete(color)">remove</a>
</li>
</ul>
<a href="" ng-click="add()">Add a new favorite color</a>
...
</form>
I would like to be able to check the validity of the favoriteColors
field doing something like this
<div ng-show="userForm.favoriteColors.$error">
You must have at least one favorite color
</div>
It doesn't seem possible to do this using a built in validator, and I'm not sure on which element I would put a custom directive in order to get the ngModelController
for favoriteColors
.
Upvotes: 3
Views: 8970
Reputation: 2860
There is another way by adding a directive:
/**
* Validator for checking an array length.
* @example
* <input ng-model="user.favoriteColors" validate-length />
*/
app.directive('validateLength', function() {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
// do not set invalid model to undefined, should stay []
ngModel.$options = {
allowInvalid: true
};
scope.$watch(function () { return ngModel.$modelValue && ngModel.$modelValue.length; }, function() {
ngModel.$validate(); // validate again when array changes
});
ngModel.$validators.length = function() {
var arr = ngModel.$modelValue;
if(!arr) { return false; }
return arr.length > 0;
};
}
};
});
Upvotes: 4
Reputation: 221
@Loi Pham, unfortunately I am not allowed to comment, so I have to add a post. I like your approach. However I had to add ng-model to the input to make the validation work:
<input style="display: none" type="number" name="length" readonly ng-model="colors.length" min="1">
Upvotes: 11
Reputation: 137
My solution is add one hidden input tag and binding with length of array
<form name="userForm">
...
<ul>
<li ng-repeat="color in user.favoriteColors">
<input type="text" ng-model="color" />
<a href="" ng-click="delete(color)">remove</a>
</li>
</ul>
<a href="" ng-click="add()">Add a new favorite color</a>
...
<!-- new line -->
<input style="display:none" name="colorsLength" type="number" min=1 value="{{user.favoriteColors.length}}"/>
</form>
So, you can use userForm.colorsLength.$error for your validation. Good luck!
Upvotes: 13
Reputation: 2141
To have validation the way you are requesting, you have to use an ng-model to put your array of colors into the form so that the array can be validated.
Here's a quick example in a plunker where I push a validator on the $parsers pipeline of the ngModelController which will check the colors array length. Keeping the colorRequired directive separate will allow you to have situations where a color is not required. You could also add to that directive so it will take a boolean argument on the attribute so you can decide at run-time if a color should be required.
http://plnkr.co/edit/yFuSXxacSW811WfZqaPC
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.colors = ['red', 'blue', 'green'];
});
app.directive("colorGroup", [function() {
"use strict";
return {
restrict: 'E',
require: 'ngModel',
template: '<ng-form name="userForm">\
<ul>\
<li ng-repeat="color in model">\
<input type="text" ng-model="color" />\
<a href="" ng-click="delete($index)">remove</a>\
</li>\
</ul>\
</ng-form>',
link: function($scope, element, attrs, ngModelCtrl)
{
$scope.$watch(function(){
return ngModelCtrl.$modelValue;
}, function(){
$scope.model = ngModelCtrl.$viewValue;
});
$scope.delete = function(idx)
{
ngModelCtrl.$viewValue.splice(idx, 1);
ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue);
$scope.model = ngModelCtrl.$viewValue;
}
}
}
}]);
app.directive("colorRequired", function() {
"use strict";
return {
restrict: 'A',
require: 'ngModel',
link: function($scope, element, attrs, ngModelCtrl)
{
ngModelCtrl.$setValidity('colorrequired', (ngModelCtrl.$viewValue.length > 0));
ngModelCtrl.$parsers.push(function(viewValue) {
var valid = viewValue.length > 0;
ngModelCtrl.$setValidity('colorrequired', valid);
return valid ? viewValue : undefined;
});
}
}
});
Upvotes: -1