@@ -91,6 +91,7 @@ function $RootScopeProvider() {
91
91
this . $$watchersCount = 0 ;
92
92
this . $id = nextUid ( ) ;
93
93
this . $$ChildScope = null ;
94
+ this . $$suspended = false ;
94
95
}
95
96
ChildScope . prototype = parent ;
96
97
return ChildScope ;
@@ -178,6 +179,7 @@ function $RootScopeProvider() {
178
179
this . $$childHead = this . $$childTail = null ;
179
180
this . $root = this ;
180
181
this . $$destroyed = false ;
182
+ this . $$suspended = false ;
181
183
this . $$listeners = { } ;
182
184
this . $$listenerCount = { } ;
183
185
this . $$watchersCount = 0 ;
@@ -811,7 +813,7 @@ function $RootScopeProvider() {
811
813
812
814
traverseScopesLoop:
813
815
do { // "traverse the scopes" loop
814
- if ( ( watchers = current . $$watchers ) ) {
816
+ if ( ( watchers = ! current . $$suspended && current . $$watchers ) ) {
815
817
// process our watches
816
818
watchers . $$digestWatchIndex = watchers . length ;
817
819
while ( watchers . $$digestWatchIndex -- ) {
@@ -855,7 +857,7 @@ function $RootScopeProvider() {
855
857
// Insanity Warning: scope depth-first traversal
856
858
// yes, this code is a bit crazy, but it works and we have tests to prove it!
857
859
// this piece should be kept in sync with the traversal in $broadcast
858
- if ( ! ( next = ( ( current . $$watchersCount && current . $$childHead ) ||
860
+ if ( ! ( next = ( ( ! current . $$suspended && current . $$watchersCount && current . $$childHead ) ||
859
861
( current !== target && current . $$nextSibling ) ) ) ) {
860
862
while ( current !== target && ! ( next = current . $$nextSibling ) ) {
861
863
current = current . $parent ;
@@ -892,6 +894,94 @@ function $RootScopeProvider() {
892
894
$browser . $$checkUrlChange ( ) ;
893
895
} ,
894
896
897
+ /**
898
+ * @ngdoc method
899
+ * @name $rootScope.Scope#$suspend
900
+ * @kind function
901
+ *
902
+ * @description
903
+ * Suspend watchers of this scope subtree so that they will not be invoked during digest.
904
+ *
905
+ * This can be used to optimize your application when you know that running those watchers
906
+ * is redundant.
907
+ *
908
+ * **Warning**
909
+ *
910
+ * Suspending scopes from the digest cycle can have unwanted and difficult to debug results.
911
+ * Only use this approach if you are confident that you know what you are doing and have
912
+ * ample tests to ensure that bindings get updated as you expect.
913
+ *
914
+ * Some of the things to consider are:
915
+ *
916
+ * * Any external event on a directive/component will not trigger a digest while the hosting
917
+ * scope is suspended - even if the event handler calls `$apply` or `$digest`.
918
+ * * Transcluded content exists on a scope that inherits from outside a directive but exists
919
+ * as a child of the directive's containing scope. If the containing scope is suspended the
920
+ * transcluded scope will also be suspended, even if the scope from which the transcluded
921
+ * scope inherits is not suspended.
922
+ * * Multiple directives trying to manage the suspended status of a scope can confuse each other:
923
+ * * A call to `$suspend` an already suspended scope is a no-op.
924
+ * * A call to `$resume` a non-suspended scope is a no-op.
925
+ * * If two directives suspend a scope, then one of them resumes the scope, the scope will no
926
+ * longer be suspended. This could result in the other directive believing a scope to be
927
+ * suspended when it is not.
928
+ * * If a parent scope is suspended then all its descendants will be excluded from future digests
929
+ * whether or not they have been suspended themselves. Note that this also applies to isolate
930
+ * child scopes.
931
+ * * Calling `$resume()` on a scope that has a suspended ancestor will not cause the scope to be
932
+ * included in future digests until all its ancestors have been resumed.
933
+ * * Resolved promises, e.g. from explicit `$q` deferreds and `$http` calls, trigger `$apply`
934
+ * against the `$rootScope` and so will still trigger a global digest even if the promise was
935
+ * initialized by a component that lives on a suspended scope.
936
+ */
937
+ $suspend : function ( ) {
938
+ this . $$suspended = true ;
939
+ } ,
940
+
941
+ /**
942
+ * @ngdoc method
943
+ * @name $rootScope.Scope#$isSuspended
944
+ * @kind function
945
+ *
946
+ * @description
947
+ * Call this method to determine if this scope or one of its ancestors have been suspended.
948
+ *
949
+ * Be aware that a scope may not be included in digests if it has a suspended ancestor,
950
+ * even if `$isSuspended()` returns false.
951
+ *
952
+ * To determine if this scope will be excluded in digests then you must check all its ancestors:
953
+ *
954
+ * ```
955
+ * function isExcludedFromDigest(scope) {
956
+ * while(scope) {
957
+ * if (scope.$isSuspended()) return true;
958
+ * scope = scope.$parent;
959
+ * }
960
+ * return false;
961
+ * ```
962
+ *
963
+ * @returns true if the current scope has been suspended.
964
+ */
965
+ $isSuspended : function ( ) {
966
+ return this . $$suspended ;
967
+ } ,
968
+
969
+ /**
970
+ * @ngdoc method
971
+ * @name $rootScope.Scope#$resume
972
+ * @kind function
973
+ *
974
+ * @description
975
+ * Resume watchers of this scope subtree in case it was suspended.
976
+ *
977
+ * It is recommended to digest on this scope after it is resumed to catch any modifications
978
+ * that might have happened while it was suspended.
979
+ *
980
+ * See {@link $rootScope.Scope#$suspend} for information about the dangers of using this approach.
981
+ */
982
+ $resume : function ( ) {
983
+ this . $$suspended = false ;
984
+ } ,
895
985
896
986
/**
897
987
* @ngdoc event
0 commit comments