mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-20 00:10:26 +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;},
|
||||
'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;},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
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'),
|
||||
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:
|
||||
* }
|
||||
*
|
||||
**/
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe("directives", function(){
|
|||
};
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
expect(_(jqCache).size()).toEqual(0);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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(){
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue