
I can provide that!


Daniel Worthy
Software Engineer on Match
- Been with JBH since Fall 2023
- Been building web apps 'professionally' since 2007
- I like to build stuff -> Wood, 3D printing, LEGO
- I'm a cat person... now
- I've become obsessed with getting tattoos
What we are going to look at?
Dependency Injection!
- Scope of Providers
- Dependency Providers
- Bootstrapping an App
Why are we going going to look at this?
- It's important!
- Back to basics
- Standalone Components/Apps
- Angular Updates (17+)
Let's Use A Service
@Component({
selector: 'app-my-test-component',
standalone: true,
imports: [CommonModule],
templateUrl: './my-test-component.component.html',
styleUrl: './my-test-component.component.css',
})
export class MyTestComponentComponent {
constructor(private myTestDataService: MyTestDataService) {}
}
@Injectable()
export class MyTestDataService {
counter = 0;
increment() {
this.counter++;
}
}
The Error

What's causing this and how can we fix it?
The compiler knows where the code lives.
Angular needs to know how to serve up the service.
Ways to Provide a Block of Code
- Provide in Root
- Provide in Component
- Provide in Module
- Provide in Application Config (Standalone)
- Provide in Route
Provide In Root
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class MyTestDataService {
counter = 0;
increment() {
this.counter++;
}
}
- Introduced in Angular 6
- Out of the box w/ CLI
- Tree Shakeable
- Can be referenced everywhere it is imported
- Globally scoped
Provide In Component
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MyTestDataService } from '../my-test-data.service';
@Component({
selector: 'app-my-test-component',
standalone: true,
imports: [CommonModule],
providers: [MyTestDataService],
templateUrl: './my-test-component.component.html',
styleUrl: './my-test-component.component.css',
})
export class MyTestComponentComponent {
constructor(private myTestDataService: MyTestDataService) {}
get count() {
return this.myTestDataService.counter;
}
addToCounter() {
this.myTestDataService.increment();
}
}
Provide In Module
import { MyTestDataService } from './my-test-data.service';
@NgModule({
providers: [MyTestDataService],
})
export class AppModule {}
Provide In Component/Module
Provide in Route
const routes: Routes = [
{
path: 'user',
component: UserComponent,
providers: [UserService]
},
];
- Introduced in Angular 14
- Serves as a group module replacement
- Learned about during the Speed Run
Bonus Reading: Resolution Modifiers
Bonus Reading Examples
@Component({
selector: 'app-self-no-data',
templateUrl: './self-no-data.component.html',
styleUrls: ['./self-no-data.component.css']
})
export class SelfNoDataComponent {
constructor(@Self() @Optional() public leaf?: LeafService) { }
}
Dependency Injection Providers
Simple Service
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, delay } from 'rxjs';
import { User } from '../models/my-test-data-model';
@Injectable()
export class MyTestDataService {
constructor(private httpClient: HttpClient) {}
getData():Observable<User[]> {
return this.httpClient
.get<User[]>('https://jsonplaceholder.typicode.com/users')
.pipe(delay(1000));
}
}
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, delay } from 'rxjs';
import { User } from '../models/my-test-data-model';
@Injectable()
export class MyTestDataService {
constructor(private httpClient: HttpClient, private baseUrl:string) {}
getData():Observable<User[]> {
return this.httpClient
.get<User[]>(this.baseUrl + '/users')
.pipe(delay(1000));
}
}
useValue
import { InjectionToken, ValueProvider } from '@angular/core';
export const BASE_URL_TOKEN = new InjectionToken<string>('baseUrl');
@NgModule({
providers: [
{
provide: BASE_URL_TOKEN,
useValue: 'https://jsonplaceholder.typicode.com/'
}
],
})
export class AppModule {}
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, delay } from 'rxjs';
import { User } from '../models/my-test-data-model';
import { BASE_URL_TOKEN } from './wherever.ts';
@Injectable()
export class MyTestDataService {
constructor(
private httpClient: HttpClient,
@Inject(BASE_URL_TOKEN) private baseUrl: string
) {}
getData():Observable<User[]> {
return this.httpClient
.get<User[]>(this.baseUrl + '/users')
.pipe(delay(1000));
}
}
TestBed.configureTestingModule({
declarations: [AppComponent],
providers: [
{
provide: MyTestDataService,
useValue: {
getData: jest.fn().mockReturnValue('abc123'),
getSomeOtherValue: jest.fn().mockReturnValue({}),
},
},
],
});
useClass
@NgModule({
providers: [
{
provide: MyTestDataService,
useClass: MyTestDataMockService
}
],
})
export class AppModule {}
useExisting
@NgModule({
providers: [
Security2Point0Service,
{
provide: SecurityService,
useExisting: Security2Point0Service
}
],
})
export class AppModule {}
Back to old code
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, delay } from 'rxjs';
import { User } from '../models/my-test-data-model';
export class MyTestDataService {
constructor(private httpClient: HttpClient, private baseUrl:string) {}
getData():Observable<User[]> {
return this.httpClient
.get<User[]>(this.baseUrl + '/users')
.pipe(delay(1000));
}
}
useFactory
@NgModule({
imports: [HttpClientModule]
providers: [
{
provide: MyTestDataService,
useFactory:
(HttpClient, baseUrl) => new MyTestDataService(HttpClient, baseUrl),
deps:[HttpClient, environment.baseUrl]
}
],
})
export class AppModule {}
useFactory
@NgModule({
imports: [ConfigurationService]
providers: [
{
provide: SecurityService,
useFactory: (configurationService: ConfigurationService) => new SecurityService(
{
basePath: environment.apiUrl,
orgId: configurationService.getOrgId()
}
),
deps: [ConfigurationService]
},
],
})
export class AppModule {}
What about multi?
{
provide: APP_INITIALIZER,
useClass: configInitializer,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: apiKeyInterceptor,
multi: true
}
Multi In code
constructor(@Inject(DataStoreClasses) public storages: DataStoreClasses[]) {
console.log('DATA_STORES_CLASSES', storages.length);
}
Module Providing
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes),
HttpClientModule,
],
providers: [MyOwnService],
bootstrap: [AppComponent],
})
export class AppModule {}
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
Standalone App Providing
import { HttpClientModule, provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes),
provideHttpClient(),
importProvidersFrom(HttpClientModule),
],
};
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);
HttpClientModule w/ Interceptors
@NgModule({
declarations: [AppComponent],
imports: [
HttpClientModule,
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: ApiKeyInterceptor,
multi: true
}
],
bootstrap: [AppComponent],
})
export class AppModule {}
HttpClientModule w/ Interceptors
import { withInterceptors, withInterceptorsFromDi, provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([ApiKeyInterceptor])
)
{
provide: HTTP_INTERCEPTORS,
useClass: ApiKeyInterceptorClass,
multi: true
},
provideHttpClient(withInterceptorsFromDi())
],
};
Provide a Provider
import { Provider } from '@angular/core';
import { HeaderService } from './header.service';
import { Header } from './header.class';
export const provideHeader: () => Provider[] = () => [
HeaderService,
{ provide: Header, useExisting: HeaderService }
]
Book Recommendation

Be Useful: Seven Tools for Life
Contact

Links
Presentation Framework -Reveal.js