-
Notifications
You must be signed in to change notification settings - Fork 27.5k
feat($rootScope): allow suspending and resuming watchers on scope #16308
Conversation
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
47b7a72
to
4951709
Compare
I have added some additional comments to the docs to indicate some of the dangers of using this feature. |
Gruntfile.js
Outdated
@@ -178,6 +178,7 @@ module.exports = function(grunt) { | |||
'i18n/**/*.js', | |||
'!docs/app/assets/js/angular-bootstrap/**', | |||
'!docs/config/templates/**', | |||
'!docs/bower_components/**', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that still needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, well it was causing a problem locally but then perhaps I had some old unused files ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing
expect(watchSpy).toHaveBeenCalled(); | ||
})); | ||
|
||
it('should suspend watchers on child scope', inject(function($rootScope) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe test that $resume
works in this case as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* as a child of the directive's containing scope. If the containing scope is suspended the | ||
* transcluded scope will also be suspended, even if the scope from which the transcluded | ||
* scope inherits is not suspended. | ||
* * Multiple directives trying to manage the suspended status of a scope can confuse each other: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes good point. Perhaps a .$isSuspended() getter or similar so a directive can find out whether its scope is already suspended?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm this brings up an additional more complex point.
If a parent scope is suspended, but the current scope is not, this scope will not take part in any digests even though it is not officially suspended.
I think that we don't care, since directives that want to suspend a scope don't really care whether an ancestor scope has been suspended. They are only interested in managing their own scope.
The only time this is a problem is if there are multiple directives trying to manage the suspended status of a single scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I see the problem. It's a fair assumption that if you asked for $isSuspended() you'd want true
returned if it was disabled because of a parent, but I think the stuff you put in the docs explaining this issue solves it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of interest, how hard would it be to implement a .$ancestorIsSuspended()
?
Unfortunately my only case for it is if people use $scope.$digest
at some point below the $suspend
'ed element and want to avoid that $digest in those cases. Since you discourage $scope.$digest
, I am girding myself for rejection here, but it would be useful to me, and .$ancestorIsSuspended()
would save me writing a custom "recurse up the $parents" function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To find out if an ancestor is suspended, you only need to read the $parent scope until you find one that is suspended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Thanks guys, and for the code example @gkalpak :)
* Only use this approach if you are confident that you know what you are doing and have | ||
* ample tests to ensure that bindings get updated as you expect. | ||
* | ||
* Some of the things to consider are: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few other things:
-
isolate scopes are also suspended (as they are still children of the parent and not the rootScope)
-
resolved promises e.g. from explicit $q deferreds, timeouts, intervals, and http calls are not bound to scopes and will trigger a global digest if you e.g. execute http calls from a component which has its scope suspended.
4951709
to
641f8b7
Compare
Added another test, the |
cdda529
to
cee0dc0
Compare
src/ng/rootScope.js
Outdated
* * Transcluded content exists on a scope that inherits from outside a directive but exists | ||
* as a child of the directive's containing scope. If the containing scope is suspended the | ||
* transcluded scope will also be suspended, even if the scope from which the transcluded | ||
* scope inherits is not suspended. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how you managed to explain that in words. Great job! Perhaps an example would make it even more clear? "Eg component B is referenced inside component A, and part of component A's content is transcluded inside component B. If component B's scope is suspended, that part of component A which is transcluded within B will also be scope-suspended." ...if I understood it correctly. Please edit or discard as you see fit!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wary of making this too easy to follow :-P otherwise people who do not understand what they are doing might think they can use this feature :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair call! :)
I'm very heartened that none of the warnings raised so far and/or added to the docs are surprising or problematic to me. I see this being used in my project and by most people simply to "turn off" whole sub-trees, eg in a version of ng-show that simply stops the $digestion of hidden content, and I don't think any of the mentioned edge cases will cause problems in such an implementation. But the proof is in the pudding. I'll test it soon in my app and give feedback on how it goes in practice. |
cee0dc0
to
65360d5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would consider using terminology that is closer to Angular (e.g. $detach
/$attach
).
src/ng/rootScope.js
Outdated
* @description | ||
* Resume watchers of this scope subtree in case it was suspended. | ||
* | ||
* It is recommended to digest on this scope after it is resumed to catch any modifications |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like we recommend $scope.$digest()
(which is something we actually recommend against) 😱
Also, it is possible that $resume()
will be called during a $digest
, in which case you should be triggering a $digest
. (Yes, we need a $safeDigest()
😛)
☝️ These should be somehow explained in this doc. Good luck 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we make $resume
trigger an async apply?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean if I have 10 components all resuming at once, there'll be 10x .$apply
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, you will get one (as long as they all resume as part of the same macrotask.
src/ng/rootScope.js
Outdated
* @kind function | ||
* | ||
* @description | ||
* Call this method to determine if this scope or one of its ancestors have been suspended. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or one of its ancestors
Not true (any more?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right
src/ng/rootScope.js
Outdated
* Be aware that a scope may not be included in digests if it has a suspended ancestor, | ||
* even if `$isSuspended()` returns false. | ||
* | ||
* To determine if this scope will be excluded in digests then you must check all its ancestors: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be worth adding a method for that (or support passing a flag to determine whether to check ancestors or not).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it is a strong enough use case right now to add it to the core library.
*/ | ||
$resume: function() { | ||
this.$$suspended = false; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, $resume
should also set dirty = true
and lastDirtyWatch = null
(in case already during a $digest
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If $resume
triggered an async apply then we wouldn't need to worry about this, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct (I think 😁).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that calling resume from a watch handler is fine, because the fact that the watch handler was called means that the digest is already marked as dirty. So a new digest has already been triggered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And we don't seem to need to worry about the short circuiting of lastDirtyWatch
:
- if the resumed scope is "after" the last dirty watch, then it will have been digested on that turn of the digest cycle.
- if the resumed scope is "before" the last dirty watch, then it will get digested before we hit the short circuit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we don't need to run any applyAsync
etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! 👍
test/ng/rootScopeSpec.js
Outdated
@@ -1255,6 +1255,84 @@ describe('Scope', function() { | |||
}); | |||
}); | |||
|
|||
|
|||
describe('$suspend/$resume', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/$isSuspended
😁
$rootScope.$resume(); | ||
expect(childScope.$isSuspended()).toBe(false); | ||
})); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, there should be some tests about suspending/resuming during a $digest
.
(E.g. to cover basic cases, but also things like #16308 (comment).)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll see what I can add.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @gkalpak - I am wary of using names that are too similar to Angular, since this is different enough that it could end up being more confusing when migrating to Angular. |
src/ng/rootScope.js
Outdated
@@ -855,7 +857,7 @@ function $RootScopeProvider() { | |||
// Insanity Warning: scope depth-first traversal | |||
// yes, this code is a bit crazy, but it works and we have tests to prove it! | |||
// this piece should be kept in sync with the traversal in $broadcast | |||
if (!(next = ((current.$$watchersCount && current.$$childHead) || | |||
if (!(next = ((!current.$$suspended && current.$$watchersCount && current.$$childHead) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment above this line says " this piece should be kept in sync with the traversal in $broadcast". So should it? Sorry I'm not familiar enough with the code to know, but I thought it was worth raising just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot! I think it makes sense to deviate in this case (i.e. suspending the scope should not prevent event propagation), but it is a good idea to reflect that in the comment to avoid future confusion).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So what does it mean "suspending the scope should not prevent event propagation"? So if a parent scope (not suspended) does a broadcast, any suspended descendants will receive that event and run the associated event code?
If that's what you mean, I think I disagree. I think it's perhaps a more consistent DX if such event receivers are suspended because it's a more absolute and complete "turning off" of the suspended descendants' functionality. Partial suspension is weirder and harder to reason about, document, explain to other developers, IMO.
Code in descendants that react to changes will be arbitrarily contained in event receivers and $watches/$onChanges, and without complete suspension I have to review all of it to make sure I understand which of it will be executed despite suspension and which won't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I agree with @poshest here. We should not pass broadcasts to suspended scopes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I disagree with both of you then 😁
In my view $suspend
/$resume
is only related to change-detection (which in AngularJS is performed via $digest
runs). Event propagation is an orthogonal concern and should be able to work independently from CD.
Suspending event propagation sounds to me a little like saying we will not trigger $timeout
s if they come from a suspended scope (assuming we could determine the scope). Or not evaluate $eval()
/$evalAsync()
expressions.
IMO it would be way more confusing to stop propagating events.
Secondary reasons:
- By running a
$digest
once the scope is resumed, you should be able to get to the same state you would have gotten if the scope was not suspended (most of the time). I.e. you are able to "replay" the changes, because they are recorded in the app state. This is especially true if (as recommended) you watch expressions and handlers are idempotent. - Events could be a handy way to trigger
$suspend
/$resume
😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I still feel that it's less confusing to have the descendants suspended in every respect, so yes, it would be a clearer API to include suspension of $timeout
s, and $eval()
/$evalAsync()
s. But you make it sound like implementation of that would be difficult. :P
By "By running a $digest
once the scope is resumed", are you saying that the missed events would have, if not for being $suspend
ed, affected a component's state and therefore the component will show the wrong state when it's $resume
d? I wouldn't code a component like that. But I guess there may be use cases I haven't thought of.
"Events could be a handy way to trigger $suspend/$resume". True! Certainly if the original intent to suspend the current scope (not only descendent ones) comes to fruition, this would be crucial. But even then, I still prefer suspending only descendants.
While it's not my preference, I'm open to excluding $broadcasts from suspending scopes, and in that case I'd argue for
- changing the names to
$suspendDigests()
and$resumeDigests()
and$digestsAreSuspended()
to be clearer - adding my proposed
$ancestorIsSuspended()
($ancestorDigestsAreSuspended()
!) so that developers could easily avoid reacting to$broadcast
ed events on suspended scopes on a case-by-case basis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I still feel that it's less confusing to have the descendants suspended in every respect, so yes, it would be a clearer API to include suspension of $timeouts, and $eval()/$evalAsync()s. But you make it sound like implementation of that would be difficult.
It's difficult / impossible, because ...
For all of the following, the scope and its descedants are excluded from any digests: $apply, $evalAsync, $timeout, $interval, $q, $http. This is because all of these trigger a digest on the $rootScope, not the current scope, so they will digest until they reach the suspended scope.
So these functions are not bound to a specific scope, but to the change detection (digest cycle) in general.
timeout and interval have an option to skip the apply for that reason. Http could get one, but it's trickier: #12557. eval() doesn't trigger a digest.
Essentially when devs use these functions they need to know that they suspend the digest, but not the triggering of the change detection.
OK, I think I have found a possible Catch 22 with this implementation from my early testing. I started by creating my most desired perf feature since one-time binding. At the moment it's just a hack of
I realised the problem pretty early on... in almost all the use cases for I read back to vahan-hartooni's version of this and he, I think rightly wrote this as a suspension of child scopes only. I don't see how also suspending the current scope could be workable, unless anyone has other ideas about how I can trigger |
I think you want to make the directive create a new scope, e.g. |
I added
to these
and I still get the same result. I've got a deadline til Sunday night on something else, but I'll get a codepen going on Monday so we're on the same page and can eliminate the possibility that I messed something silly up. Enjoy your weekends! :) |
Ahah! Of course, the component is requesting an isolate scope, so you can't also ask for a new scope :-( |
Another quick observation while I think of it is that
Those digests need to be initiated from the parent or above, right? |
Thanks @poshest for doing the tests! I think you are right with your concerns. So in general, I think it makes sense to suspend the child scopes when calling suspend on a scope. I assume a general use case will be to cut of whole parts of an application from digesting if they are hidden, e.g. if a modal is open, you don't need to digest the rest of the UI. That means most of the time the control of the suspension does not lie with the scope owner ( e.g. component), but with the parent. For example, if I want to use a third party component, I should be able to put a "suspender" directive on the element, that will toggle digestion on the component's scope (template) (the part which I don't own), while still allowing the component element (which might have other bindings) to digest. At the moment, this seems like it's unsupported. Manual $digests will digest the scope on which they are called, and all descendant scopes. This is to be expected. You should use scope.$apply anyway, which does not have this problem - it digest the rootScope and will stop on the suspended scope. |
Is there actually anything wrong with @Narretz's example directive? Does it work if there is an isolate component on the element being transcluded? |
Why would this need to be shipped with core? |
Because of
i.e. you need one scope that handles the watch that suspends / unsuspends another scope. You can't have both on the same scope, because the watch won't fire on a suspended scope. |
Here is a directive that appears to work as I would expect: http://plnkr.co/edit/N0w01zkzUPugUJ10wRUp?p=preview Can you comment? |
The transclusion trick works for me and so do your directives @Narretz and @petebacondarwin. They resolve my issue of $watch disabling, albeit in a way that I wouldn't easily come to myself. The suspend children/descendants method might be more intuitive for less experienced developers, but then as you said, this feature isn't for them :) |
Since it is a bit tricky to get it to work just-so, how about we do implement an |
src/ng/rootScope.js
Outdated
* * If two directives suspend a scope, then one of them resumes the scope, the scope will no | ||
* longer be suspended. This could result in the other directive believing a scope to be | ||
* suspended when it is not. | ||
* * If a parent scope is suspended then all its descendants will be excluded from future digests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...will be excluded from future digests that are initiated from the parent or any ancestor of the parent (which includes every .$apply()
) whether or not...
@petebacondarwin I'm happy either way. :) But I'm curious why not just go with the suspend children option? I don't really understand @Narretz
Why does that matter? If there are no child scopes there's nothing to suspend (works as expected). The suspension will still work if that scope later acquires children, no? |
I guess. I haven't looked into the implementation. I think it's easy to break the scope traversion loop when we find a scope that whose children are suspended. @petebacondarwin directive has the advantage that you can put it on the same element. |
vahan-hartooni's version of this simply omits the
So that effectively only the change to this line
remains. Does that effectively target only the child/descendent scopes? Or is this a naive approach? |
As a thought experiment consider the following situation where there is a directive <div ng-controller="MyController"> <!-- this creates a scope for us -->
<div>{{ a }}</div>
<div suspend-on="value">
{{ b }}
<some-component></some-component>
</div>
<div>{{ c }}</div>
</div> The scope at the a) If we go with the current implementation, then such a directive would suspend updates to all the interpolations ( In both cases this is crazy and confusing. So, our For me, this makes sense. I believe that changing to suspend children rather than suspending the current scope is no more intuitive than what this PR proposes, since the directive that would need to be implemented is just as complex. |
I agree that suspending only children is tricky/not ideal (but probably for different reasons than @Narretz 😁). Basically, change detection should be a responsibility of the component that contans the bindings, not the user of the component. The primary (only) reason for suspending a scope is performance and only the implementor should be concerned with when is an appropriate time to suspend/resume. The user of the component should not know anything about that and not care. For example, imagine a component that displays user info and only cares to update its view when the logged-in user changes. It suspends its scope once the use logs in and temporarily resume once the By suspending children only we make it essentially impossible for a component to control its own change detection (unless every components that needs that adds an extra wrapper in its template and creates a new scope for it). Furthermore, this approach deviates from how things work in Angular (2+). Although we can't get a 1-to-1 match in bahavior, keeping the concepts and usage patterns as similar as possible would make sense to me. Still, I don't have a brilliant alternative suggestion 😁 Maybe the Ideally, I would like an easy way to comfigure this on the CDO (Component Definition Object) - similar to Angular's But I think these can be built on top of In any case, I wouldn't go too far recommending an alternative usage pattern (e.g. as shown with the |
Woohoo! I monkey-patched my way to (a naive implementation of) |
Looks like you and I look at this from 2 different perspectives @gkalpak :>
|
Darn, you're absolutely right. Oh, and that just made me think... in the case of My problem with your "user info" example is that your use case is a simple component with no children appearing once on the page. This is not a situation calling for perf optimisation. AngularJS suffers in situations when you have massive ng-repeat lists. So a better example is a "report" component, which has many table rows. Even if the parent report isn't scope-suspended, all of its ng-repeated child scopes will be, which gives you the orders of magnitude perf improvement you're looking for. In any case, I believe Pete's thought experiment renders this thread redundant. On the other hand, Maybe...? Definitely! :) Oh, it's gonna be a great Christmas :) |
@poshest regarding usage of |
@petebacondarwin sure, let's merge now and make some progress with this. With all that I've learned here, Do you mean will the later adding of I'm happy to go with not suspending I still argue that some developers might find this confusing, but with appropriate warnings in the documentation it can work. On the other hand, not suspending
And we can always manually "ignore" broadcasts on suspended tree branches by checking for ancestries' |
I think suspending the events would be a breaking change. I also don't think we should suspend / stop the events anyway because they are independent from the digest cycle. |
65360d5
to
701aeeb
Compare
src/ng/rootScope.js
Outdated
* Some of the things to consider are: | ||
* | ||
* * Any external event on a directive/component will not trigger a digest while the hosting | ||
* scope is suspended - even if the event handler calls `$apply()` or `$digest()`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well...if the scope itself isn't suspended, scope.$digest()
will run change detection, no?
Maybe change to "scope.$apply()
or $rootScope.$digest()
" to be more explicit.
src/ng/rootScope.js
Outdated
* transcluded scope will also be suspended, even if the scope from which the transcluded | ||
* scope inherits is not suspended. | ||
* * Multiple directives trying to manage the suspended status of a scope can confuse each other: | ||
* * A call to `$suspend` an already suspended scope is a no-op. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
an already --> on an already (?)
src/ng/rootScope.js
Outdated
* scope inherits is not suspended. | ||
* * Multiple directives trying to manage the suspended status of a scope can confuse each other: | ||
* * A call to `$suspend` an already suspended scope is a no-op. | ||
* * A call to `$resume` a non-suspended scope is a no-op. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a non-suspended --> on a non-suspended (?)
src/ng/rootScope.js
Outdated
* included in future digests until all its ancestors have been resumed. | ||
* * Resolved promises, e.g. from explicit `$q` deferreds and `$http` calls, trigger `$apply()` | ||
* against the `$rootScope` and so will still trigger a global digest even if the promise was | ||
* initialiated by a component that lives on a suspended scope. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initialized
+ created
= initialiated
😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
initialized
+ initiated
*/ | ||
$resume: function() { | ||
this.$$suspended = false; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! 👍
Sorry for the late replies 😞 Here you go:
The point is that they don't know if a component needs to be updated or not and they shouldn't know imo. (There are implementation details that only the component author should know and care about.)
Oh, good point (sadly) 😞 Fair enough then 😄
It was just an example. The exact same applies to a super-heavy table component with hundreds of ngRepeated rows and cells. The component itself should be in charge of whether/when the scopes can be suspended' i.e. the component author, not the component consumer.
Did it? 😛
In my PoC, I add the I am still pretty happy with how easy-ish it was to implement an |
701aeeb
to
f1d6c0e
Compare
TODO:
|
This can be very helpful for external modules that help making the digest loop faster by ignoring some of the watchers under some circumstance. Example: https://github.com/shahata/angular-viewport-watch Thanks to @shahata for the original implementation. Closes angular#5301
f1d6c0e
to
ba6874b
Compare
Thank you Pete and everyone for all your work on this. It will make a big difference to me and I hope others will find it useful. |
This can be very helpful for external modules that help making the digest loop faster by ignoring some of the watchers under some circumstance. Example: https://github.com/shahata/angular-viewport-watch Thanks to @shahata for the original implementation. Closes #5301 Closes #16308
This is a rebase of @shahata's original PR at #10658.
We need to think about @lgalfaso's concerns (#10658 (comment) and #10658 (comment)) before merging...