I've been writing a lot of exploring Angular providers and how to make them global vs how to make them apply to a specific route or screen. I'm taking a sidebar on that to talk about how to create a component that can easily be configurable.
You could easily add inputs which control how the component behaves, but I'm going to do something different by giving the component a custom service
What Component should we Build?
For the purposes of this sample let's build a component that formats a date.
Create the Infrastructure
There will be multiple files related to this component, so let's create a module. Fire off a few of these Angular CLI Commands2ng generate service date-formatter/dateFormatterConfig
3ng generate component dateFormatter
You should see something like this:
We generated the main component, a config service, and a module to wrap this all together.
Modify the Config Service
Open up the Config Service:
2
3@Injectable({
4 providedIn: 'root'
5})
6export class DateFormatterConfigService {
7
8 constructor() { }
9}
When I create a service related to a component, I do not want to use the providedIn metadata config object to automatically inject it. I want to give my component consumers more granular control. As such, let's remove that from the service. Additionally, let's add one piece of data for dateFormatterMask:
2
3@Injectable()
4export class DateFormatterConfigService {
5
6 dateFormatterMask = 'shortDate';
7
8 constructor() { }
9}
This is super simple.
Create the Component
Open up the date-formatter.component.ts file:
2@Component({
3 selector: 'app-date-formatter',
4 templateUrl: './date-formatter.component.html',
5 styleUrls: ['./date-formatter.component.css']
6})
7export class DateFormatterComponent implements OnInit {
8 constructor() { }
9 ngOnInit(): void {
10 }
11}
This is the default component. First let's add an input, for the date that needs to be displayed. I defaulted it to a new Date() object instance:
2 displayDate = new Date();
Next, let's inject the DateFormatterConfig service and also the DatePipe into the constructor:
2 public datePipe: DatePipe) { }
Don't forget about the imports:
2import {DateFormatterConfigService} from './date-formatter-config.service';
3import {DatePipe} from '@angular/common';
Now open up the date-formatter-component.html file:
Pretty boring. Let's fix that to display the date and use the pipe and config service to format it::
That's all we need here.
Create the Component Module
Now, let's turn our attention to the component module:
2import { CommonModule } from '@angular/common';
3
4@NgModule({
5 declarations: [],
6 imports: [
7 CommonModule
8 ]
9})
10export class DateFormatterModule { }
We want to declare, and export the DateFormatterComponent:
2 DateFormatterComponent
3 ],
4 exports: [
5 DateFormatterComponent
6 ]
The imports can remain untouched, but we'll want to set up the two providers:
2 CustomDateFormatterConfigService,
3 DatePipe
4 ],
Don't forget to update all your imports:
2import {CommonModule, DatePipe} from '@angular/common';
3import {DateFormatterComponent} from '../../../projects/my-lib/src/lib/date-formatter/date-formatter.component';
4import {CustomDateFormatterConfigService} from '../../../projects/lazyloading/src/app/custom-date-formatter-config.service';
Use This Component
We should pretend that our users of this component are using it deep within some library and that we're importing from remote npm repository without close access to the code. In a module load the module from the lib:
2 // other stuff
3 DateFormatterModule
4 ],
Now add it to the view template of a component:
The load it up and see something like this:
Cool, but how do we change the display type? Easy; we just replace the config. First, create your own config class by loading a replacement provider. This is one way:
2 {provide: DateFormatterConfigService, useValue: {dateFormatterMask: 'medium'}}]
3]
Whenever the DateFormatterConfigService is loaded, instead we'll load the replacement object.
Rerun the code and you'll see this in the browser:
This has been a great approach for my team to build reusable components, but still provider the consumers a way to configure different options.