Bug
In v6, BreakpointObserver immediately emits when subscribed to
In v7, BreakpointObserver does not emit immediately.
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.
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.
@angular/cdk 7.0.0
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
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._
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:
https://stackblitz.com/edit/angular-material2-issue-breakpointobserver-lyaaca