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

BreakpointObserver doesn't emit immediately #13852

Closed
henry-alakazhang opened this issue Oct 28, 2018 · 6 comments
Closed

BreakpointObserver doesn't emit immediately #13852

henry-alakazhang opened this issue Oct 28, 2018 · 6 comments
Assignees

Comments

@henry-alakazhang
Copy link

henry-alakazhang commented Oct 28, 2018

Bug, feature request, or proposal:

Bug

What is the expected behavior?

In v6, BreakpointObserver immediately emits when subscribed to

What is the current behavior?

In v7, BreakpointObserver does not emit immediately.

What are the steps to reproduce?

https://stackblitz.com/edit/angular-material2-issue-breakpointobserver

The initial code is using CDK 6.4.7. The console prints only true and string.
If you swap the contents of package.json with package.json.2, this bumps up the version to 7.0.0. The console will prints null, then true. The behaviour of | async has not changed, evidenced by the fact that string is still printed in the same way.

What is the use-case or motivation for changing an existing behavior?

This causes issues in any code that was not using a strict negation check (eg. !(isMobile$ | async)), as the check becomes truthy temporarily. As an example, this code:

https://stackblitz.com/edit/angular-material2-issue-breakpointobserver-usecase

this.isMobile$ = this.breakpointObserver
  .observe(Breakpoints.XSmall)
  .pipe(map(obs => obs.matches));
this.isNotMobile$ = this.breakpointObserver
  .observe(Breakpoints.XSmall)
  .pipe(map(obs => !obs.matches));
<div *ngIf="!(isMobile$ | async)">
  {{ foo$ | async }}
</div>
<div *ngIf="(isNotMobile$ | async)">
  {{ bar$ | async }}
</div>

You would expect the two to have the same functionality: nothing is displayed for mobile, and everything is displayed for non-mobile. This is exactly what happens if you move between mobile and non-mobile.

However, if you load it in mobile view (ie. when nothing should show), foo$ is still subscribed to (you can tell by the log), because breakpointObserver doesn't emit immediately and isMobile$ | async === null, which is false-y. This can cause unnecessary network requests to be fired, for example.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

@angular/cdk 7.0.0

Is there anything else we should know?

This isn't necessarily a bad thing - any problematic behaviour caused can be fixed with strict checks or using startWith, however given it seems to be an undocumented side-effect of #11007 , I'm not sure if it's intended or not

@manklu
Copy link

manklu commented Oct 29, 2018

You shouldn't wrap your observable with a function. If you use the observable directly it works as expected.

https://stackblitz.com/edit/angular-material2-issue-breakpointobserver-vfv5jl

For your sample, something like the following seems to work:

this.isNotMobile$ = this.isMobile$
  .pipe(map(matches => !matches))
<ng-container *ngIf="(isNotMobile$ | async) && (data$ | async) as data">
  <!-- use data -->
</ng-container>

https://stackblitz.com/edit/angular-material2-issue-breakpointobserver-lyaaca

@henry-alakazhang
Copy link
Author

henry-alakazhang commented Oct 29, 2018

The function was just to log and make it clearer what was going on. My stackblitz was admittedly contrived to show everything (and remove the possibility of other things affecting it)

Here's a slightly different example, much closer to the actual use case I had an issue with:
https://stackblitz.com/edit/angular-material2-issue-breakpointobserver-usecase

this.isMobile$ = this.breakpointObserver
  .observe(Breakpoints.XSmall)
  .pipe(map(obs => obs.matches));
this.isNotMobile$ = this.breakpointObserver
  .observe(Breakpoints.XSmall)
  .pipe(map(obs => !obs.matches));
<div *ngIf="!(isMobile$ | async)">
  {{ foo$ | async }}
</div>
<div *ngIf="(isNotMobile$ | async)">
  {{ bar$ | async }}
</div>

You would expect the two to have the same functionality: nothing is displayed for mobile, and everything is displayed for non-mobile. This is exactly what happens if you move between mobile and non-mobile.

However, if you load it in mobile view (ie. when nothing should show), foo$ is still subscribed to (you can tell by the log), because breakpointObserver doesn't emit immediately and isMobile$ | async === null, which is false-y. This only occurs in 7.0

I've updated the issue with this clearer use case.

@manklu
Copy link

manklu commented Oct 30, 2018

@henry-alakazhang !(isMobile$ | async) is the same as calling a function.

@josephperrott josephperrott self-assigned this Nov 26, 2018
@intellix
Copy link

I guess this is fixed now as it's synchronous:

this.breakpointObserver.observe(Breakpoints.Handset).subscribe(() => console.log(1));
Rx.of(2).subscribe(console.log);
console.log(3);

Outputs:

1
2
3

@mmalerba mmalerba added the needs triage This issue needs to be triaged by the team label May 20, 2020
@crisbeto crisbeto added area: cdk/layout and removed needs triage This issue needs to be triaged by the team labels May 27, 2020
@crisbeto
Copy link
Member

This seems to be fixed in master. Closing.

@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 Jun 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants