loading...

Angular theme provider

aziziyazit profile image Azizi Yazit Updated on ・3 min read

Providing theme provider is a MUST in component library. All the components should be able to gain access to the Theme Object. This includes palettes, spacings, sizings and many more.

In Angular, providing the theme provider is easy to be accomplished as the Angular have this ability out-of-the-box.

In this post we are using forwardRef and ngTemplate

forwardRef

Basically forwardRef is just a function that captures a class reference into closure. By using forwardRef we be able to get access to the component reference and use it in others components.

constructor(
  @Inject(forwardRef(() => YourComponentName))
  private yourComponentName: YourComponentName
) {}

ng-template

ngTemplate can use properties of its parent context and can have its own private context.

<parent>
  <ng-template let-parentData1="data1" let-parentData2="data2">
    <!-- The "data1" and "data2" is available to be used here -->
  </ng-template>
</parent>

In above example, the data2 and data2 is the data's from Parent Component

ThemeProviderComponent

ThemeProviderComponent is the root component or the parent component for the other components. All the components must be wrapped in this provider component in order to gain access to the theme object. It's highly recommended to wrap your entire app at the highest level (see below)

<theme-provider>
 <app>
  <layout>
   <grid>
    <div> 
    </div>
   <grid>
  <layout>
 </app>
</theme-provider>

ThemeProviderComponent snippet code

@Component({
  selector: "theme-provider",
  template: {% raw %}`
    <ng-container *ngIf="childAsTemplate">
      <ng-container *ngTemplateOutlet="template; context: theme"></ng-container>
    </ng-container>
    <ng-content *ngIf="!childAsTemplate"></ng-content>
  `{% endraw %}
})
export class ThemeProviderComponent implements AfterContentInit {
  @ContentChild(TemplateRef, { static: false }) template!: TemplateRef<any>;
  theme: any = {
    palette,
    scales
  };
  childAsTemplate: boolean = false;

  constructor() {}

  ngAfterContentInit() {
    this.childAsTemplate = this.template && true;
  }
}

palette snippet code

const palette = {
  uiBlueDarker: "#071d40",
  uiBlueDark: "#0d3880",
  uiBlue: "#184da8",
  uiBlueLight: "#2765cf",
  uiBlueLighter: "#e60278"
  ...
};

scales snippet code

const scales = {
  "spacing-01": "0.125rem",
  "spacing-02": "0.25rem",
  "spacing-03": "0.5rem",
  "spacing-04": "0.75rem"
  ...
};

palette and scales is the theme items provided for child components usage

usages

used in Component

In this example, we created a button component that accept color and scale as an inputs and use the theme object provided by ThemeProviderComponent for palette and scales values.

@Component({
  selector: "my-button",
  template: {% raw %}`
    <button type="button" [style.color]="themeProvider?.theme?.palette[color]" [style.font-size]="themeProvider?.theme?.scales['spacing-0'+scale]">
      <ng-content></ng-content>
    </button>
  `{% endraw %}
})
export class MyButtonComponent {
  @HostBinding("class") className;
  @Input() color: string;
  @Input() scale: number;

  constructor(
    @Inject(forwardRef(() => ThemeProviderComponent))
    private themeProvider: ThemeProviderComponent
  ) {}

}

we get the reference of ThemeProviderComponent by using forwardRef. In the template markup, we utilised the theme for palette and scale.

Below is code implementation for my-button component

<theme-provider>
<my-button
[color]="'uiBlue'"
[scale]="9">
Button
</my-button>
</theme-provider>




used in ngTemplate

In this example we customised my-button component and gain access the theme object thru ngTemplate

<theme-provider>
<ng-template let-palette="palette" let-scales="scales">
<my-button>
<div
[style.color]="palette['uiYellow']"
[style.background-color]="palette['uiPink']"
[style.font-size]="scales['spacing-09']"
>
Button
</div>
</my-button>
</ng-template>
</theme-provider>




Summary

The theme provider let us use the theme object like palette, spacing and sizing in scripting layer rather than stylesheet layer. For those who more comfortable in styling the component in JavaScript, this pattern can help to speed up styling works.

You can get all the codes used in this post here - https://stackblitz.com/edit/theme-provider

Posted on by:

aziziyazit profile

Azizi Yazit

@aziziyazit

Component library developer (angular and react)

Discussion

markdown guide