When developing applications, eventually we'll have some versioning to reflect our changes. An indicator, visual or on an HTML element, can be helpful when we have CI/CD flows and want to confirm pushed changes on production.
In this article, we will cover the attribute approach on HTML element. We'll use the bumped version from package.json and add attribute to our app's element like Angular does with the ng-version
attribute.
The Code
Let's dive right in. To achieve this, we'll need to have access to the app's native element. We could get access to the element from the AppComponent
, but there is a nicer way to do this, and that is by using APP_BOOTSTRAP_LISTENER
token.
First things first, depending on TS config, we may need to set resolveJsonModule
to true
so that we are allowed to import needed values from package.json. If your moduleResolution
is set to bundler
you can skip this step.
// tsconfig.json
{
"compilerOptions": {
"resolveJsonModule": true
// ...
}
// ...
}
Then we need to define a provider using APP_BOOTSTRAP_LISTENER
token. In callback we'll get the ComponentRef
which will be used for setting up our version attribute.
// version.provider.ts
import {
APP_BOOTSTRAP_LISTENER,
ComponentRef,
Provider,
Renderer2,
} from '@angular/core';
import packageJson from './../package.json';
import { AppComponent } from './app.component';
function versionFactory() {
return ({ injector, location }: ComponentRef<AppComponent>): void => {
const renderer = injector.get(Renderer2);
const element = location.nativeElement;
renderer.setAttribute(
element,
`${packageJson.name}-version`,
packageJson.version
);
};
}
export function provideVersion(): Provider {
return {
provide: APP_BOOTSTRAP_LISTENER,
useFactory: versionFactory,
multi: true,
};
}
In the end all we have left is to add a provider to our ApplicationConfig
.
// app.config.ts
import { provideVersion } from './config/version.bootstrap';
export const appConfig: ApplicationConfig = {
providers: [
provideVersion(),
// ...
],
};
The Token
APP_BOOTSTRAP_LISTENER - A DI token that provides a set of callbacks to be called for every component that is bootstrapped.
During the Angular initialization process - when instantiating the application (and executing loadComponent method), registered listeners under the APP_BOOTSTRAP_LISTENER
token will be called and the app component will be passed. At this moment the app component is already attached to DOM and has all the needed services provided. Registered listeners under the APP_BOOTSTRAP_LISTENER
token will be called after the NavigationStart
event is triggered for the first time of the app's lifetime.
One could ask, when to use the APP_BOOTSTRAP_LISTENER
opposed to APP_INITIALIZER
for setting various configs in applications? - A General rule of thumb can be based on the need of app component.
Use APP_BOOTSTRAP_LISTENER when you:
- Need access to app component
- Potentially need it after child components are added to app component
- Potentially need it before loading other routed modules/components
Use APP_INITIALIZER when you:
- Need access to services only
- Potentially need app bootstrap to wait before sync/async work is done
Conclusion
Having a version attribute on an element is a nice way for the DEV/QA/TS/CS team to follow up on app's changes and have better communication. Also, by using bootstrap tokens, we can neatly "tuck" away all the configs for the application.
If you found this post helpful, feel free to share it with others who might benefit.
Thanks for reading!
Top comments (0)