可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm an angular newbie, and I'm stumbling over something in how angular's form validation directives work.
I know that I can fairly easily add directives to individual fields, but I'm trying to add a validation which will compare two form fields (both of which are elements of a model).
Here's a form skeleton:
<form name="edit_form" > <input name="min" type="number" ng-model="field.min"/> <input name="max" type="number" ng-model="field.max"/> </form> <div class="error" ng-show="edit_form.min.$dirty || edit_form.max.$dirty"> <small class="error" ng-show="(what goes here?)"> Min cannot exceed max </small> </div>
In short, I want to write a directive and use it to show/hide this small.error
if min
and max
both have values but min > max
. How can I access both fields inside one directive? Is a directive the right tool for this job?
回答1:
Many ways to skin a cat.
PLUNKER
app.directive('lowerThan', [ function() { var link = function($scope, $element, $attrs, ctrl) { var validate = function(viewValue) { var comparisonModel = $attrs.lowerThan; if(!viewValue || !comparisonModel){ // It's valid because we have nothing to compare against ctrl.$setValidity('lowerThan', true); } // It's valid if model is lower than the model we're comparing against ctrl.$setValidity('lowerThan', parseInt(viewValue, 10) < parseInt(comparisonModel, 10) ); return viewValue; }; ctrl.$parsers.unshift(validate); ctrl.$formatters.push(validate); $attrs.$observe('lowerThan', function(comparisonModel){ // Whenever the comparison model changes we'll re-validate return validate(ctrl.$viewValue); }); }; return { require: 'ngModel', link: link }; } ]);
Usage:
<input name="min" type="number" ng-model="field.min" lower-than="{{field.max}}" /> <span class="error" ng-show="form.min.$error.lowerThan"> Min cannot exceed max. </span>
回答2:
You do not need any directive. Just assign the "min" value of max to min-value. Like:
<input name="min" type="number" ng-model="field.min"/> <input name="max" type="number" ng-model="field.max" min=" {{ field.min }}"/>
And you do not need any customization.
More: you can do min=" {{ field.min + 1}}"
回答3:
Would a simple comparison suit you?
<small class="error" ng-show="field.min > field.max">
I think a directive would be an overkill if your case is just this. If you do not feel comfortable with the view containing application logic, you can export it in a function of the controller:
$scope.isMinMaxInalid = function() { return $scope.field.min > $scope.field.max; };
And the template:
<small class="error" ng-show="isMinMaxInalid()">
回答4:
For me, beyond a feedback message, I needed define the field as invalid, preventing submit. So I gathered some approaches, like @thestewie approach, with a view configuration to gather a solution for dates comparison. I hope can aggregate the solutions that were presented.
The code is in PLUNKER
angular.module('MyApp') .directive('thisEarlierThan', function () { return { require: 'ngModel', restrict: 'A', link: function (scope, elem, attrs, ctrl) { var startDate, endDate; scope.$watch(attrs.ngModel, function (newVal, oldVal, scope) { startDate = newVal; check(); }); scope.$watch(attrs.thisEarlierThan, function (newVal, oldVal, scope) { endDate = newVal; check(); }); var check = function () { if (typeof startDate === 'undefined' || typeof endDate === 'undefined') { return; } if (!validate(startDate)) { startDate = new Date(startDate); if (!validate(startDate)) { return; } } if (!validate(endDate)) { endDate = new Date(endDate); if (!validate(endDate)) { return; } } if (startDate < endDate) { ctrl.$setValidity('thisEarlierThan', true); } else { ctrl.$setValidity('thisEarlierThan', false); } return; }; var validate = function (date) { if (Object.prototype.toString.call(date) === '[object Date]') { if (isNaN(date.getTime())) { return false; } else { return true; } } else { return false; } }; } }; }) ;
回答5:
My version of the directive:
module.directive('greaterThan', function () { return { restrict: 'A', require: 'ngModel', link: function (scope, element, attributes, ngModelController) { var otherValue; scope.$watch(attributes.greaterThan, function (value) { otherValue = value; ngModelController.$validate(); }); ngModelController.$parsers.unshift(function (viewValue) { ngModelController.$setValidity('greaterThan', !viewValue || !otherValue || viewValue > otherValue); return viewValue; }); } }; });
回答6:
You may take a look at https://github.com/nelsonomuto/angular-ui-form-validation
This provides a directive that is preconfigured with an api that exposes the scope and its models to your validator function.
Here is a plunker with the your specific use case: http://plnkr.co/edit/S0rBlS?p=preview
The syntax for the directives validators is as shown in the below example : { errorMessage: 'Cannot contain the number one', validator: function (errorMessageElement, val, attr, element, model, modelCtrl){ /** * The model and modelCtrl(scope) are exposed in the validator function * */ return /1/.test(val) !== true;
} }