Flex-layout performance solution

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-button fxHide fxShow.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.]

Leave a Reply

Your email address will not be published. Required fields are marked *