aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMisko Hevery2010-03-24 16:13:42 -0700
committerMisko Hevery2010-03-24 16:13:42 -0700
commit0c42eb9909d554807549cd3394e0ea0c715cc2d1 (patch)
tree331c637a5c86b87970166a0936874fec9c6920a6 /src
parent3d3694240034b6841c9fdf2e38a2a7955cb592c7 (diff)
downloadangular.js-0c42eb9909d554807549cd3394e0ea0c715cc2d1.tar.bz2
input[type=text] now works with binding, validation, formatter, required
Diffstat (limited to 'src')
-rw-r--r--src/Angular.js9
-rw-r--r--src/Compiler.js4
-rw-r--r--src/Validators.js24
-rw-r--r--src/jqLite.js34
-rw-r--r--src/widgets2.js71
5 files changed, 108 insertions, 34 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 95f7325a..b76926b9 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -44,6 +44,8 @@ function extensionList(angular, name) {
}
var consoleNode, msie,
+ VALUE = 'value',
+ NOOP = 'noop',
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
slice = Array.prototype.slice,
angular = window['angular'] || (window['angular'] = {}),
@@ -92,6 +94,9 @@ function isObject(value){ return typeof value == 'object';}
function isString(value){ return typeof value == 'string';}
function isArray(value) { return value instanceof Array; }
function isFunction(value){ return typeof value == 'function';}
+function lowercase(value){ return isString(value) ? value.toLowerCase() : value; }
+function uppercase(value){ return isString(value) ? value.toUpperCase() : value; }
+function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; };
function log(a, b, c){
var console = window['console'];
@@ -244,10 +249,6 @@ function outerHTML(node) {
return outerHTML;
}
-function trim(str) {
- return str.replace(/^ */, '').replace(/ *$/, '');
-}
-
function toBoolean(value) {
var v = ("" + value).toLowerCase();
if (v == 'f' || v == '0' || v == 'false' || v == 'no')
diff --git a/src/Compiler.js b/src/Compiler.js
index ba598a43..4423fcef 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -105,9 +105,7 @@ Compiler.prototype = {
templatize: function(element){
var self = this,
- elementName = element[0].nodeName,
- widgets = self.widgets,
- widget = widgets[elementName],
+ widget = self.widgets[element[0].nodeName],
directives = self.directives,
descend = true,
exclusive = false,
diff --git a/src/Validators.js b/src/Validators.js
index b7efcb4a..cdff5e1a 100644
--- a/src/Validators.js
+++ b/src/Validators.js
@@ -1,4 +1,6 @@
foreach({
+ 'noop': noop,
+
'regexp': function(value, regexp, msg) {
if (!value.match(regexp)) {
return msg ||
@@ -7,7 +9,7 @@ foreach({
return null;
}
},
-
+
'number': function(value, min, max) {
var num = 1 * value;
if (num == value) {
@@ -19,40 +21,40 @@ foreach({
}
return null;
} else {
- return "Value is not a number.";
+ return "Not a number";
}
},
-
+
'integer': function(value, min, max) {
var numberError = angularValidator['number'](value, min, max);
if (numberError) return numberError;
if (!("" + value).match(/^\s*[\d+]*\s*$/) || value != Math.round(value)) {
- return "Value is not a whole number.";
+ return "Not a whole number";
}
return null;
},
-
+
'date': function(value, min, max) {
if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) {
return null;
}
return "Value is not a date. (Expecting format: 12/31/2009).";
},
-
+
'ssn': function(value) {
if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) {
return null;
}
return "SSN needs to be in 999-99-9999 format.";
},
-
+
'email': function(value) {
if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) {
return null;
}
return "Email needs to be in username@host.com format.";
},
-
+
'phone': function(value) {
if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) {
return null;
@@ -62,14 +64,14 @@ foreach({
}
return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly.";
},
-
+
'url': function(value) {
if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) {
return null;
}
return "URL needs to be in http://server[:port]/path format.";
},
-
+
'json': function(value) {
try {
fromJson(value);
@@ -78,7 +80,7 @@ foreach({
return e.toString();
}
},
-
+
'asynchronous': function(text, asynchronousFn) {
var stateKey = '$validateState';
var lastKey = '$lastKey';
diff --git a/src/jqLite.js b/src/jqLite.js
index 035a7a1b..2ac3f6c9 100644
--- a/src/jqLite.js
+++ b/src/jqLite.js
@@ -77,22 +77,24 @@ JQLite.prototype = {
},
bind: function(type, fn){
- var element = this[0],
- bind = this.data('bind'),
+ var self = this,
+ element = self[0],
+ bind = self.data('bind'),
eventHandler;
if (!bind) this.data('bind', bind = {});
- eventHandler = bind[type];
- if (!eventHandler) {
- bind[type] = eventHandler = function() {
- var self = this;
- foreach(eventHandler.fns, function(fn){
- fn.apply(self, arguments);
- });
- };
- eventHandler.fns = [];
- addEventListener(element, type, eventHandler);
- }
- eventHandler.fns.push(fn);
+ foreach(type.split(' '), function(type){
+ eventHandler = bind[type];
+ if (!eventHandler) {
+ bind[type] = eventHandler = function() {
+ foreach(eventHandler.fns, function(fn){
+ fn.apply(self, arguments);
+ });
+ };
+ eventHandler.fns = [];
+ addEventListener(element, type, eventHandler);
+ }
+ eventHandler.fns.push(fn);
+ });
},
trigger: function(type) {
@@ -134,6 +136,10 @@ JQLite.prototype = {
return false;
},
+ removeClass: function(selector) {
+ this[0].className = trim((" " + this[0].className + " ").replace(/[\n\t]/g, " ").replace(" " + selector + " ", ""));
+ },
+
addClass: function( selector ) {
if (!this.hasClass(selector)) {
this[0].className += ' ' + selector;
diff --git a/src/widgets2.js b/src/widgets2.js
index b0f467d4..cee0e49e 100644
--- a/src/widgets2.js
+++ b/src/widgets2.js
@@ -1,3 +1,72 @@
+function scopeAccessor(scope, element) {
+ var expr = element.attr('name'),
+ farmatterName = element.attr('ng-format') || NOOP,
+ formatter = angularFormatter(farmatterName);
+ if (!expr) throw "Required field 'name' not found.";
+ if (!formatter) throw "Formatter named '" + farmatterName + "' not found.";
+ return {
+ get: function() {
+ return formatter['format'](scope.$eval(expr));
+ },
+ set: function(value) {
+ scope.$eval(expr + '=' + toJson(formatter['parse'](value)));
+ }
+ };
+}
+
+function domAccessor(element) {
+ var validatorName = element.attr('ng-validate') || NOOP,
+ validator = angularValidator(validatorName),
+ required = element.attr('ng-required'),
+ lastError;
+ required = required || required == '';
+ if (!validator) throw "Validator named '" + validatorName + "' not found.";
+ function validate(value) {
+ var error = required && !trim(value) ? "Required" : validator(value);
+ if (error !== lastError) {
+ if (error) {
+ element.addClass(NG_VALIDATION_ERROR);
+ element.attr(NG_ERROR, error);
+ } else {
+ element.removeClass(NG_VALIDATION_ERROR);
+ element.removeAttr(NG_ERROR);
+ }
+ lastError = error;
+ }
+ return value;
+ }
+ return {
+ get: function(){
+ return validate(element.attr(VALUE));
+ },
+ set: function(value){
+ element.attr(VALUE, validate(value));
+ }
+ };
+}
+
+var NG_ERROR = 'ng-error',
+ NG_VALIDATION_ERROR = 'ng-validation-error',
+ INPUT_META = {
+ 'text': ["", 'keyup change']
+};
+
+angularWidget('INPUT', function input(element){
+ var meta = INPUT_META[lowercase(element.attr('type'))];
+ return meta ? function(element) {
+ var scope = scopeAccessor(this, element),
+ dom = domAccessor(element);
+ scope.set(dom.get() || meta[0]);
+ element.bind(meta[1], function(){
+ scope.set(dom.get());
+ });
+ this.$watch(scope.get, dom.set);
+ } : 0;
+});
+
+
+
+
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
@@ -6,8 +75,6 @@
-
-
//widget related
//ng-validate, ng-required, ng-formatter
//ng-error