Angular Providers & Angular Decorators - Angular Dependency Injection
Angular Providers & Angular Decorators - Angular Dependency Injection

In this post you will learn such decorators inside Angular like Optional, Self, SkipSelf and Host.

Here I already prepared a small project with parent, child and app components.

<parent></parent>

Inside our app.component.html we just render our parent.

<div>parent</div>
<child></child>

Inside a parent.component.html we are rendering child.

The last thing that we have is a service that we will try to inject.

// users.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable()
export class UsersService {
  usersKey = 'users';
  constructor(private http: HttpClient) {}

  getUsers() {
    return this.http.get('http://localhost:3004/users');
  }
}

Optional decorator

Let's say that our child component really needs a UsersService. Typically we will provide it like this.

export class ChildComponent implements OnInit {
  constructor(private usersService: UsersService) {}

  ngOnInit(): void {
    console.log('key', this.usersService.usersKey);
  }
}

We should not also forget to register it in our app.module.ts.

@NgModule({
  ...
  providers: [UsersService],
})
export class AppModule {}

But actually we could do it differently. We can remove the registration of this service in the module and just say that it is optional. Then our component will work even if our service is not provided.

export class ChildComponent implements OnInit {
  constructor(@Optional() private usersService: UsersService) {}

  ngOnInit(): void {
    console.log('key', this.usersService?.usersKey || 'not set');
  }
}

Here I've added Optional decorator so in the case if we didn't provide it we won't get an error but null. This is why I've adjusted ngOnInit to handle null correctly and fallback to a default string.

We use Optional decorator when our code can work without a service.

Self decorator

Now let's talk about Self. Let's add it instead of Optional.

export class ChildComponent implements OnInit {
  constructor(@Self() private usersService: UsersService) {}
}

Now even if we register a service in app.module we will get an error that usersService is not provided. It happens because Self decorator says that we need to register provider inside a component itself.

@Component({
  selector: 'child',
  templateUrl: './child.component.html',
  providers: [UsersService]
})

This is the only way when our Self decorator will be satisfied.

We use Self decorator when we want to make sure that we registered the dependency in the component itself.

SkipSelf decorator

SkipSelf works exactly in opposite way. It doesn't not allow us to register a service in our component. Only higher like in parent component or in app module.

export class ChildComponent implements OnInit {
  constructor(@SkipSelf() private usersService: UsersService) {}
}

Now our code will break as we registered our service inside component. In order to fix that we can move UsersService registration to app.module for example.

We use SkipSelf decorator when we want to make sure that we did NOT registered the dependency in the component itself.

Host decorator

The most interesting decorator is Host.

@Component({
  selector: 'child',
  templateUrl: './child.component.html',
  providers: [UsersService]
})
export class ChildComponent implements OnInit {
  constructor(@Host() private usersService: UsersService) {}
}

Here we used Host decorator and provided a service in the component again. It allows up to register UsersService only in components which a visible inside our markup. In our case it is parent and child component.

But here is a catch. If we try to register it in our parent component as a provider it will break. We need to do it differently.

@Component({
  selector: 'parent',
  templateUrl: './parent.component.html',
  viewProviders: [UsersService],
})
export class ParentComponent {}

Instead of providers we used here viewProviders. When we have a host decorator it parent providers will be accessible only through viewProviders but not providers.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done