Providing in Angular - how to share data
Tags: javascript, beginners, programming, angular
Published: 2023-07-07 11:41:22

### Introduction
In the angular framework, dependency injection, and providers are one of the core features. This feature allows us to use a single data source in many places. For example, we can share one HTTP call result with many components to avoid too many requests for the same data. Another example could be to override one value or a class with another. In this article, I will explain the providers, how to define them, and how to use them in many places.

### What are the providers?
Providers in Angular are a source of value that could be shared between many recipients. It could be a static value like, for example, `string` or `boolean`, or it could be a whole class that uses the same provider instance. In the Angular role of provider, classes are covered by services. Services are a special kind of class with an `Injectable` decorator. Thanks to that, we can use other providers inside our class because they can be injected. By default, the `Injectable` decorator takes as an argument for the `providedIn` value `root`, meaning that this current service will be initiated at the root level of the app. More details about that You can find later in this article.

### Type of providers
The angular framework has a few types of providers which work differently but, in the end, serve data.

#### Injection token
One of the possible injectors is the `InjectionToken`.
It is the simplest provider whose main purpose is to represent values not contained in any class.
An example definition of the injection token is:
```typescript
export const timezoneToken = new InjectionToken<string>('My timezone');
```
It is generic and accepts type definition and `My timezone` as a description. It is useful when we forget about implementing a given injection token and the error contains a given description.

#### Value provider
The value provider is a provider who accepts the `useValue` property value that will be set for the provider set in the `provide` property. It can be any of the values. An example definition can be: 
```typescript
    {
      provide: timezoneToken,
      useValue: 'europe/warsaw'
    },
```
I used the token from the previous example as a provider and set `europe/warsaw` as a value.

#### Factory provider
Sometimes we want to do something with an existing provider and provide it as another new provider instance. We can use a factory provider, which accepts three properties for that purpose. The `provide` property defines which provider we will provide. Let's make an example. First of all, I will define another injection token:
```typescript
export const timezoneCityToken = new InjectionToken<string>('My timezone city');
```
Then to define the factory provider:
```typescript
    {
      provide: timezoneCityToken,
      useFactory: (timezone: string) => {
          return timezone.split('/')[1];
      },
      deps: [timezoneToken]
    },
```
In the `deps` property, we set dependencies for our provider. In this example, I used `timezoneToken` from the previous example. Those dependencies are passed as params to the method, which is set in the `useFactory` property. That property accepts a method that returns the value for our new provider. 
Sometimes we want to set an array of values under the same provider. To achieve that, We can set property `multi` to `true`, so the value of the above provider would be `['warsaw']` instead of just `warsaw`.

#### Use class provider
Another type of provider is the `useClass` provider. It is used when we want to use a standard class to create an instance for our provider. An example of usage can be:
```typescript
    {
      provide: MyDataAbstarct,
      useClass: UsersList,
    }
```
And the `MyDataAbstarct` content is an abstract class, so it cannot have its own instance:
```typescript
export abstract class MyDataAbstarct {
    abstract itemsList: string[];
}
```
And the `UsersList` content:
```typescript
export class UsersList extends MyDataAbstarct {
    itemsList = ['John', 'Mike', 'Anna'];
}
```
To reach this list, we can utilize the `inject` method by:
```typescript
  readonly list = inject(MyDataAbstarct).itemsList;
```
So `useClass` provider is a cool way to have consistency across different places and use common patterns because we can easily replace the used class but keep the provider.

#### Use existing provider
The last type of provider is the `useExisting` provider. It works similarly to a `useClass`, but it shares the same instance with the selected provider. Simple usage can be:
```typescript
...,
    UsersList,
    {
      provide: MyDataAbstarct,
      useExisting: UsersList,
    },
...
```
Please remember that `useClass` would create a separate instance of the selected provider.

### Providers usage
Those are examples of which types of providers exists in Angular. Now let's see how we can provide them.

#### Root level
Services in Angular are classes that can be injected with other providers and are mainly used to serve common logic. We can inject them (to create new instances) at the root level of our application, which is a default approach for services. It is set by a dedicated decorator:
```typescript
@Injectable({
  providedIn: 'root'
})
```
Value `root` means that a given service will be injected at the app's top level. Another value can be `platform`, meaning we share instances with other apps on the same page. The last possible value is `any`, meaning that each place will receive a new, unique service instance.

#### Module level
We can provide any provider at the module level by defining it in `providers` property, for example:
```typescript
@NgModule({
  declarations: [
    FlagComponent
  ],
  imports: [
  ],
  providers: [FlagService],
})
export class FlagModule { }
```
That means `FlagService` will provide the same instance across all modules and components used in `FlagModule`.

#### Component level
Like above, we can use the provider on a component level by adding it to the `providers` property. The same instance and all children's components will be shared inside that component.

#### Route level
To decrease imports and lines of code in components, We can set providers on the route configuration, for example:
```typescript
export const appRoutes: Routes = [
    {
        path: 'flag',
        loadChildren: () => import('./flag/flag.module').then(m => m.FlagModule),
        providers: [
            FlagService,
        ],
    }
]
```
It works in a similar way as provided on the component level.

To access a provider, we can utilize two ways:
- `inject` function in the class body or `useFactory` provider, for example:
```typescript
readonly myInstance = inject(MyService);
```
- `constructor`, for example:
```typescript
constructor(readonly myInstance: MyInstance) {}
```

#### Providers direct usage configuration
Sometimes we want to omit a provider's usage, or it can be optional. Angular has dedicated flags for providers to set it up.
- [`Optional`](https://angular.io/api/core/Optional) - marks provider as optional and won't throw an error when not provided but returns `null` instead,
- [`Self`](https://angular.io/api/core/Self) - will look for a provider *only* at the current component or directive level
- [`SkipSelf`](https://angular.io/api/core/SkipSelf) - will skip the current component or directive and look for the provider at the parent level,
- [`Host`](https://angular.io/api/core/Host) - will look for a provider up to the host level.

To apply those flags with the `inject` method, we can add them as a configuration object, for example:
```typescript
  readonly list = inject(MyDataAbstarct, { optional: true })?.itemsList;
```
Or if we are using a constructor, then we have to add a dedicated decorator:
```typescript
  constructor(
    @Optional() readonly list: MyDataAbstarct,
  ) {}
```

### Summary
Angular has a powerful mechanism of providing values and shares common sources with many ways of configuring it. Knowing it allows developers to write reusable and clean code. [Here](https://angular.io/guide/dependency-injection-overview) You can find more details.