mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-22 05:11:51 +00:00
checkbox and radio now working
This commit is contained in:
parent
f29f6a47c4
commit
b814c79b58
7 changed files with 142 additions and 133 deletions
|
|
@ -10,6 +10,7 @@ Lexer.OPERATORS = {
|
||||||
'null':function(self){return null;},
|
'null':function(self){return null;},
|
||||||
'true':function(self){return true;},
|
'true':function(self){return true;},
|
||||||
'false':function(self){return false;},
|
'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||0)-(b||0);},
|
'-':function(self, a,b){return (a||0)-(b||0);},
|
||||||
'*':function(self, a,b){return a*b;},
|
'*':function(self, a,b){return a*b;},
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,10 @@ function Scope(initialState, name) {
|
||||||
'$parent': initialState,
|
'$parent': initialState,
|
||||||
'$watch': bind(self, self.addWatchListener),
|
'$watch': bind(self, self.addWatchListener),
|
||||||
'$eval': bind(self, self.eval),
|
'$eval': bind(self, self.eval),
|
||||||
// change name to onEval?
|
'$bind': bind(self, bind, self),
|
||||||
'$addEval': bind(self, self.addEval)
|
// change name to autoEval?
|
||||||
|
'$addEval': bind(self, self.addEval),
|
||||||
|
'$updateView': bind(self, self.updateView)
|
||||||
});
|
});
|
||||||
if (name == "ROOT") {
|
if (name == "ROOT") {
|
||||||
self.state['$root'] = self.state;
|
self.state['$root'] = self.state;
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ JQLite.prototype = {
|
||||||
(function dealoc(element){
|
(function dealoc(element){
|
||||||
jqClearData(element);
|
jqClearData(element);
|
||||||
for ( var i = 0, children = element.childNodes; i < children.length; i++) {
|
for ( var i = 0, children = element.childNodes; i < children.length; i++) {
|
||||||
dealoc(children[0]);
|
dealoc(children[i]);
|
||||||
}
|
}
|
||||||
})(this[0]);
|
})(this[0]);
|
||||||
},
|
},
|
||||||
|
|
@ -86,9 +86,11 @@ JQLite.prototype = {
|
||||||
eventHandler = bind[type];
|
eventHandler = bind[type];
|
||||||
if (!eventHandler) {
|
if (!eventHandler) {
|
||||||
bind[type] = eventHandler = function() {
|
bind[type] = eventHandler = function() {
|
||||||
|
var value = false;
|
||||||
foreach(eventHandler.fns, function(fn){
|
foreach(eventHandler.fns, function(fn){
|
||||||
fn.apply(self, arguments);
|
value = value || fn.apply(self, arguments);
|
||||||
});
|
});
|
||||||
|
return value;
|
||||||
};
|
};
|
||||||
eventHandler.fns = [];
|
eventHandler.fns = [];
|
||||||
addEventListener(element, type, eventHandler);
|
addEventListener(element, type, eventHandler);
|
||||||
|
|
@ -98,10 +100,9 @@ JQLite.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
trigger: function(type) {
|
trigger: function(type) {
|
||||||
var cache = this.data('bind');
|
var evnt = document.createEvent('MouseEvent');
|
||||||
if (cache) {
|
evnt.initMouseEvent(type, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||||
(cache[type] || noop)();
|
this[0].dispatchEvent(evnt);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
click: function(fn) {
|
click: function(fn) {
|
||||||
|
|
|
||||||
176
src/widgets2.js
176
src/widgets2.js
|
|
@ -1,4 +1,4 @@
|
||||||
function scopeAccessor(scope, element) {
|
function modelAccessor(scope, element) {
|
||||||
var expr = element.attr('name'),
|
var expr = element.attr('name'),
|
||||||
farmatterName = element.attr('ng-format') || NOOP,
|
farmatterName = element.attr('ng-format') || NOOP,
|
||||||
formatter = angularFormatter(farmatterName);
|
formatter = angularFormatter(farmatterName);
|
||||||
|
|
@ -14,7 +14,7 @@ function scopeAccessor(scope, element) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function domAccessor(element) {
|
function valueAccessor(element) {
|
||||||
var validatorName = element.attr('ng-validate') || NOOP,
|
var validatorName = element.attr('ng-validate') || NOOP,
|
||||||
validator = angularValidator(validatorName),
|
validator = angularValidator(validatorName),
|
||||||
required = element.attr('ng-required'),
|
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',
|
var NG_ERROR = 'ng-error',
|
||||||
NG_VALIDATION_ERROR = 'ng-validation-error',
|
NG_VALIDATION_ERROR = 'ng-validation-error',
|
||||||
TEXT_META = ['', 'keyup change'],
|
textWidget = inputWidget('keyup change', modelAccessor, valueAccessor, ''),
|
||||||
INPUT_META = {
|
buttonWidget = inputWidget('click', noopAccessor, noopAccessor, undefined),
|
||||||
'text': TEXT_META,
|
INPUT_TYPE = {
|
||||||
'textarea': TEXT_META,
|
'text': textWidget,
|
||||||
'hidden': TEXT_META,
|
'textarea': textWidget,
|
||||||
'password': TEXT_META
|
'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) {
|
function inputWidget(events, modelAccessor, viewAccessor, initValue) {
|
||||||
return meta ? function(element) {
|
return function(element) {
|
||||||
var scope = scopeAccessor(this, element),
|
var scope = this,
|
||||||
dom = domAccessor(element);
|
model = modelAccessor(scope, element),
|
||||||
scope.set(dom.get() || meta[0]);
|
view = viewAccessor(element),
|
||||||
element.bind(meta[1], function(){
|
action = element.attr('ng-action') || '';
|
||||||
scope.set(dom.get());
|
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);
|
scope.$watch(model.get, view.set);
|
||||||
} : 0;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
angularWidget('INPUT', function input(element){
|
angularWidget('INPUT', function input(element){
|
||||||
return inputWidget(INPUT_META[lowercase(element[0].type)]);
|
return function(element) {
|
||||||
});
|
this.$eval(element.attr('ng-init')||'');
|
||||||
|
(INPUT_TYPE[lowercase(element[0].type)] || noop).call(this, element);
|
||||||
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));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
angular.widget("template", function(element) {
|
angularWidget('TEXTAREA', function(){
|
||||||
var srcExpression = element.attr('src');
|
return textWidget;
|
||||||
var self = this;
|
|
||||||
return {watch:srcExpression, apply:function(src){
|
|
||||||
$.load(src, function(html){
|
|
||||||
self.destroy(element);
|
|
||||||
element.html(html);
|
|
||||||
self.compile(element);
|
|
||||||
});
|
|
||||||
}};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* withScope: //safely executes, with a try/catch. applies scope
|
|
||||||
* compile:
|
|
||||||
* widget:
|
|
||||||
* directive:
|
|
||||||
* validator:
|
|
||||||
* formatter:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* config:
|
|
||||||
* loadCSS:
|
|
||||||
* loadScript:
|
|
||||||
* loadTemplate:
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,16 @@ LexerTest.prototype.testTokenizeAString = function(){
|
||||||
assertEquals(tokens[i].string, 'd"e');
|
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(){
|
LexerTest.prototype.testTokenizeRegExp = function(){
|
||||||
var lexer = new Lexer("/r 1/");
|
var lexer = new Lexer("/r 1/");
|
||||||
|
|
@ -486,3 +496,10 @@ ParserTest.prototype.testParsingBug = function () {
|
||||||
var scope = new Scope();
|
var scope = new Scope();
|
||||||
assertEquals({a: "-"}, scope.eval("{a:'-'}"));
|
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"));
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ describe("directives", function(){
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function(){
|
afterEach(function() {
|
||||||
element.remove();
|
element.remove();
|
||||||
expect(_(jqCache).size()).toEqual(0);
|
expect(_(jqCache).size()).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
describe("input widget", function(){
|
describe("input widget", function(){
|
||||||
|
|
||||||
var compile, element, scope;
|
var compile, element, scope, model;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
scope = null;
|
scope = null;
|
||||||
|
|
@ -11,6 +11,7 @@ describe("input widget", function(){
|
||||||
var view = compiler.compile(element)(element);
|
var view = compiler.compile(element)(element);
|
||||||
view.init();
|
view.init();
|
||||||
scope = view.scope;
|
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(){
|
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('name')).toEqual("Misko");
|
||||||
|
expect(scope.get('count')).toEqual(0);
|
||||||
|
|
||||||
scope.set('name', 'Adam');
|
scope.set('name', 'Adam');
|
||||||
scope.updateView();
|
scope.updateView();
|
||||||
|
|
@ -30,10 +32,12 @@ describe("input widget", function(){
|
||||||
element.val('Shyam');
|
element.val('Shyam');
|
||||||
element.trigger('keyup');
|
element.trigger('keyup');
|
||||||
expect(scope.get('name')).toEqual('Shyam');
|
expect(scope.get('name')).toEqual('Shyam');
|
||||||
|
expect(scope.get('count')).toEqual(1);
|
||||||
|
|
||||||
element.val('Kai');
|
element.val('Kai');
|
||||||
element.trigger('change');
|
element.trigger('change');
|
||||||
expect(scope.get('name')).toEqual('Kai');
|
expect(scope.get('name')).toEqual('Kai');
|
||||||
|
expect(scope.get('count')).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should process ng-format", function(){
|
it("should process ng-format", function(){
|
||||||
|
|
@ -98,5 +102,57 @@ describe("input widget", function(){
|
||||||
expect(scope.get('name')).toEqual('Kai');
|
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(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue