I have scoured the internet trying to figure out why the proprietary AngularJS validation is not working when using jQuery.Select2 and the ui-select2 directive - is it a bug??
Edit: ok I've discovered it is only tagging inputs this is occurring, my Select drop downs and text inputs are working fine.
Here's the fiddle http://jsfiddle.net/whiteb0x/ftEHu/
Html:
<form name="myForm" ng-controller="Ctrl">
userType: <input ui-select2="version1" class="input-xlarge" type="hidden" name="input" ng-model="userType" required>
<span class="error" ng-show="myForm.input.$error.required">Required</span><br>
<tt>userType = {{userType}}</tt><br>
<tt>myForm.input.$valid = {{myForm.input.$valid}} wtf???????!!!</tt><br>
<tt>myForm.input.$error = {{myForm.input.$error}} huh????</tt><br>
<tt>myForm.$valid = {{myForm.$valid}} | please hand me a gun :)</tt><br>
<tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br>
</form>
Javascript:
var app = angular.module('myApp', ['ui.directives']);
function Ctrl($scope) {
$scope.userType = '';
$scope.version1 = {
tags : null
};
}
I believe there's a problem in how select2 directive is passing the value between the ngModel and select2 plugin.
In short, when there are no tags selected, the UI's select2 directive will set the model to [] (empty array), because that's the default value returned by the select2 plugin on empty multi-select inputs (See the docs for select2 Data method)
I'll post an issue on Github to confirm this, but in the meantime here's a workaround:
function Ctrl($scope) {
$scope.userType = null;
$scope.version1 = {
tags : null
};
$scope.$watch('userType', function(userType){
if(angular.isArray(userType) && userType.length === 0){
$scope.userType = '';
}
});
}
Or you could use this custom 'required-multiple' directive which will take empty array into consideration when validating the model:
app.directive('requiredMultiple', function() {
function isEmpty(value) {
return angular.isUndefined(value) || (angular.isArray(value) && value.length === 0) || value === '' || value === null || value !== value;
}
return {
require: '?ngModel',
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.required = true; // force truthy in case we are on non input element
var validator = function(value) {
if (attr.required && (isEmpty(value) || value === false)) {
ctrl.$setValidity('required', false);
return;
} else {
ctrl.$setValidity('required', true);
return value;
}
};
ctrl.$formatters.push(validator);
ctrl.$parsers.unshift(validator);
attr.$observe('required', function() {
validator(ctrl.$viewValue);
});
}
};
});
For tagging, the solution I've been using for this issue is setting ng-minlength="1" on the input. Seems to work well enough.