Components: BreakpointObserver doesn't emit immediately

Created on 29 Oct 2018  路  6Comments  路  Source: angular/components

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));
md5-71d0ce4110d13b2fe1c4d90b039bd6f1




md5-bde2191b57c3a266b8a78adf9c3374d6


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

cdlayout

Most helpful comment

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

All 6 comments

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

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));
md5-71d0ce4110d13b2fe1c4d90b039bd6f1




md5-bde2191b57c3a266b8a78adf9c3374d6


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.

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

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:

2
3

This seems to be fixed in master. Closing.

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._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dzrust picture dzrust  路  3Comments

Miiekeee picture Miiekeee  路  3Comments

3mp3ri0r picture 3mp3ri0r  路  3Comments

vitaly-t picture vitaly-t  路  3Comments

RoxKilly picture RoxKilly  路  3Comments