Skip to content

Commit

Permalink
Strict validators
Browse files Browse the repository at this point in the history
  • Loading branch information
matsko committed May 27, 2014
1 parent b7ec365 commit 5c88424
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 31 deletions.
47 changes: 26 additions & 21 deletions src/ng/directive/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -1762,11 +1762,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @description
* Runs each of the registered validations set on the $validators object.
*/
this.$validate = function() {
ctrl.$$deferValidation = false;
var value = ctrl.$modelValue;
this.$validate = function(modelValue, viewValue) {
modelValue = modelValue || ctrl.$modelValue;
viewValue = viewValue || ctrl.$viewValue;
forEach(ctrl.$validators, function(fn, name) {
ctrl.$setValidity(name, fn(value));
ctrl.$setValidity(name, fn(modelValue, viewValue));
});
};

Expand All @@ -1782,12 +1782,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* usually handles calling this in response to input events.
*/
this.$commitViewValue = function() {
var value = ctrl.$viewValue;
var viewValue = ctrl.$viewValue;
$timeout.cancel(pendingDebounce);
if (ctrl.$$lastCommittedViewValue === value) {
if (ctrl.$$lastCommittedViewValue === viewValue) {
return;
}
ctrl.$$lastCommittedViewValue = value;
ctrl.$$lastCommittedViewValue = viewValue;

// change to dirty
if (ctrl.$pristine) {
Expand All @@ -1798,15 +1798,18 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
parentForm.$setDirty();
}

var modelValue = viewValue;
forEach(ctrl.$parsers, function(fn) {
value = fn(value);
modelValue = fn(modelValue);
});

if (ctrl.$modelValue !== value) {
ctrl.$modelValue = value;
ctrl.$validate();
if (ctrl.$modelValue !== modelValue && (!ctrl.$rawModelValue || ctrl.$rawModelValue != modelValue)) {

ngModelSet($scope, value);
ctrl.$validate(modelValue, viewValue);
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
ctrl.$rawModelValue = ctrl.$valid ? undefined : modelValue;

ngModelSet($scope, ctrl.$modelValue);
forEach(ctrl.$viewChangeListeners, function(listener) {
try {
listener();
Expand Down Expand Up @@ -1880,28 +1883,30 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$

// model -> value
$scope.$watch(function ngModelWatch() {
var value = ngModelGet($scope);
var modelValue = ngModelGet($scope);

// if scope model value and ngModel value are out of sync
if (ctrl.$modelValue !== value) {
if (ctrl.$modelValue !== modelValue && (!ctrl.$rawModelValue || ctrl.$rawModelValue != modelValue)) {

var formatters = ctrl.$formatters,
idx = formatters.length;

ctrl.$modelValue = value;
ctrl.$validate();

var viewValue = modelValue;
while(idx--) {
value = formatters[idx](value);
viewValue = formatters[idx](viewValue);
}

if (ctrl.$viewValue !== value) {
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = value;
ctrl.$validate(modelValue, viewValue);
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
ctrl.$rawModelValue = ctrl.$valid ? undefined : modelValue;

if (ctrl.$viewValue !== viewValue) {
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
ctrl.$render();
}
}

return value;
return modelValue;
});
}];

Expand Down
55 changes: 45 additions & 10 deletions test/ng/directive/inputSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,19 +280,54 @@ describe('NgModelController', function() {
expect(ctrl.$valid).toBe(true);
});

it('should perform validations when $validate() is called', function() {
ctrl.$validators.uppercase = function(value) {
return (/^[A-Z]+$/).test(value);
};

ctrl.$modelValue = 'test';
ctrl.$validate();

expect(ctrl.$valid).toBe(false);

ctrl.$modelValue = 'TEST';
ctrl.$validate();

expect(ctrl.$valid).toBe(true);
});

it('should always perform validations using the parsed model value', function() {
var expectedSpy = jasmine.createSpy('expected value');
var captures;
ctrl.$validators.raw = function() {
captures = arguments;
return captures[0];
};

ctrl.$validators.raw = expectedSpy;
ctrl.$parsers.push(function(value) {
return value.toUpperCase();
});

ctrl.$setViewValue('my-value');

expect(expectedSpy).toHaveBeenCalled();
var data = expectedSpy.mostRecentCall.args[0];
expect(data).toBe('MY-VALUE');
expect(captures).toEqual(['MY-VALUE', 'my-value']);
});

it('should always perform validations using the formatted view value', function() {
var captures;
ctrl.$validators.raw = function() {
captures = arguments;
return captures[0];
};

ctrl.$formatters.push(function(value) {
return value + '...';
});

scope.$apply(function() {
scope.value = 'matias';
});

expect(captures).toEqual(['matias', 'matias...']);
});

it('should only perform validations if the view value is different', function() {
Expand All @@ -311,7 +346,7 @@ describe('NgModelController', function() {
expect(count).toBe(2);
});

it('should perform validations each time the model value changes within a digest', function() {
it('should perform validations twice each time the model value changes within a digest', function() {
var count = 0;
ctrl.$validators.number = function(value) {
count++;
Expand All @@ -325,16 +360,16 @@ describe('NgModelController', function() {
}

val('');
expect(count).toBe(1);
expect(count).toBe(2);

val(1);
expect(count).toBe(2);
expect(count).toBe(3);

val(1);
expect(count).toBe(2);
expect(count).toBe(3);

val('');
expect(count).toBe(3);
expect(count).toBe(4);
});

it('should only validate to true if all validations are true', function() {
Expand Down

0 comments on commit 5c88424

Please sign in to comment.