seperatio validation and exception handling

This commit is contained in:
Misko Hevery 2010-04-07 17:24:24 -07:00
parent a8aa5af413
commit e0ad7dfcd4
9 changed files with 36 additions and 37 deletions

27
angular-debug.js vendored
View file

@ -34,7 +34,6 @@ var consoleNode,
PRIORITY_WATCH = -1000,
PRIORITY_LAST = 99999,
NOOP = 'noop',
NG_ERROR = 'ng-error',
NG_EXCEPTION = 'ng-exception',
NG_VALIDATION_ERROR = 'ng-validation-error',
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
@ -283,10 +282,10 @@ function elementError(element, type, error) {
}
if (error) {
element.addClass(type);
element.attr(NG_ERROR, error);
element.attr(type, error);
} else {
element.removeClass(type);
element.removeAttr(NG_ERROR);
element.removeAttr(type);
}
}
@ -1222,16 +1221,7 @@ Parser.prototype = {
for ( var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self));
}
var pipeThis = function(){
var _this = this;
foreach(self, function(v, k) {
if (k.charAt(0) == '$') {
_this[k] = v;
}
});
};
pipeThis.prototype = self.self;
return fn.apply(new pipeThis(), args);
return fn.apply(self.state, args);
};
return function(){
return fnInvoke;
@ -2883,12 +2873,17 @@ angularDirective("ng-bind-template", function(expression){
};
});
var REMOVE_ATTRIBUTES = {
'disabled':true,
'readonly':true,
'checked':true
};
angularDirective("ng-bind-attr", function(expression){
return function(element){
this.$onEval(function(){
foreach(this.$eval(expression), function(bindExp, key) {
var value = compileBindTemplate(bindExp).call(this, element);
if (key == 'disabled' && !toBoolean(value)) {
if (REMOVE_ATTRIBUTES[lowercase(key)] && !toBoolean(value)) {
element.removeAttr('disabled');
} else {
element.attr(key, value);
@ -3129,7 +3124,7 @@ function valueAccessor(scope, element) {
required = required || required === '';
if (!validator) throw "Validator named '" + validatorName + "' not found.";
function validate(value) {
var error = required && !trim(value) ? "Required" : validator({self:scope, scope:{get:scope.$get, set:scope.$set}}, value);
var error = required && !trim(value) ? "Required" : validator({state:scope, scope:{get:scope.$get, set:scope.$set}}, value);
if (error !== lastError) {
elementError(element, NG_VALIDATION_ERROR, error);
lastError = error;
@ -3402,7 +3397,7 @@ angularService("$location", function(browser){
angularService("$hover", function(browser) {
var tooltip, self = this, error, width = 300, arrowWidth = 10;
browser.hover(function(element, show){
if (show && (error = element.attr('ng-error'))) {
if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) {
if (!tooltip) {
tooltip = {
callout: jqLite('<div id="ng-callout"></div>'),

View file

@ -10,7 +10,6 @@ var consoleNode,
PRIORITY_WATCH = -1000,
PRIORITY_LAST = 99999,
NOOP = 'noop',
NG_ERROR = 'ng-error',
NG_EXCEPTION = 'ng-exception',
NG_VALIDATION_ERROR = 'ng-validation-error',
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
@ -259,10 +258,10 @@ function elementError(element, type, error) {
}
if (error) {
element.addClass(type);
element.attr(NG_ERROR, error);
element.attr(type, error);
} else {
element.removeClass(type);
element.removeAttr(NG_ERROR);
element.removeAttr(type);
}
}

View file

@ -80,12 +80,17 @@ angularDirective("ng-bind-template", function(expression){
};
});
var REMOVE_ATTRIBUTES = {
'disabled':true,
'readonly':true,
'checked':true
};
angularDirective("ng-bind-attr", function(expression){
return function(element){
this.$onEval(function(){
foreach(this.$eval(expression), function(bindExp, key) {
var value = compileBindTemplate(bindExp).call(this, element);
if (key == 'disabled' && !toBoolean(value)) {
if (REMOVE_ATTRIBUTES[lowercase(key)] && !toBoolean(value)) {
element.removeAttr('disabled');
} else {
element.attr(key, value);

View file

@ -47,7 +47,7 @@ angularService("$location", function(browser){
angularService("$hover", function(browser) {
var tooltip, self = this, error, width = 300, arrowWidth = 10;
browser.hover(function(element, show){
if (show && (error = element.attr('ng-error'))) {
if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) {
if (!tooltip) {
tooltip = {
callout: jqLite('<div id="ng-callout"></div>'),

View file

@ -299,20 +299,20 @@ BinderTest.prototype.testIfTextBindingThrowsErrorDecorateTheSpan = function(){
var span = childNode(doc, 0);
assertTrue(span.hasClass('ng-exception'));
assertEquals('ErrorMsg1', fromJson(span.text()));
assertEquals('"ErrorMsg1"', span.attr('ng-error'));
assertEquals('"ErrorMsg1"', span.attr('ng-exception'));
a.scope.$set('error.throw', function(){throw "MyError";});
a.scope.$eval();
span = childNode(doc, 0);
assertTrue(span.hasClass('ng-exception'));
assertTrue(span.text(), span.text().match('MyError') !== null);
assertEquals('"MyError"', span.attr('ng-error'));
assertEquals('"MyError"', span.attr('ng-exception'));
a.scope.$set('error.throw', function(){return "ok";});
a.scope.$eval();
assertFalse(span.hasClass('ng-exception'));
assertEquals('ok', span.text());
assertEquals(null, span.attr('ng-error'));
assertEquals(null, span.attr('ng-exception'));
};
BinderTest.prototype.testIfAttrBindingThrowsErrorDecorateTheAttribute = function(){
@ -322,14 +322,14 @@ BinderTest.prototype.testIfAttrBindingThrowsErrorDecorateTheAttribute = function
a.scope.$set('error.throw', function(){throw "ErrorMsg";});
a.scope.$eval();
assertTrue('ng-exception', doc.hasClass('ng-exception'));
assertEquals('"ErrorMsg"', doc.attr('ng-error'));
assertEquals('"ErrorMsg"', doc.attr('ng-exception'));
assertEquals('before "ErrorMsg" after', doc.attr('attr'));
a.scope.$set('error.throw', function(){ return 'X';});
a.scope.$eval();
assertFalse('!ng-exception', doc.hasClass('ng-exception'));
assertEquals('before X after', doc.attr('attr'));
assertEquals(null, doc.attr('ng-error'));
assertEquals(null, doc.attr('ng-exception'));
};
@ -474,7 +474,7 @@ BinderTest.prototype.testActionOnAHrefThrowsError = function(){
};
var input = c.node;
input.click();
assertEquals({a:"abc", b:2}, fromJson(input.attr('ng-error')));
assertEquals({a:"abc", b:2}, fromJson(input.attr('ng-exception')));
assertTrue("should have an error class", input.hasClass('ng-exception'));
// TODO: I think that exception should never get cleared so this portion of test makes no sense

View file

@ -78,7 +78,7 @@ describe('scope/model', function(){
var element = jqLite('<div></div>');
var scope = createScope();
scope.$tryEval('throw "myError"', element);
expect(element.attr('ng-error')).toEqual('"myError"'); // errors are jsonified
expect(element.attr('ng-exception')).toEqual('"myError"'); // errors are jsonified
expect(element.hasClass('ng-exception')).toBeTruthy();
});

View file

@ -118,7 +118,7 @@ describe('Validator:asynchronous', function(){
expect(input.hasClass('ng-input-indicator-wait')).toBeTruthy();
fn("myError");
expect(input.hasClass('ng-input-indicator-wait')).toBeFalsy();
expect(input.attr('ng-error')).toEqual("myError");
expect(input.attr('ng-validation-error')).toEqual("myError");
scope.$element.remove();
});

View file

@ -88,7 +88,7 @@ describe("directives", function(){
it('should error on wrong parsing of ng-repeat', function(){
var scope = compile('<ul><li ng-repeat="i dont parse"></li></ul>');
var log = "";
log += element.attr('ng-error') + ';';
log += element.attr('ng-exception') + ';';
log += element.hasClass('ng-exception') + ';';
expect(log).toEqual("\"Expected ng-repeat in form of 'item in collection' but got 'i dont parse'.\";true;");
});

View file

@ -54,36 +54,36 @@ describe("input widget", function(){
it("should process ng-validation", function(){
compile('<input type="text" name="price" value="abc" ng-validate="number"/>');
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-error')).toEqual('Not a number');
expect(element.attr('ng-validation-error')).toEqual('Not a number');
scope.$set('price', '123');
scope.$eval();
expect(element.hasClass('ng-validation-error')).toBeFalsy();
expect(element.attr('ng-error')).toBeFalsy();
expect(element.attr('ng-validation-error')).toBeFalsy();
element.val('x');
element.trigger('keyup');
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-error')).toEqual('Not a number');
expect(element.attr('ng-validation-error')).toEqual('Not a number');
});
it("should process ng-required", function(){
compile('<input type="text" name="price" ng-required/>');
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-error')).toEqual('Required');
expect(element.attr('ng-validation-error')).toEqual('Required');
scope.$set('price', 'xxx');
scope.$eval();
expect(element.hasClass('ng-validation-error')).toBeFalsy();
expect(element.attr('ng-error')).toBeFalsy();
expect(element.attr('ng-validation-error')).toBeFalsy();
element.val('');
element.trigger('keyup');
expect(element.hasClass('ng-validation-error')).toBeTruthy();
expect(element.attr('ng-error')).toEqual('Required');
expect(element.attr('ng-validation-error')).toEqual('Required');
});
it("should process ng-required", function() {
it("should process ng-required2", function() {
compile('<textarea name="name">Misko</textarea>');
expect(scope.$get('name')).toEqual("Misko");