In my previous blog post, I described a performance problem with the special responsive features of the Angular flex-layout module. Combined use of fxShow and fxHide was killing my app’s performance. Example:
<button mat-button fxHide="true" fxShow.gt-sm="true"
As I studied the documentation more I learned that the above syntax is overkill. The following is easier and does the same thing:
<button mat-button fxHide fxShow.gt-sm
This simplification makes no difference in performance, however.
The .gt-sm means “when greater than small.” A few HTML tags like this one don’t hurt, but I might have a few thousand, generated by an ngFor.
Solution: go imperative – use flex-layout’s ObservableMedia
The “dot” modifiers like the .gt-sm shown above are known as mediaQuery alias suffixes. While they are one of the main attractions of the flex-layout module’s responsive API, I got rid of them all. Eventually I went ahead and removed the static API also.
<button mat-buttonfxHidefxShow.gt-sm^ ^ | | remove static API ----+ +--- remove responsive API
I put in flex-layout’s way of checking mediaQuery changes, the isActive method of the ObservableMedia service. This is in my main app.component.ts. Here are the relevant parts:
import { ObservableMedia } from '@angular/flex-layout'; ... constructor ( public media: ObservableMedia, ... sizeNum: Object = {'PHONE':1,'TABLET':2,'HD':3} activeSize(): number { if (this.media.isActive('xs')) {return this.sizeNum['PHONE'];} if (this.media.isActive('sm')) {return this.sizeNum['TABLET'];} else {return this.sizeNum['HD'];} }
Now I can use ngIf in my html:
<button mat-button *ngIf="activeSize()>=sizeNum['HD']"
In addition to implementing show/hide logic, I also was using mediaQuery alias suffixes on ngClass. Now I am doing this instead:
TypeScript
classList(size:number): string[] { if (size>=this.sizeNum['HD']) {return ['small']} else if (size>=this.sizeNum['TABLET']) {return ['tiny']} else {return ['nano']} }
HTML
<img [src]="songImgUrl()" class="rounded" [ngClass]="classList(activeSize())"
Conclusion
The turning point was removing the responsive API directives. That restored the performance but I had some constructs like this:
<button mat-button [fxShow]="activeSize()>=sizeNum['HD']"
This is the same thing as Angular’s stock ngIf. So I decided to switch everything to native Angular DOM directives and use only the ObservableMedia from flex-layout. I am satisfied with this solution because:
- the performance is fully restored
- it’s almost entirely stock Angular
- it’s clear that I am setting up just three breakpoints
- it still taps into, rather than duplicates, the Material Design breakpoints
[25-Jun-2018 I can do constants better now. See global constants.]