Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(observe-content): refactor so logic can be used without directive #11170

Merged
merged 4 commits into from
May 15, 2018

Conversation

mmalerba
Copy link
Contributor

@mmalerba mmalerba commented May 4, 2018

No description provided.

@mmalerba mmalerba requested a review from jelbourn May 4, 2018 22:17
@mmalerba mmalerba requested a review from crisbeto as a code owner May 4, 2018 22:17
@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label May 4, 2018
export class ContentObserverFactory {
constructor(private _mutationObserverFactory: MutationObserverFactory, private _ngZone: NgZone) {}

create(element: Element, debounce?: number) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we call this one observe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually start observing, it just creates the observer



/** A class that observes an element for content changes. */
export class ContentObserver {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could simplify the whole approach a bit by not having this class to begin with. Instead, ContentObserverFactory.observe can return an observable which creates a MutationObserver when something subscribes to it and then tears down the observer when there are no more subscriptions. We did something similar in the ScrollDispatcher. It means that we lose the ability to pause an observer and resume it with the same MutationObserver, but AFAIK there isn't much overhead to creating mutation observers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose keeping ContentObserverFactory how it was before (docs private API that we use internally), and then making ContentObserver an injectable that works like ScrollDispatcher and BreakpointObserver; I think having the user need to interact with two different types is more work than it has to be

@@ -120,6 +120,57 @@ describe('Observe content', () => {
});
});

describe('Observe content injectable', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since all the logic is in the injectable, maybe we should put all of the tests under this suite?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to leave the above tests alone to show that this doesn't change the behavior of the directive. Yes, we could add more tests here as well, but it felt like it would just be retesting the same logic

@mmalerba mmalerba force-pushed the obs-con branch 2 times, most recently from 82e86d9 to 492e9fb Compare May 8, 2018 22:35
@mmalerba
Copy link
Contributor Author

mmalerba commented May 8, 2018

Comments addressed. I also removed the part where we observe outside the zone. I think if it's a public API that returns an Observable, users will probably expect that Observable to emit inside the zone. Does this make sense to you guys?

/** A factory that creates ContentObservers. */
@Injectable({providedIn: 'root'})
export class ContentObserver {
private _observedElements = new Map<Element, {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a WeakMap

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I made it a Map so I could loop on destroy and clean up, but forgot to actually do that.


constructor(private _mutationObserverFactory: MutationObserverFactory) {}

observe(element: Element, debounce?: number): Observable<MutationRecord[]> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As user-facing JsDoc here and other method descriptions throughout?


constructor(private _mutationObserverFactory: MutationObserverFactory) {}

observe(element: Element, debounce?: number): Observable<MutationRecord[]> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning behind making debouce part of the API rather than letting subscribers debounce (or throttle or filter, etc) however they want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it here because it was part of the directive's logic, but I agree it doesn't really belong here, will remove.


private _unobserveElement(element: Element) {
if (this._observedElements.has(element)) {
if (!--this._observedElements.get(element)!.count) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you break up the decrement and the conditional? It's generally easier to understand that way, since many people have a hard time remembering the different between --value and value--


subscription.unsubscribe();
})));
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Should add a test for observing the same element twice, unobserving once and ensuring you still get events.
  • Should add a test for debouncing if it's part of the API

@mmalerba mmalerba force-pushed the obs-con branch 2 times, most recently from 42c014d to 45cc979 Compare May 9, 2018 18:51
@mmalerba
Copy link
Contributor Author

mmalerba commented May 9, 2018

PTAL

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker target: minor This PR is targeted for the next minor release and removed pr: needs review labels May 9, 2018
@ngbot
Copy link

ngbot bot commented May 9, 2018

I see that you just added the pr: merge ready label, but the following checks are still failing:
    failure status "continuous-integration/travis-ci/pr" is failing
    pending status "branch manager" is pending

If you want your PR to be merged, it has to pass all the CI checks.

If you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help.

@mmalerba
Copy link
Contributor Author

@jelbourn I moved the @Output() back outside the zone for now. This can be re-presubmitted

@tinayuangao tinayuangao merged commit ba57852 into angular:master May 15, 2018
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants