Skip to content

Commit 8194fae

Browse files
committed
feat($rootScope): allow for a scope to have partialDigest
When a scope has partialDigest, $apply-ing the scope only triggers the dirty checking in that scope and its descendants BREAKING CHANGE: Scope.$apply now requires its `this` to be a given scope. Replace any calls similar to ```js expect($rootScope.$apply).toThrow('...') ``` with ```js expect(function () { $rootScope.$apply(); }).toThrow('...'); ```
1 parent b0ca519 commit 8194fae

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

src/ng/rootScope.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ function $RootScopeProvider(){
134134
this.$$listeners = {};
135135
this.$$listenerCount = {};
136136
this.$$isolateBindings = {};
137+
this.$$digestTarget = true;
137138
}
138139

139140
/**
@@ -170,7 +171,7 @@ function $RootScopeProvider(){
170171
* @returns {Object} The newly created child scope.
171172
*
172173
*/
173-
$new: function(isolate) {
174+
$new: function(isolate, partialDigest) {
174175
var ChildScope,
175176
child;
176177

@@ -205,6 +206,7 @@ function $RootScopeProvider(){
205206
} else {
206207
this.$$childHead = this.$$childTail = child;
207208
}
209+
child.$$digestTarget = !!partialDigest;
208210
return child;
209211
},
210212

@@ -976,7 +978,9 @@ function $RootScopeProvider(){
976978
} finally {
977979
clearPhase();
978980
try {
979-
$rootScope.$digest();
981+
var scope = this;
982+
while(!scope.$$digestTarget) scope = scope.$parent;
983+
scope.$digest();
980984
} catch (e) {
981985
$exceptionHandler(e);
982986
throw e;

test/ng/compileSpec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -3014,7 +3014,7 @@ describe('$compile', function() {
30143014
expect(componentScope.ref).toBe('hello world');
30153015

30163016
componentScope.ref = 'ignore me';
3017-
expect($rootScope.$apply).
3017+
expect(function() {$rootScope.$apply(); }).
30183018
toThrowMinErr("$compile", "nonassign", "Expression ''hello ' + name' used with directive 'myComponent' is non-assignable!");
30193019
expect(componentScope.ref).toBe('hello world');
30203020
// reset since the exception was rethrown which prevented phase clearing
@@ -5180,7 +5180,7 @@ describe('$compile', function() {
51805180
/* jshint scripturl:true */
51815181
element = $compile('<iframe src="{{testUrl}}"></iframe>')($rootScope);
51825182
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
5183-
expect($rootScope.$apply).toThrowMinErr(
5183+
expect(function() {$rootScope.$apply(); }).toThrowMinErr(
51845184
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
51855185
"loading resource from url not allowed by $sceDelegate policy. URL: javascript:doTrustedStuff()");
51865186
}));
@@ -5226,7 +5226,7 @@ describe('$compile', function() {
52265226
/* jshint scripturl:true */
52275227
element = $compile('<form action="{{testUrl}}"></form>')($rootScope);
52285228
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
5229-
expect($rootScope.$apply).toThrowMinErr(
5229+
expect(function() {$rootScope.$apply(); }).toThrowMinErr(
52305230
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
52315231
"loading resource from url not allowed by $sceDelegate policy. URL: javascript:doTrustedStuff()");
52325232
}));

test/ng/directive/ngSrcSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('ngSrc', function() {
4545
it('should error on non-resource_url src attributes', inject(function($compile, $rootScope, $sce) {
4646
element = $compile('<iframe ng-src="{{testUrl}}"></iframe>')($rootScope);
4747
$rootScope.testUrl = $sce.trustAsUrl("javascript:doTrustedStuff()");
48-
expect($rootScope.$apply).toThrowMinErr(
48+
expect(function() {$rootScope.$apply(); }).toThrowMinErr(
4949
"$interpolate", "interr", "Can't interpolate: {{testUrl}}\nError: [$sce:insecurl] Blocked " +
5050
"loading resource from url not allowed by $sceDelegate policy. URL: " +
5151
"javascript:doTrustedStuff()");

test/ng/rootScopeSpec.js

+38
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,44 @@ describe('Scope', function() {
7272
expect(child.$new).toBe($rootScope.$new);
7373
expect(child.$root).toBe($rootScope);
7474
}));
75+
76+
it('should create a child scope with partialDigest', inject(function($rootScope, log) {
77+
var child = $rootScope.$new(false, true);
78+
var spyRoot = jasmine.createSpy('spyRoot');
79+
var spyChild = jasmine.createSpy('spyChild');
80+
$rootScope.$watch(spyRoot);
81+
child.$watch(spyChild);
82+
83+
child.$apply();
84+
expect(spyRoot).not.toHaveBeenCalled();
85+
expect(spyChild).toHaveBeenCalled();
86+
87+
spyRoot.reset();
88+
spyChild.reset();
89+
90+
$rootScope.$apply();
91+
expect(spyRoot).toHaveBeenCalled();
92+
expect(spyChild).toHaveBeenCalled();
93+
}));
94+
95+
it('should create an isolated child scope with partialDigest', inject(function($rootScope, log) {
96+
var child = $rootScope.$new(true, true);
97+
var spyRoot = jasmine.createSpy('spyRoot');
98+
var spyChild = jasmine.createSpy('spyChild');
99+
$rootScope.$watch(spyRoot);
100+
child.$watch(spyChild);
101+
102+
child.$apply();
103+
expect(spyRoot).not.toHaveBeenCalled();
104+
expect(spyChild).toHaveBeenCalled();
105+
106+
spyRoot.reset();
107+
spyChild.reset();
108+
109+
$rootScope.$apply();
110+
expect(spyRoot).toHaveBeenCalled();
111+
expect(spyChild).toHaveBeenCalled();
112+
}));
75113
});
76114

77115

0 commit comments

Comments
 (0)