fixed issue with radio view clobering model if radio was checked.

This commit is contained in:
Misko Hevery 2010-05-13 13:57:39 -07:00
parent 0a288d1db3
commit 22d1464d7a
4 changed files with 176 additions and 129 deletions

View file

@ -97,7 +97,7 @@ function createScope(parent, services, existing) {
$set: bind(instance, setter, instance),
$eval: function $eval(exp) {
if (exp) {
if (exp !== undefined) {
return expressionCompile(exp).apply(instance, slice.call(arguments, 1, arguments.length));
} else {
foreach(evalLists.sorted, function(list) {

View file

@ -155,9 +155,15 @@ function initWidgetValue(initValue) {
function radioInit(model, view, element) {
var modelValue = model.get(), viewValue = view.get(), input = element[0];
input.checked = false;
input.name = this.$id + '@' + input.name;
if (isUndefined(modelValue)) model.set(null);
if (viewValue !== null) model.set(viewValue);
if (isUndefined(modelValue)) {
model.set(modelValue = null);
}
if (modelValue == null && viewValue !== null) {
model.set(viewValue);
}
view.set(modelValue);
}
function inputWidget(events, modelAccessor, viewAccessor, initFn) {

View file

@ -15,133 +15,146 @@ describe('scope/model', function(){
expect(model.$root).toEqual(model);
});
//$eval
it('should eval function with correct this and pass arguments', function(){
var model = createScope();
model.$eval(function(name){
this.name = name;
}, 'works');
expect(model.name).toEqual('works');
});
it('should eval expression with correct this', function(){
var model = createScope();
model.$eval('name="works"');
expect(model.name).toEqual('works');
});
//$watch
it('should watch an expression for change', function(){
var model = createScope();
model.oldValue = "";
var nameCount = 0, evalCount = 0;
model.name = 'adam';
model.$watch('name', function(){ nameCount ++; });
model.$watch(function(){return model.name;}, function(newValue, oldValue){
this.newValue = newValue;
this.oldValue = oldValue;
describe('$eval', function(){
it('should eval function with correct this and pass arguments', function(){
var model = createScope();
model.$eval(function(name){
this.name = name;
}, 'works');
expect(model.name).toEqual('works');
});
model.$onEval(function(){evalCount ++;});
model.name = 'misko';
model.$eval();
expect(nameCount).toEqual(2);
expect(evalCount).toEqual(1);
expect(model.newValue).toEqual('misko');
expect(model.oldValue).toEqual('adam');
});
it('should eval with no arguments', function(){
var model = createScope();
var count = 0;
model.$onEval(function(){count++;});
model.$eval();
expect(count).toEqual(1);
});
//$bind
it('should curry a function with respect to scope', function(){
var model = createScope();
model.name = 'misko';
expect(model.$bind(function(){return this.name;})()).toEqual('misko');
});
//$tryEval
it('should report error on element', function(){
var scope = createScope();
scope.$tryEval('throw "myerror";', function(error){
scope.error = error;
it('should eval expression with correct this', function(){
var model = createScope();
model.$eval('name="works"');
expect(model.name).toEqual('works');
});
it('should do nothing on empty string and not update view', function(){
var model = createScope();
var onEval = jasmine.createSpy('onEval');
model.$onEval(onEval);
model.$eval('');
expect(onEval).wasNotCalled();
});
expect(scope.error).toEqual('myerror');
});
it('should report error on visible element', function(){
var element = jqLite('<div></div>');
var scope = createScope();
scope.$tryEval('throw "myError"', element);
expect(element.attr('ng-exception')).toEqual('"myError"'); // errors are jsonified
expect(element.hasClass('ng-exception')).toBeTruthy();
describe('$watch', function(){
it('should watch an expression for change', function(){
var model = createScope();
model.oldValue = "";
var nameCount = 0, evalCount = 0;
model.name = 'adam';
model.$watch('name', function(){ nameCount ++; });
model.$watch(function(){return model.name;}, function(newValue, oldValue){
this.newValue = newValue;
this.oldValue = oldValue;
});
model.$onEval(function(){evalCount ++;});
model.name = 'misko';
model.$eval();
expect(nameCount).toEqual(2);
expect(evalCount).toEqual(1);
expect(model.newValue).toEqual('misko');
expect(model.oldValue).toEqual('adam');
});
it('should eval with no arguments', function(){
var model = createScope();
var count = 0;
model.$onEval(function(){count++;});
model.$eval();
expect(count).toEqual(1);
});
});
it('should report error on $excetionHandler', function(){
var element = jqLite('<div></div>');
var scope = createScope();
scope.$exceptionHandler = function(e){
this.error = e;
};
scope.$tryEval('throw "myError"');
expect(scope.error).toEqual("myError");
describe('$bind', function(){
it('should curry a function with respect to scope', function(){
var model = createScope();
model.name = 'misko';
expect(model.$bind(function(){return this.name;})()).toEqual('misko');
});
});
describe('$tryEval', function(){
it('should report error on element', function(){
var scope = createScope();
scope.$tryEval('throw "myerror";', function(error){
scope.error = error;
});
expect(scope.error).toEqual('myerror');
});
it('should report error on visible element', function(){
var element = jqLite('<div></div>');
var scope = createScope();
scope.$tryEval('throw "myError"', element);
expect(element.attr('ng-exception')).toEqual('"myError"'); // errors are jsonified
expect(element.hasClass('ng-exception')).toBeTruthy();
});
it('should report error on $excetionHandler', function(){
var element = jqLite('<div></div>');
var scope = createScope();
scope.$exceptionHandler = function(e){
this.error = e;
};
scope.$tryEval('throw "myError"');
expect(scope.error).toEqual("myError");
});
});
// $onEval
describe('$onEval', function(){
it("should eval using priority", function(){
var scope = createScope();
scope.log = "";
scope.$onEval('log = log + "middle;"');
scope.$onEval(-1, 'log = log + "first;"');
scope.$onEval(1, 'log = log + "last;"');
scope.$eval();
expect(scope.log).toEqual('first;middle;last;');
});
it("should eval using priority", function(){
var scope = createScope();
scope.log = "";
scope.$onEval('log = log + "middle;"');
scope.$onEval(-1, 'log = log + "first;"');
scope.$onEval(1, 'log = log + "last;"');
scope.$eval();
expect(scope.log).toEqual('first;middle;last;');
it("should have $root and $parent", function(){
var parent = createScope();
var scope = createScope(parent);
expect(scope.$root).toEqual(parent);
expect(scope.$parent).toEqual(parent);
});
});
it("should have $root and $parent", function(){
var parent = createScope();
var scope = createScope(parent);
expect(scope.$root).toEqual(parent);
expect(scope.$parent).toEqual(parent);
});
// Service injection
it('should inject services', function(){
var scope = createScope(null, {
service:function(){
describe('service injection', function(){
it('should inject services', function(){
var scope = createScope(null, {
service:function(){
return "ABC";
}
});
expect(scope.service).toEqual("ABC");
});
expect(scope.service).toEqual("ABC");
});
it('should inject arugments', function(){
var scope = createScope(null, {
name:function(){
it('should inject arugments', function(){
var scope = createScope(null, {
name:function(){
return "misko";
},
greet: extend(function(name) {
return 'hello ' + name;
}, {inject:['name']})
});
expect(scope.greet).toEqual("hello misko");
});
it('should throw error on missing dependency', function(){
try {
createScope(null, {
greet: extend(function(name) {
}, {inject:['name']})
});
} catch(e) {
expect(e).toEqual("Don't know how to inject 'name'.");
}
});
expect(scope.greet).toEqual("hello misko");
});
it('should throw error on missing dependency', function(){
try {
createScope(null, {
greet: extend(function(name) {
}, {inject:['name']})
});
} catch(e) {
expect(e).toEqual("Don't know how to inject 'name'.");
}
});
});
});

View file

@ -8,7 +8,7 @@ describe("widget", function(){
compile = function(html, before) {
element = jqLite(html);
scope = compiler.compile(element)(element);
(before||noop)();
(before||noop).apply(scope);
scope.$init();
};
});
@ -222,29 +222,57 @@ describe("widget", function(){
expect(scope.checkbox).toEqual(true);
});
it('should support type="radio"', function(){
compile('<div>' +
'<input type="radio" name="chose" value="A" ng-change="clicked = 1"/>' +
'<input type="radio" name="chose" value="B" checked ng-change="clicked = 2"/>' +
'<input type="radio" name="chose" value="C" ng-change="clicked = 3"/>' +
describe('radio', function(){
it('should support type="radio"', function(){
compile('<div>' +
'<input type="radio" name="chose" value="A" ng-change="clicked = 1"/>' +
'<input type="radio" name="chose" value="B" checked ng-change="clicked = 2"/>' +
'<input type="radio" name="chose" value="C" ng-change="clicked = 3"/>' +
'</div>');
var a = element[0].childNodes[0];
var b = element[0].childNodes[1];
expect(b.name.split('@')[1]).toEqual('chose');
expect(scope.chose).toEqual('B');
scope.chose = 'A';
scope.$eval();
expect(a.checked).toEqual(true);
var a = element[0].childNodes[0];
var b = element[0].childNodes[1];
expect(b.name.split('@')[1]).toEqual('chose');
expect(scope.chose).toEqual('B');
scope.chose = 'A';
scope.$eval();
expect(a.checked).toEqual(true);
scope.chose = 'B';
scope.$eval();
expect(a.checked).toEqual(false);
expect(b.checked).toEqual(true);
expect(scope.clicked).not.toBeDefined();
scope.chose = 'B';
scope.$eval();
expect(a.checked).toEqual(false);
expect(b.checked).toEqual(true);
expect(scope.clicked).not.toBeDefined();
click(a);
expect(scope.chose).toEqual('A');
expect(scope.clicked).toEqual(1);
});
it('should honor model over html checked keyword after', function(){
compile('<div>' +
'<input type="radio" name="choose" value="A""/>' +
'<input type="radio" name="choose" value="B" checked/>' +
'<input type="radio" name="choose" value="C"/>' +
'</div>', function(){
this.choose = 'C';
});
expect(scope.choose).toEqual('C');
});
it('should honor model over html checked keyword before', function(){
compile('<div>' +
'<input type="radio" name="choose" value="A""/>' +
'<input type="radio" name="choose" value="B" checked/>' +
'<input type="radio" name="choose" value="C"/>' +
'</div>', function(){
this.choose = 'A';
});
expect(scope.choose).toEqual('A');
});
click(a);
expect(scope.chose).toEqual('A');
expect(scope.clicked).toEqual(1);
});
it('should support type="select-one"', function(){