aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2011-08-11 15:19:26 -0700
committerMisko Hevery2011-08-12 16:18:41 -0700
commit3f99cdbdc30e85c2100acd7cbe67052befd08776 (patch)
treedd16babb99b2874cdde91cecde85e1d1ed6e7ccd
parent13e7df68a65b0dd2eb4eed673f7b8e3e702d72a9 (diff)
downloadangular.js-3f99cdbdc30e85c2100acd7cbe67052befd08776.tar.bz2
feat(scope): $evalAsync support
-rw-r--r--CHANGELOG.md4
-rw-r--r--src/Scope.js38
-rw-r--r--test/ScopeSpec.js35
3 files changed, 77 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2c3704e..f60c9b17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
<a name="0.9.19"><a/>
# 0.9.19 canine-psychokinesis (in-progress) #
+### Features
+- Scope $evalAsync()
+
+
# Breaking Changes
- Controller constructor functions are now looked up on scope first and then on window.
- angular.equals now use === which means that things which used to be equal are no longer.
diff --git a/src/Scope.js b/src/Scope.js
index ffac1317..af956de2 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -96,6 +96,7 @@ function Scope() {
this.$$phase = this.$parent = this.$$watchers =
this.$$nextSibling = this.$$childHead = this.$$childTail = null;
this['this'] = this.$root = this;
+ this.$$asyncQueue = [];
}
/**
@@ -168,6 +169,7 @@ Scope.prototype = {
child['this'] = child;
child.$parent = this;
child.$id = nextUid();
+ child.$$asyncQueue = [];
child.$$phase = child.$$watchers =
child.$$nextSibling = child.$$childHead = child.$$childTail = null;
if (this.$$childHead) {
@@ -319,6 +321,7 @@ Scope.prototype = {
var child,
watch, value, last,
watchers = this.$$watchers,
+ asyncQueue = this.$$asyncQueue,
length, count = 0,
dirtyCount, ttl = 100,
recheck = !this.$parent || !this.$parent.$$phase;
@@ -328,6 +331,13 @@ Scope.prototype = {
}
this.$$phase = '$digest';
do {
+ while(asyncQueue.length) {
+ try {
+ this.$eval(asyncQueue.shift());
+ } catch (e) {
+ this.$service('$exceptionHandler')(e);
+ }
+ }
dirtyCount = 0;
if (watchers) {
// process our watches
@@ -441,6 +451,34 @@ Scope.prototype = {
/**
* @workInProgress
* @ngdoc function
+ * @name angular.scope.$evalAsync
+ * @function
+ *
+ * @description
+ * Executes the expression on the current scope at a later point in time.
+ *
+ * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that:
+ *
+ * - it will execute in the current script execution context (before any DOM rendering).
+ * - at least one {@link angular.scope.$digest $digest cycle} will be performed after
+ * `expression` execution.
+ *
+ * Any exceptions from the execution of the expression are forwarded to the
+ * {@link angular.service.$exceptionHandler $exceptionHandler} service.
+ *
+ * @param {(string|function())=} expression An angular expression to be executed.
+ *
+ * - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
+ * - `function(scope)`: execute the function with the current `scope` parameter.
+ *
+ */
+ $evalAsync: function(expr) {
+ this.$$asyncQueue.push(expr);
+ },
+
+ /**
+ * @workInProgress
+ * @ngdoc function
* @name angular.scope.$apply
* @function
*
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index 5a14abd6..b3edad6c 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -306,6 +306,41 @@ describe('Scope', function(){
});
});
+ describe('$evalAsync', function(){
+
+ it('should run callback before $watch', function(){
+ var log = '';
+ var child = root.$new();
+ root.$evalAsync(function(scope){ log += 'parent.async;'; });
+ root.$watch('value', function(){ log += 'parent.$digest;'; });
+ child.$evalAsync(function(scope){ log += 'child.async;'; });
+ child.$watch('value', function(){ log += 'child.$digest;'; });
+ root.$digest();
+ expect(log).toEqual('parent.async;parent.$digest;child.async;child.$digest;');
+ });
+
+ it('should cause a $digest rerun', function(){
+ root.log = '';
+ root.value = 0;
+ root.$watch('value', 'log = log + ".";');
+ root.$watch('init', function(){
+ root.$evalAsync('value = 123; log = log + "=" ');
+ expect(root.value).toEqual(0);
+ });
+ root.$digest();
+ expect(root.log).toEqual('.=.');
+ });
+
+ it('should run async in the same order as added', function(){
+ root.log = '';
+ root.$evalAsync("log = log + 1");
+ root.$evalAsync("log = log + 2");
+ root.$digest();
+ expect(root.log).toBe('12');
+ });
+
+ });
+
describe('$apply', function(){
it('should apply expression with full lifecycle', function(){