Skip to content

Style Builders

CaerusKaru edited this page Jan 18, 2020 · 28 revisions

Reduced to a simplistic form, the Angular Layout library is composed of two things:

  • Directives that inject styles [FlexBox CSS or CSSGrid] inline onto DOM elements.
  • Responsive MediaQuery features that trigger updates to injected styles based on current viewport size (eg breakpoint).

Library Configuration

Traditionally developers customize Angular Layout with either:

import {FlexLayoutModule} from '@angular/flex-layout';

FlexLayoutModule.withConfig({
    addFlexToParent       : true,
    disableVendorPrefixes : true
  }, 
  breakpoints
);

Configuration Limitations

Obviously the Runtime overrides are limited... and also prevent the developer from customizing the actual styles that will be injected inline.

With the release of v7.0.0-beta.20, however, Angular Layout has introduced the StyleBuilder to allow for greater flexibility for developers.

NOTE: This feature is best suited for advanced users; that is, those who want to dig deeper into the library. Understanding style builders is not a requirement for using the library

Performance Considerations

Prior to StyleBuilders, the style generation was hard-coded and non-performant... using a simple trigger 'n generate algorithm.

  • Each directive instance would get an event to rebuild the styles, and then
  • Each directive would generate css styles... every time

Not only is this approach inefficient, it presents a problem if a user disagrees with the default style-generation algorithm. And subsequent changes to the algorithm, of course, would then affect all users.

The StyleBuilder API resolves these issues & provides the greatest flexibility and performance without compromising existing usability.


StyleBuilder API

export abstract class StyleBuilder {
  shouldCache = true;

  abstract buildStyles(input: string, parent?: Object): StyleDefinition;

  sideEffect(_input: string, _styles: StyleDefinition, _parent?: Object) {
    // This should be a no-op unless an algorithm is provided in a subclass
  }
}
  • buildStyles(): All style computation that is destined for the host directive happens here, and is returned in the output. Not only that, but the output from this computation is cached, so if there is no need to regenerate styles for the same input, you don't have to!
  • shouldCache: a flag that tells the base directive whether or not to store the computed value from buildStyles in the cache (and also whether to retrieve it from the cache before generation)
  • sideEffect: this is meant as a salve in the event that extra computation is needed that doesn't occur on the main directive, and happens regardless of the caching mechanism. This fires each and every time an input event reaches the StyleBuilder

Finally, a note on the parent parameter. Some directives may requires information from its parent that can't be retrieved from dependency injection alone. To resolve this, we pass in a parent object for certain directives that contain vital information.

Benefits

  1. Full control over the style generation process on an ad-hoc basis, i.e. you don't need to override all directives if you don't want to, you can just override one or two
  2. The ability to handle custom inputs on an organizational/localization level, i.e. different languages for directions in fxLayoutAlign, or different keywords, i.e. cc === center center in fxLayoutAlign
  3. Better performance using the new internal memoization [caching mechanism]

Usage

To override injected styles, developers implement a custom StyleBuilder and use Angular DI to inject an instance of the custom builder instead of the default builder.

For instance, to override the StyleBuilder for fxFlexAlign:

import { Injectable, NgModule } from '@angular/core';
import {
  StyleBuilder,
  StyleDefinition,
  FlexAlignStyleBuilder,
} from '@angular/flex-layout';

@Injectable()
export class CustomAlignStyleBuilder extends StyleBuilder {
  buildStyles(input: string): StyleDefinition {
    return { /** CUSTOM_CSS */ };
  }
}

@NgModule({
  ...,
  providers: [
    {
		provide : FlexAlignStyleBuilder,    // when default is requested
    	useClass: CustomAlignStyleBuilder  // provide instead custom builder
	}
  ]
})
export class MyAppModule {}

Overriding Defaults

It's now very simple to provide a new default value for directives. Simply create a style builder that calls the original, but with the new default.

@Injectable()
export class CustomAlignStyleBuilder extends FlexAlignStyleBuilder { // <--- notice we extend the real style builder, not the abstract class
  buildStyles(input: string): StyleDefinition {
    input = input || NEW_DEFAULT;    // <--- set this here or in a const variable
	return super.buildStyles(input); // now send this off to the main style builder for processing
  }
}

This feature provides developers with complete control to intercept and override or enhance injected styles.

Clone this wiki locally