mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-12 17:03:09 +00:00
HEAD is now at 10c0151 Fixes on issue when a SELECT has OPTION which are data bound (ie OPTION has repeater or OPTION.value is bound), then SELECT does not update to match the correct OPTION after the change in model (ie after the OPTION repeater unrolls or OPTION.value is changed.)
This commit is contained in:
parent
125d725e7d
commit
006fd2ca25
8 changed files with 106 additions and 18 deletions
|
|
@ -124,9 +124,9 @@ function jqLiteWrap(element) {
|
||||||
}
|
}
|
||||||
function isUndefined(value){ return typeof value == $undefined; }
|
function isUndefined(value){ return typeof value == $undefined; }
|
||||||
function isDefined(value){ return typeof value != $undefined; }
|
function isDefined(value){ return typeof value != $undefined; }
|
||||||
function isObject(value){ return value!=_null && typeof value == 'object';}
|
function isObject(value){ return value!=_null && typeof value == $object;}
|
||||||
function isString(value){ return typeof value == 'string';}
|
function isString(value){ return typeof value == $string;}
|
||||||
function isNumber(value){ return typeof value == 'number';}
|
function isNumber(value){ return typeof value == $number;}
|
||||||
function isArray(value) { return value instanceof Array; }
|
function isArray(value) { return value instanceof Array; }
|
||||||
function isFunction(value){ return typeof value == $function;}
|
function isFunction(value){ return typeof value == $function;}
|
||||||
function isTextNode(node) { return nodeName(node) == '#text'; }
|
function isTextNode(node) { return nodeName(node) == '#text'; }
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ function Template(priority) {
|
||||||
this.paths = [];
|
this.paths = [];
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.inits = [];
|
this.inits = [];
|
||||||
this.priority = priority || 0;
|
this.priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
Template.prototype = {
|
Template.prototype = {
|
||||||
|
|
@ -130,7 +130,7 @@ Compiler.prototype = {
|
||||||
priority = priority || 0;
|
priority = priority || 0;
|
||||||
}
|
}
|
||||||
if (isString(priority)) {
|
if (isString(priority)) {
|
||||||
priority = PRIORITY[uppercase(priority)] || 0;
|
priority = PRIORITY[uppercase(priority)] || parseInt(priority);
|
||||||
}
|
}
|
||||||
template = new Template(priority);
|
template = new Template(priority);
|
||||||
eachAttribute(element, function(value, name){
|
eachAttribute(element, function(value, name){
|
||||||
|
|
|
||||||
40
src/Scope.js
40
src/Scope.js
|
|
@ -22,7 +22,7 @@ function getter(instance, path, unboundFn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!unboundFn && isFunction(instance) && !instance['$$factory']) {
|
if (!unboundFn && isFunction(instance)) {
|
||||||
return bind(lastInstance, instance);
|
return bind(lastInstance, instance);
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
|
|
@ -113,12 +113,13 @@ function createScope(parent, services, existing) {
|
||||||
function API(){}
|
function API(){}
|
||||||
function Behavior(){}
|
function Behavior(){}
|
||||||
|
|
||||||
var instance, behavior, api, evalLists = {sorted:[]}, servicesCache = extend({}, existing);
|
|
||||||
|
|
||||||
parent = Parent.prototype = (parent || {});
|
parent = Parent.prototype = (parent || {});
|
||||||
api = API.prototype = new Parent();
|
var evalLists = {sorted:[]};
|
||||||
behavior = Behavior.prototype = new API();
|
var postList = [], postHash = {}, postId = 0;
|
||||||
instance = new Behavior();
|
var servicesCache = extend({}, existing);
|
||||||
|
var api = API.prototype = new Parent();
|
||||||
|
var behavior = Behavior.prototype = new API();
|
||||||
|
var instance = new Behavior();
|
||||||
|
|
||||||
extend(api, {
|
extend(api, {
|
||||||
'this': instance,
|
'this': instance,
|
||||||
|
|
@ -130,14 +131,23 @@ function createScope(parent, services, existing) {
|
||||||
|
|
||||||
$eval: function $eval(exp) {
|
$eval: function $eval(exp) {
|
||||||
var type = typeof exp;
|
var type = typeof exp;
|
||||||
|
var i, iSize;
|
||||||
|
var j, jSize;
|
||||||
|
var queue;
|
||||||
|
var fn;
|
||||||
if (type == $undefined) {
|
if (type == $undefined) {
|
||||||
for ( var i = 0, iSize = evalLists.sorted.length; i < iSize; i++) {
|
for ( i = 0, iSize = evalLists.sorted.length; i < iSize; i++) {
|
||||||
for ( var queue = evalLists.sorted[i],
|
for ( queue = evalLists.sorted[i],
|
||||||
jSize = queue.length,
|
jSize = queue.length,
|
||||||
j= 0; j < jSize; j++) {
|
j= 0; j < jSize; j++) {
|
||||||
instance.$tryEval(queue[j].fn, queue[j].handler);
|
instance.$tryEval(queue[j].fn, queue[j].handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while(postList.length) {
|
||||||
|
fn = postList.shift();
|
||||||
|
delete postHash[fn.$postEvalId];
|
||||||
|
instance.$tryEval(fn);
|
||||||
|
}
|
||||||
} else if (type === $function) {
|
} else if (type === $function) {
|
||||||
return exp.call(instance);
|
return exp.call(instance);
|
||||||
} else if (type === 'string') {
|
} else if (type === 'string') {
|
||||||
|
|
@ -202,6 +212,20 @@ function createScope(parent, services, existing) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
$postEval: function(expr) {
|
||||||
|
if (expr) {
|
||||||
|
var fn = expressionCompile(expr);
|
||||||
|
var id = fn.$postEvalId;
|
||||||
|
if (!id) {
|
||||||
|
id = '$' + instance.$id + "_" + (postId++);
|
||||||
|
fn.$postEvalId = id;
|
||||||
|
}
|
||||||
|
if (!postHash[id]) {
|
||||||
|
postList.push(postHash[id] = fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
$become: function(Class) {
|
$become: function(Class) {
|
||||||
// remove existing
|
// remove existing
|
||||||
foreach(behavior, function(value, key){ delete behavior[key]; });
|
foreach(behavior, function(value, key){ delete behavior[key]; });
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ var REMOVE_ATTRIBUTES = {
|
||||||
angularDirective("ng:bind-attr", function(expression){
|
angularDirective("ng:bind-attr", function(expression){
|
||||||
return function(element){
|
return function(element){
|
||||||
var lastValue = {};
|
var lastValue = {};
|
||||||
|
var updateFn = element.parent().data('$update');
|
||||||
this.$onEval(function(){
|
this.$onEval(function(){
|
||||||
var values = this.$eval(expression);
|
var values = this.$eval(expression);
|
||||||
for(var key in values) {
|
for(var key in values) {
|
||||||
|
|
@ -132,6 +133,7 @@ angularDirective("ng:bind-attr", function(expression){
|
||||||
} else {
|
} else {
|
||||||
element.attr(key, value);
|
element.attr(key, value);
|
||||||
}
|
}
|
||||||
|
this.$postEval(updateFn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, element);
|
}, element);
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,11 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
view.set(lastValue = model.get());
|
function updateView(){
|
||||||
|
view.set(lastValue = model.get());
|
||||||
|
}
|
||||||
|
updateView();
|
||||||
|
element.data('$update', updateView);
|
||||||
scope.$watch(model.get, function(value){
|
scope.$watch(model.get, function(value){
|
||||||
if (lastValue !== value) {
|
if (lastValue !== value) {
|
||||||
view.set(lastValue = value);
|
view.set(lastValue = value);
|
||||||
|
|
@ -231,6 +235,14 @@ angularWidget('select', function(element){
|
||||||
return inputWidgetSelector.call(this, element);
|
return inputWidgetSelector.call(this, element);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
angularWidget('option', function(){
|
||||||
|
this.descend(true);
|
||||||
|
this.directives(true);
|
||||||
|
return function(element) {
|
||||||
|
this.$postEval(element.parent().data('$update'));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
angularWidget('ng:include', function(element){
|
angularWidget('ng:include', function(element){
|
||||||
var compiler = this,
|
var compiler = this,
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,27 @@ describe('scope/model', function(){
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('$postEval', function(){
|
||||||
|
it('should eval function once and last', function(){
|
||||||
|
var log = '';
|
||||||
|
var scope = createScope();
|
||||||
|
function onceOnly(){log+= '@';}
|
||||||
|
scope.$onEval(function(){log+= '.';});
|
||||||
|
scope.$postEval(function(){log+= '!';});
|
||||||
|
scope.$postEval(onceOnly);
|
||||||
|
scope.$postEval(onceOnly);
|
||||||
|
scope.$postEval(); // ignore
|
||||||
|
scope.$eval();
|
||||||
|
expect(log).toEqual('.!@');
|
||||||
|
scope.$eval();
|
||||||
|
expect(log).toEqual('.!@.');
|
||||||
|
|
||||||
|
scope.$postEval(onceOnly);
|
||||||
|
scope.$postEval(onceOnly);
|
||||||
|
scope.$eval();
|
||||||
|
expect(log).toEqual('.!@..@');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ extend(angular, {
|
||||||
|
|
||||||
function sortedHtml(element) {
|
function sortedHtml(element) {
|
||||||
var html = "";
|
var html = "";
|
||||||
foreach(element, function toString(node) {
|
foreach(jqLite(element), function toString(node) {
|
||||||
if (node.nodeName == "#text") {
|
if (node.nodeName == "#text") {
|
||||||
html += escapeHtml(node.nodeValue);
|
html += escapeHtml(node.nodeValue);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -188,6 +188,7 @@ function click(element) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name == 'option') {
|
if (name == 'option') {
|
||||||
|
element.parent().val(element.val());
|
||||||
JQLite.prototype.trigger.call(element.parent(), 'change');
|
JQLite.prototype.trigger.call(element.parent(), 'change');
|
||||||
} else {
|
} else {
|
||||||
JQLite.prototype.trigger.call(element, 'click');
|
JQLite.prototype.trigger.call(element, 'click');
|
||||||
|
|
|
||||||
|
|
@ -355,9 +355,9 @@ describe("widget", function(){
|
||||||
it('should honor the value field in option', function(){
|
it('should honor the value field in option', function(){
|
||||||
compile(
|
compile(
|
||||||
'<select name="selection" ng:format="number">' +
|
'<select name="selection" ng:format="number">' +
|
||||||
'<option value="{{$index}}" ng:repeat="name in [\'A\', \'B\']">{{name}}</option>' +
|
'<option value="{{$index}}" ng:repeat="name in [\'A\', \'B\', \'C\']">{{name}}</option>' +
|
||||||
'</select>');
|
'</select>');
|
||||||
// childNodes[0] is repeater
|
// childNodes[0] is repeater comment
|
||||||
expect(scope.selection).toEqual(undefined);
|
expect(scope.selection).toEqual(undefined);
|
||||||
|
|
||||||
click(element[0].childNodes[1]);
|
click(element[0].childNodes[1]);
|
||||||
|
|
@ -367,6 +367,32 @@ describe("widget", function(){
|
||||||
scope.$eval();
|
scope.$eval();
|
||||||
expect(element[0].childNodes[2].selected).toEqual(true);
|
expect(element[0].childNodes[2].selected).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should unroll select options before eval', function(){
|
||||||
|
compile(
|
||||||
|
'<select name="selection" ng:required>' +
|
||||||
|
'<option value="{{$index}}" ng:repeat="opt in options">{{opt}}</option>' +
|
||||||
|
'</select>');
|
||||||
|
scope.selection = 1;
|
||||||
|
scope.options = ['one', 'two'];
|
||||||
|
scope.$eval();
|
||||||
|
expect(element[0].value).toEqual('1');
|
||||||
|
expect(element.hasClass(NG_VALIDATION_ERROR)).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update select when value changes', function(){
|
||||||
|
compile(
|
||||||
|
'<select name="selection">' +
|
||||||
|
'<option value="...">...</option>' +
|
||||||
|
'<option value="{{value}}">B</option>' +
|
||||||
|
'</select>');
|
||||||
|
scope.selection = 'B';
|
||||||
|
scope.$eval();
|
||||||
|
expect(element[0].childNodes[1].selected).toEqual(false);
|
||||||
|
scope.value = 'B';
|
||||||
|
scope.$eval();
|
||||||
|
expect(element[0].childNodes[1].selected).toEqual(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support type="select-multiple"', function(){
|
it('should support type="select-multiple"', function(){
|
||||||
|
|
@ -468,7 +494,7 @@ describe("widget", function(){
|
||||||
|
|
||||||
scope.url = undefined;
|
scope.url = undefined;
|
||||||
scope.$eval();
|
scope.$eval();
|
||||||
|
|
||||||
expect(element.text()).toEqual('');
|
expect(element.text()).toEqual('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue