aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Angular.js6
-rw-r--r--src/Compiler.js4
-rw-r--r--src/Scope.js40
-rw-r--r--src/directives.js2
-rw-r--r--src/widgets.js14
-rw-r--r--test/ScopeSpec.js23
-rw-r--r--test/testabilityPatch.js3
-rw-r--r--test/widgetsSpec.js32
8 files changed, 106 insertions, 18 deletions
diff --git a/src/Angular.js b/src/Angular.js
index 7ea43c98..ef1187f2 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -124,9 +124,9 @@ function jqLiteWrap(element) {
}
function isUndefined(value){ return typeof value == $undefined; }
function isDefined(value){ return typeof value != $undefined; }
-function isObject(value){ return value!=_null && typeof value == 'object';}
-function isString(value){ return typeof value == 'string';}
-function isNumber(value){ return typeof value == 'number';}
+function isObject(value){ return value!=_null && typeof value == $object;}
+function isString(value){ return typeof value == $string;}
+function isNumber(value){ return typeof value == $number;}
function isArray(value) { return value instanceof Array; }
function isFunction(value){ return typeof value == $function;}
function isTextNode(node) { return nodeName(node) == '#text'; }
diff --git a/src/Compiler.js b/src/Compiler.js
index c45dd46e..6252e0c4 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -8,7 +8,7 @@ function Template(priority) {
this.paths = [];
this.children = [];
this.inits = [];
- this.priority = priority || 0;
+ this.priority = priority;
}
Template.prototype = {
@@ -130,7 +130,7 @@ Compiler.prototype = {
priority = priority || 0;
}
if (isString(priority)) {
- priority = PRIORITY[uppercase(priority)] || 0;
+ priority = PRIORITY[uppercase(priority)] || parseInt(priority);
}
template = new Template(priority);
eachAttribute(element, function(value, name){
diff --git a/src/Scope.js b/src/Scope.js
index c2a4f098..53228a0d 100644
--- a/src/Scope.js
+++ b/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 instance;
@@ -113,12 +113,13 @@ function createScope(parent, services, existing) {
function API(){}
function Behavior(){}
- var instance, behavior, api, evalLists = {sorted:[]}, servicesCache = extend({}, existing);
-
parent = Parent.prototype = (parent || {});
- api = API.prototype = new Parent();
- behavior = Behavior.prototype = new API();
- instance = new Behavior();
+ var evalLists = {sorted:[]};
+ var postList = [], postHash = {}, postId = 0;
+ var servicesCache = extend({}, existing);
+ var api = API.prototype = new Parent();
+ var behavior = Behavior.prototype = new API();
+ var instance = new Behavior();
extend(api, {
'this': instance,
@@ -130,14 +131,23 @@ function createScope(parent, services, existing) {
$eval: function $eval(exp) {
var type = typeof exp;
+ var i, iSize;
+ var j, jSize;
+ var queue;
+ var fn;
if (type == $undefined) {
- for ( var i = 0, iSize = evalLists.sorted.length; i < iSize; i++) {
- for ( var queue = evalLists.sorted[i],
+ for ( i = 0, iSize = evalLists.sorted.length; i < iSize; i++) {
+ for ( queue = evalLists.sorted[i],
jSize = queue.length,
j= 0; j < jSize; j++) {
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) {
return exp.call(instance);
} 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) {
// remove existing
foreach(behavior, function(value, key){ delete behavior[key]; });
diff --git a/src/directives.js b/src/directives.js
index 994ef90e..69648e31 100644
--- a/src/directives.js
+++ b/src/directives.js
@@ -115,6 +115,7 @@ var REMOVE_ATTRIBUTES = {
angularDirective("ng:bind-attr", function(expression){
return function(element){
var lastValue = {};
+ var updateFn = element.parent().data('$update');
this.$onEval(function(){
var values = this.$eval(expression);
for(var key in values) {
@@ -132,6 +133,7 @@ angularDirective("ng:bind-attr", function(expression){
} else {
element.attr(key, value);
}
+ this.$postEval(updateFn);
}
}
}, element);
diff --git a/src/widgets.js b/src/widgets.js
index b70c4dcb..fbca8436 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -209,7 +209,11 @@ function inputWidget(events, modelAccessor, viewAccessor, initFn) {
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){
if (lastValue !== value) {
view.set(lastValue = value);
@@ -231,6 +235,14 @@ angularWidget('select', function(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){
var compiler = this,
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index ea63fea4..66e9d489 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -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('.!@..@');
+ });
+ });
+
});
diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js
index bbe2876e..955dccfa 100644
--- a/test/testabilityPatch.js
+++ b/test/testabilityPatch.js
@@ -59,7 +59,7 @@ extend(angular, {
function sortedHtml(element) {
var html = "";
- foreach(element, function toString(node) {
+ foreach(jqLite(element), function toString(node) {
if (node.nodeName == "#text") {
html += escapeHtml(node.nodeValue);
} else {
@@ -188,6 +188,7 @@ function click(element) {
}
}
if (name == 'option') {
+ element.parent().val(element.val());
JQLite.prototype.trigger.call(element.parent(), 'change');
} else {
JQLite.prototype.trigger.call(element, 'click');
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index 3861ef4f..d113e6ee 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -355,9 +355,9 @@ describe("widget", function(){
it('should honor the value field in option', function(){
compile(
'<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>');
- // childNodes[0] is repeater
+ // childNodes[0] is repeater comment
expect(scope.selection).toEqual(undefined);
click(element[0].childNodes[1]);
@@ -367,6 +367,32 @@ describe("widget", function(){
scope.$eval();
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(){
@@ -468,7 +494,7 @@ describe("widget", function(){
scope.url = undefined;
scope.$eval();
-
+
expect(element.text()).toEqual('');
});
});