Creating a Packaged and Themeable Angular Material Component

Scenario

We need to create an Angular component, big.component.ts, that is NPM installable and also themeable.

The component makes text contained by its inner html bigger and uses the accent theme color for the text.

Approach

Workspace

We will be using the Angular Package Format for the component and module library, so first create a workspace for the library.

ng new big-workspace --create-application=false
cd big-workspace

Library

Then create the big component library.

ng g library big

And open the project in VSCode.

code .

Delete big.component.spec.ts , big.service.ts, and big.service.spec.ts .

rm projects/big/src/lib/big.component.spec.ts
rm projects/big/src/lib/big.service.ts
rm projects/big/src/lib/big.service.spec.ts

And update public-api.ts.

export * from './lib/big.component';
export * from './lib/big.module';

Styling

Add big.component.scss .

touch projects/big/src/lib/big.component.scss

Declare the non theme dependent CSS.

:host {
letter-spacing: 0.05em;
text-transform: uppercase;
font-size: 4rem;
font-family: monospace;
}

Add theming/index.scss

mkdir projects/big/src/lib/theming
touch projects/big/src/lib/theming/index.scss

Declare the theme dependent CSS.

@use '@angular/material' as mat;@mixin big-component-theme($theme) {
$accent: map-get($theme, accent);
fs-big {
color: mat.get-color-from-palette($accent);
}
}

Note that we are naming the theme file for the component index.scss such that we can import it like this (Which is symmetric with how Angular Material does it in general).

@use "@fireflysemantics/big-component-example/theming" as theme;

Create a big.component.html

touch projects/big/src/lib/big.component.html

Put the template in that.

<p>big works!</p>

Change the implementation of big.component.ts .

import { Component, OnInit } from '@angular/core';@Component({
selector: 'fs-big',
templateUrl: './big.component.html',
styleUrls: ['./big.component.scss']
})
export class BigComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}

Library Test Build

Build the library project.

ng build big

Library Assets Packaging

Create a src/CHANGELOG.md for our change log notes.

touch projects/big/src/CHANGELOG.md

In order for ng-packagr to copy our assets into the distribution when building we need to update the assets section of ng-package.json .

After the update run the build command ng build big again.

Check the dist folder. It now contains a src folder with our assets.

sandbox/big-workspace $ tree dist/big/src/dist/big/src/├── CHANGELOG.md
└── lib
└── theming
└── index.scss
2 directories, 2 files

We also need to create an exports block for index.scss in the libraries package.json file.

This enables clients of our component to import the theme like this:

@use "@fireflysemantics/big-component-example/theming" as theme;

Note that we also changed the package name.

"name": "@fireflysemantics/big-component-example",

Local Testing

Test the component. To do this we will generate a test application.

ng generate application test --style=scss --routing 

And add Angular Material.

ng add @angular/material

Add the big.module.ts BigModule to the test application app.module.ts .

import { BigModule} from 'big'
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BigModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Declare the fs-big component inside app.component.html .

<div>
<fs-big></fs-big>
</div>

Declare the CSS for the test rendering within app.component.scss .

div {
margin-top: 10rem;
display: flex;
justify-content: center;
align-items: center;
font-weight: bolder;
}

Within src create a themes.scss file.

@use '@angular/material' as mat;$my-theme-primary: mat.define-palette(mat.$indigo-palette, 500, 200, 800);$my-theme-accent: mat.define-palette(mat.$cyan-palette);
$my-theme-warn: mat.define-palette(mat.$deep-orange-palette, A200);
$light-theme: mat.define-light-theme(
$my-theme-primary,
$my-theme-accent,
$my-theme-warn
);

And add the themes to styles.scss .

We can now run ng serve -o and we can see that it renders our component.

Packaging

Open package.json for the library and change the name.

"name": "@fireflysemantics/big-component-example",

Workspace Package Scripts

In order to build and publish the project add the following package.json scripts.

Install Global

This will install @jsdevtools/version-bump-prompt for semantic versioning.

"ig": "npm install -g @jsdevtools/version-bump-prompt",    

Build

"b": "ng build big",    

Bump Patch

Bump the package patch version.

"bp": "cd projects/big && bump patch",    

Publish

We copy over README.md over the library README.md that way we only maintain the workspace/README.md file.

"p": "cp README.md ./projects/big/ && npm run bp && npm run b && cd dist/big/ && npm publish"

Publish to NPM

The first time we publish we need to add --access public so we will run the p script manually the first time.

cp README.md ./projects/big && npm run b && cd dist/big/ && npm publish --access public

Now we can find @fireflysemantics/big-component-example on NPM.

We also have this project on Github as a reference.

And now it’s on Github. Feel free to clone it and use it as a reference.

Client Testing

We will also test the package on Stackblitz in a Web Container.

Within theme.scss we load our custom component theme.

@use '@fireflysemantics/big-component-example/theming' as theme;

Related Concepts

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ole Ersoy

Ole Ersoy

Founder of Firefly Semantics Corporation