checkbox and radio now working

This commit is contained in:
Misko Hevery 2010-03-25 13:01:08 -07:00
parent f29f6a47c4
commit b814c79b58
7 changed files with 142 additions and 133 deletions

View file

@ -10,6 +10,7 @@ Lexer.OPERATORS = {
'null':function(self){return null;},
'true':function(self){return true;},
'false':function(self){return false;},
'undefined':noop,
'+':function(self, a,b){return (a||0)+(b||0);},
'-':function(self, a,b){return (a||0)-(b||0);},
'*':function(self, a,b){return a*b;},

View file

@ -12,8 +12,10 @@ function Scope(initialState, name) {
'$parent': initialState,
'$watch': bind(self, self.addWatchListener),
'$eval': bind(self, self.eval),
// change name to onEval?
'$addEval': bind(self, self.addEval)
'$bind': bind(self, bind, self),
// change name to autoEval?
'$addEval': bind(self, self.addEval),
'$updateView': bind(self, self.updateView)
});
if (name == "ROOT") {
self.state['$root'] = self.state;

View file

@ -71,7 +71,7 @@ JQLite.prototype = {
(function dealoc(element){
jqClearData(element);
for ( var i = 0, children = element.childNodes; i < children.length; i++) {
dealoc(children[0]);
dealoc(children[i]);
}
})(this[0]);
},
@ -86,9 +86,11 @@ JQLite.prototype = {
eventHandler = bind[type];
if (!eventHandler) {
bind[type] = eventHandler = function() {
var value = false;
foreach(eventHandler.fns, function(fn){
fn.apply(self, arguments);
value = value || fn.apply(self, arguments);
});
return value;
};
eventHandler.fns = [];
addEventListener(element, type, eventHandler);
@ -98,10 +100,9 @@ JQLite.prototype = {
},
trigger: function(type) {
var cache = this.data('bind');
if (cache) {
(cache[type] || noop)();
}
var evnt = document.createEvent('MouseEvent');
evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
this[0].dispatchEvent(evnt);
},
click: function(fn) {

View file

@ -1,4 +1,4 @@
function scopeAccessor(scope, element) {
function modelAccessor(scope, element) {
var expr = element.attr('name'),
farmatterName = element.attr('ng-format') || NOOP,
formatter = angularFormatter(farmatterName);
@ -14,7 +14,7 @@ function scopeAccessor(scope, element) {
};
}
function domAccessor(element) {
function valueAccessor(element) {
var validatorName = element.attr('ng-validate') || NOOP,
validator = angularValidator(validatorName),
required = element.attr('ng-required'),
@ -41,135 +41,67 @@ function domAccessor(element) {
};
}
function checkedAccessor(element) {
var domElement = element[0];
return {
get: function(){ return !!domElement.checked; },
set: function(value){ domElement.checked = !!value; }
};
}
function radioAccessor(element) {
var domElement = element[0];
return {
get: function(){ return domElement.checked ? domElement.value : null; },
set: function(value){ domElement.checked = value == domElement.value; }
};
}
function noopAccessor() { return { get: noop, set: noop }; }
var NG_ERROR = 'ng-error',
NG_VALIDATION_ERROR = 'ng-validation-error',
TEXT_META = ['', 'keyup change'],
INPUT_META = {
'text': TEXT_META,
'textarea': TEXT_META,
'hidden': TEXT_META,
'password': TEXT_META
textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined),
INPUT_TYPE = {
'text': textWidget,
'textarea': textWidget,
'hidden': textWidget,
'password': textWidget,
'button': buttonWidget,
'submit': buttonWidget,
'reset': buttonWidget,
'image': buttonWidget,
'checkbox': inputWidget('click', modelAccessor, checkedAccessor, false),
'radio': inputWidget('click', modelAccessor, radioAccessor, undefined)
// 'select-one': [null, 'change'],
// 'select-multiple': [[], 'change'],
// 'file': [{}, 'click']
};
function inputWidget(meta) {
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());
function inputWidget(events, modelAccessor, viewAccessor, initValue) {
return function(element) {
var scope = this,
model = modelAccessor(scope, element),
view = viewAccessor(element),
action = element.attr('ng-action') || '';
var value = view.get() || initValue;
if (isDefined(value)) model.set(value);
element.bind(events, function(){
model.set(view.get());
scope.$eval(action);
});
this.$watch(scope.get, dom.set);
} : 0;
scope.$watch(model.get, view.set);
};
}
angularWidget('INPUT', function input(element){
return inputWidget(INPUT_META[lowercase(element[0].type)]);
});
angularWidget('TEXTAREA', function(){
return inputWidget(INPUT_META['text']);
});
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
//widget related
//ng-validate, ng-required, ng-formatter
//ng-error
//ng-scope ng-controller????
// <input type="text" name="bla" ng-action=""> -> <ng:textinput name="" ng-action=""/>
angular.widget("inputtext", function(element) {
var expression = element.attr('name');
var formatter = this.formatter(element.attr('formatter'));
var validator = this.validator(element.attr('validator'));
function validate(value) {
var error = validator(element);
if (error) {
element.addClass("ng-error");
scope.markInvalid(this); //move out of scope
} else {
scope.clearInvalid(this);
}
}
element.keyup(this.withScope(function(){
this.$evalSet(expression, formatter.parse(element.val()));
validate(element.val());
}));
return {watch: expression, apply: function(newValue){
element.val(formatter.format(newValue));
validate(element.val());
}};
});
angular.widget("inputfile", function(element) {
});
angular.widget("inputradio", function(element) {
});
// <ng:colorpicker name="chosenColor" >
angular.widget("colorpicker", function(element) {
var name = element.attr('datasource');
var formatter = this.formatter(element.attr('ng-formatter'));
element.colorPicker(this.withScope(function(selectedColor){
this.$evalSet(name, formatter.parse(selectedColor));
}));
return function(){
this.$watch(expression, function(cmyk){
element.setColor(formatter.format(cmyk));
});
return function(element) {
this.$eval(element.attr('ng-init')||'');
(INPUT_TYPE[lowercase(element[0].type)] || noop).call(this, element);
};
});
angular.widget("template", function(element) {
var srcExpression = element.attr('src');
var self = this;
return {watch:srcExpression, apply:function(src){
$.load(src, function(html){
self.destroy(element);
element.html(html);
self.compile(element);
});
}};
angularWidget('TEXTAREA', function(){
return textWidget;
});
/**
*
* {
* withScope: //safely executes, with a try/catch. applies scope
* compile:
* widget:
* directive:
* validator:
* formatter:
*
*
* config:
* loadCSS:
* loadScript:
* loadTemplate:
* }
*
**/

View file

@ -52,6 +52,16 @@ LexerTest.prototype.testTokenizeAString = function(){
assertEquals(tokens[i].string, 'd"e');
};
LexerTest.prototype.testTokenizeUndefined = function(){
var lexer = new Lexer("undefined");
var tokens = lexer.parse();
var i = 0;
assertEquals(tokens[i].index, 0);
assertEquals(tokens[i].text, 'undefined');
assertEquals(undefined, tokens[i].fn());
};
LexerTest.prototype.testTokenizeRegExp = function(){
var lexer = new Lexer("/r 1/");
@ -486,3 +496,10 @@ ParserTest.prototype.testParsingBug = function () {
var scope = new Scope();
assertEquals({a: "-"}, scope.eval("{a:'-'}"));
};
ParserTest.prototype.testUndefined = function () {
var scope = new Scope();
assertEquals(undefined, scope.eval("undefined"));
assertEquals(undefined, scope.eval("a=undefined"));
assertEquals(undefined, scope.get("a"));
};

View file

@ -12,7 +12,7 @@ describe("directives", function(){
};
});
afterEach(function(){
afterEach(function() {
element.remove();
expect(_(jqCache).size()).toEqual(0);
});

View file

@ -1,6 +1,6 @@
describe("input widget", function(){
var compile, element, scope;
var compile, element, scope, model;
beforeEach(function() {
scope = null;
@ -11,6 +11,7 @@ describe("input widget", function(){
var view = compiler.compile(element)(element);
view.init();
scope = view.scope;
model = scope.state;
};
});
@ -20,8 +21,9 @@ describe("input widget", function(){
});
it('should input-text auto init and handle keyup/change events', function(){
compile('<input type="Text" name="name" value="Misko"/>');
compile('<input type="Text" name="name" value="Misko" ng-action="count = count + 1" ng-init="count=0"/>');
expect(scope.get('name')).toEqual("Misko");
expect(scope.get('count')).toEqual(0);
scope.set('name', 'Adam');
scope.updateView();
@ -30,10 +32,12 @@ describe("input widget", function(){
element.val('Shyam');
element.trigger('keyup');
expect(scope.get('name')).toEqual('Shyam');
expect(scope.get('count')).toEqual(1);
element.val('Kai');
element.trigger('change');
expect(scope.get('name')).toEqual('Kai');
expect(scope.get('count')).toEqual(2);
});
it("should process ng-format", function(){
@ -98,5 +102,57 @@ describe("input widget", function(){
expect(scope.get('name')).toEqual('Kai');
});
it('should call ng-action on button click', function(){
compile('<input type="button" value="Click Me" ng-action="clicked = true"/>');
element.click();
expect(scope.get('clicked')).toEqual(true);
});
it('should type="checkbox"', function(){
compile('<input type="checkbox" name="checkbox" checked ng-action="action = true"/>');
expect(scope.get('checkbox')).toEqual(true);
element.click();
expect(scope.get('checkbox')).toEqual(false);
expect(scope.get('action')).toEqual(true);
element.click();
expect(scope.get('checkbox')).toEqual(true);
});
it('should type="radio"', function(){
compile('<div>' +
'<input type="radio" name="chose" value="A" ng-action="clicked = 1"/>' +
'<input type="radio" name="chose" value="B" checked ng-action="clicked = 2"/>' +
'</div>');
var a = element[0].childNodes[0];
var b = element[0].childNodes[1];
expect(model.chose).toEqual('B');
expect(model.clicked).not.toBeDefined();
model.chose = 'A';
model.$updateView();
expect(a.checked).toEqual(true);
model.chose = 'B';
model.$updateView();
expect(a.checked).toEqual(false);
expect(b.checked).toEqual(true);
expect(model.clicked).not.toBeDefined();
jqLite(a).click();
expect(model.chose).toEqual('A');
expect(model.clicked).toEqual(1);
});
it('should report error on missing field', function(){
});
it('should report error on assignment error', function(){
});
it('should report error on ng-action exception', function(){
});
});