V19 Sharing DI Services
Here are some steps to include, when creating a public library that uses services from DI, such as HttpClient.
We cater the example on this page to using HttpClient.
But, they apply, equally, to any library that consumes from DI.
Library Service
Our example is a library service that uses HttpClient.
It will inject HttpClient from DI.
NOTE: Our service example will require that something registers HttpClient with DI, so that no error occurs at runtime.
So, we have a providers instance, below, that needs to be run by any consuming app.
Think of it as an initialization for the library.
Here's what the library service looks like:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class MyLibraryService {
private http = inject(HttpClient);
constructor() {}
getData() {
return this.http.get('https://api.example.com/data');
}
}
Note that the above example injects the HttpClient, directly, into a private variable, instead of in the constructor.
This is a new injection method, allowed in V18.
Library Providers
Next, our library needs to include a providers instance that any consuming library or app can add to its providers array.
For this, we create a file, lib-providers.ts, in the src folder of our library, that looks like this:
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';
export const LIB_OGAWEBUISHAREDKERNEL_PROVIDERS: ApplicationConfig =
{
providers: [provideHttpClient(withFetch())]
};
Note: We've set the provider name to be unique to our library.
You, as well, will want to do this, to prevent colliding with the providers entry of another library.
Library Export
The public-api.ts file of our library needs to include an export statement for the providers instance, above:
export * from './lib-providers';
Consuming Library
Any library that consumes our library needs to add our provider to its providers array, like this lib-providers.ts:
import { ApplicationConfig } from '@angular/core';
import { LIB_OGAWEBUISHAREDKERNEL_PROVIDERS } from 'my-library';
export const CONSUMER_LIB_PROVIDERS: ApplicationConfig = {
providers: [
...LIB_OGAWEBUISHAREDKERNEL_PROVIDERS.providers // ✅ Inherit HttpClient from `my-library`
]
};
The above will ensure that any consumer of our consuming library, will also include the providers from our library.
Consuming Service
Any service in our consuming library or app that consumes the service from our library, will inject it, like this:
import { Injectable, inject } from '@angular/core';
import { MyLibraryService } from 'my-library';
@Injectable({
providedIn: 'root'
})
export class ConsumerLibraryService {
private myLibraryService = inject(MyLibraryService);
fetchData() {
return this.myLibraryService.getData(); // ✅ Calls the service from `my-library`
}
}
Consuming Library Public-API.ts
The public-api.ts file of the consuming library, will include a similar export of its providers instance, so that any registrations are performed by consumers of it.
That public-api.ts includes these elements:
export * from './consumer-service';
export * from './providers'; // ✅ Expose CONSUMER_LIB_PROVIDERS for easy consumption
Consuming Application
And, any application that consumes our library, or the intermediate consuming library, needs to import it and add its providers to the app's bootstrap statement, like this:
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { CONSUMER_LIB_PROVIDERS } from 'consumer-library';
bootstrapApplication(AppComponent, CONSUMER_LIB_PROVIDERS);
NOTE: If your consuming app does NOT register the DI provider of the library, you will see a runtime error, that the injector cannot resolve the HttpClient.
Consuming Component
This leaves our last example of a component of our app, that consumes the service from our library.
For a pre-V19 app, it looks like this:
import { Component, inject } from '@angular/core';
import { ConsumerLibraryService } from 'consumer-library';
@Component({
selector: 'app-root',
template: `<button (click)="loadData()">Fetch Data</button>`,
})
export class AppComponent {
private consumerService = inject(ConsumerLibraryService);
loadData() {
this.consumerService.fetchData().subscribe(data => {
console.log('Received Data:', data);
});
}
}
For a v19 and later app, the provider gets referenced in the app.config.ts file, like this:
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { LIB_OGAWEBUISHAREDKERNEL_PROVIDERS } from '../../../lib-oga-webui-sharedkernel/src/lib-providers';
export const appConfig: ApplicationConfig = {
providers:
[
provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes),
LIB_OGAWEBUISHAREDKERNEL_PROVIDERS.providers
]
};
No Comments