Angular theme provider
Tags: angular, themeprovider
Published: 2019-08-24 18:04:18

---
title: Angular theme provider
published: true
description: 
tags: angular, themeprovider
---

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](https://angular.io/api/core/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.

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

## ng-template
ngTemplate can use properties of its parent context and can have its own private context.

~~~html
<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 <b>data2</b> and <b>data2</b> 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)

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

### ThemeProviderComponent snippet code

~~~javascript
@Component({
  selector: "theme-provider",
  template: `
    <ng-container *ngIf="childAsTemplate">
      <ng-container *ngTemplateOutlet="template; context: theme"></ng-container>
    </ng-container>
    <ng-content *ngIf="!childAsTemplate"></ng-content>
  `
})
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

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

### scales snippet code

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

> <b>palette</b> and <b>scales</b> 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 <b>palette</b> and <b>scales</b> values.

~~~javascript
@Component({
  selector: "my-button",
  template: `
    <button type="button" [style.color]="themeProvider?.theme?.palette[color]" [style.font-size]="themeProvider?.theme?.scales['spacing-0'+scale]">
      <ng-content></ng-content>
    </button>
  `
})
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

~~~html
<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

~~~html
<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](https://stackblitz.com/edit/theme-provider)