Providing a theme provider is a must in a component library. All components should be able to gain access to the Theme Object. This includes palettes, spacings, sizings, and more.
In Angular, providing the theme provider is easy to accomplish as Angular has this ability out of the box.
In this post, we are using forwardRef and ngTemplate.
forwardRef
Basically, forwardRef is a function that captures a class reference into a closure. By using forwardRef, we can access the component reference and use it in other components.
constructor(
@Inject(forwardRef(() => YourComponentName))
private yourComponentName: YourComponentName
) {}
~~~{% endraw %}
## ng-template
{% raw %}`ngTemplate`{% endraw %} can use properties of its parent context and can have its own private context.{% raw %}
~~~html
<parent>
<ng-template let-parentData1="data1" let-parentData2="data2">
<!-- The "data1" and "data2" are available here -->
</ng-template>
</parent>
~~~{% endraw %}
> In the above example, **data1** and **data2** are data from the Parent Component.
## ThemeProviderComponent
{% raw %}`ThemeProviderComponent`{% endraw %} is the root or parent component for other components.
All components must be wrapped in this provider component to gain access to the theme object.
It’s highly recommended to wrap your entire app at the highest level (see below):{% raw %}
~~~html
<theme-provider>
<app>
<layout>
<grid>
<div></div>
</grid>
</layout>
</app>
</theme-provider>
~~~{% endraw %}
### ThemeProviderComponent snippet{% raw %}
~~~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;
}
}
~~~{% endraw %}
### palette snippet{% raw %}
~~~javascript
const palette = {
uiBlueDarker: "#071d40",
uiBlueDark: "#0d3880",
uiBlue: "#184da8",
uiBlueLight: "#2765cf",
uiBlueLighter: "#e60278",
// ...
};
~~~{% endraw %}
### scales snippet{% raw %}
~~~javascript
const scales = {
"spacing-01": "0.125rem",
"spacing-02": "0.25rem",
"spacing-03": "0.5rem",
"spacing-04": "0.75rem",
// ...
};
~~~{% endraw %}
> **palette** and **scales** are theme items provided for child components' usage.
## Usages
### Used in a Component
In this example, we create a button component that accepts {% raw %}`color` and `scale` as inputs and uses the theme object provided by `ThemeProviderComponent` for **palette** and **scales** values.
~~~javascript
@Component({
selector: "my-button",
template: `
<button
type="button"
[style.color]="themeProvider?.theme?.palette[color]"
[style.fontSize]="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 a reference to `ThemeProviderComponent` using `forwardRef`.
> In the template markup, we utilize the theme for `palette` and `scale`.
Below is an example usage of the `my-button` component:
~~~html
<theme-provider>
<my-button [color]="'uiBlue'" [scale]="9">
Button
</my-button>
</theme-provider>
~~~
### Used in ngTemplate
In this example, we customize the `my-button` component and gain access to the theme object through `ngTemplate`:
~~~html
<theme-provider>
<ng-template let-palette="palette" let-scales="scales">
<my-button>
<div
[style.color]="palette['uiYellow']"
[style.backgroundColor]="palette['uiPink']"
[style.fontSize]="scales['spacing-09']"
>
Button
</div>
</my-button>
</ng-template>
</theme-provider>
~~~
## Summary
The theme provider lets us use the theme object (like **palette**, **spacing**, and **sizing**) in the scripting layer rather than the stylesheet layer.
For those more comfortable styling components in JavaScript, this pattern can help speed up styling work.
You can get all the code used in this post here:
👉 [https://stackblitz.com/edit/theme-provider](https://stackblitz.com/edit/theme-provider)
Top comments (1)
like reactjs <3 thank bro