barbarian meets coding

WebDev, UX & a Pinch of Fantasy

Getting Started With Angular 2 Step by Step: 2 - Refactoring to Services

| Comments

UPDATE (8th May 2016): This tutorial has been updated to the latest version of Angular (Angular 4). Note that the Angular team has re-branded the terms Angular 2 to Angular and Angular 1.x to AngularJS which are now the official names of these frameworks.

This is the second article on the Getting Started with Angular 2 Step by Step series if you missed the first article go and check it out!

Ok! So you’ve learned how get started with an Angular 2 and TypeScript application and you’ve written your first component. Yey! The next thing you are going to learn about are services and dependency injection.

Angular 2 Getting Started

In the previous example we had a PeopleListComponent where we initialized an array of Star Wars persons of note. But your component shouldn’t know or care about where the list of people is coming from be it local storage, a database, a web service or the grace of the holy spirit.

That’s why we are going to extract that data access logic into a service that we’ll inject into our component through its constructor. Breaking your application in smaller pieces that have a single responsiblity will make it much easier to reason about, more maintainable, reusable and testable. Win Win!

The Code Samples

You can download the code samples for this article from this GitHub repository.

Our People List Component Right Now

So this is how our PeopleListComponent looks right now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Component, OnInit } from '@angular/core';
import { Person } from '../person';

@Component({
  selector: 'app-people-list',
  template: `
  <!-- this is the new syntax for ng-repeat -->
  <ul>
    <li *ngFor="let person of people">
     {{person.name}}
    </li>
  </ul>
  `,
  styleUrls: ['./people-list.component.scss']
})
export class PeopleListComponent implements OnInit {
    people: Person[] = [
    {name: 'Luke Skywalker', height: 177, weight: 70},
    {name: 'Darth Vader', height: 200, weight: 100},
    {name: 'Han Solo', height: 185, weight: 85},
  ];

  constructor() {}
  ngOnInit() {}

}

Our mission is to extract the data access logic into a separate PeopleService service that will encapsulate that responsibility of retrieving people from wherever people come from and provide that as a service for the rest of the application.

I know, our current data access logic is super simple, we are just instantiating an array and that’s it, but in the very near future we will be using a service, so bear with me :).

What is an Angular 2 Service?

Every application is composed of a lot of subsystems that do different things like logging, data access, caching, specific application domain knowledge, etc. Depending on the type of application that you are building or the framework that you are using there’s different ways to represent these subsystems.

Angular 2 uses the concept of services, an Angular service is a class that encapsulates some sort of functionality and provides it as a service for the rest of your application.

If you have an AngularJS background you’ll welcome this simplification in the Angular conceptual model: there’s no more service/factory/constant/provider conumdrum. In Angular 2 there’s only services, and they are just vanilla ES6 classes.

Creating The PeopleService

Let’s create our new PeopleService in a new file people.service.ts. We’ll take advantage of the Angular cli generators and run the following command:

1
2
3
PS> ng generate service people
# in short notation:
# ng g s people

This will create an empty service called people.service.ts that will look like this:

1
2
3
4
5
6
7
8
import { Injectable } from '@angular/core';

@Injectable()
export class PeopleService {

  constructor() { }

}

Now if we extract the data access code from our PeopleListComponent into the service we’ll get the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { Injectable } from '@angular/core';
import { Person } from './person';

@Injectable()
export class PeopleService {
  constructor() { }

  getAll() : Person[] {
    return [
      {name: 'Luke Skywalker', height: 177, weight: 70},
      {name: 'Darth Vader', height: 200, weight: 100},
      {name: 'Han Solo', height: 185, weight: 85},
    ];
  }

}

As you can see above this is just a vanilla ES6 class that exposes a getAll method which returns a list of persons.

Injecting Our People Service in The People List Component

Now that we have a service that encapsulates Star Wars person data access we can use it from our PeopleListComponent. We will take advantage of dependency injection and inject the service through the component’s constructor.

Dependency Injection?

Real world applications do a lot of things.

In order to manage this innate complexity of real world systems we break them down into smaller subsystems that ideally do one thing or a teeny tiny set of things.

As a result of that we have a simpler system made up of small components that depend on each other. Initially we may have these as hard-set dependencies (for instance using the new operator to instantiate them), but we want these dependencies to know the least amount of information possible about each other. We want each subsystem to depend on abstractions and not concrete implementations.

Why? Because that way we can create systems that are more flexible, extensible and sturdy where changes in concrete implementations don’t propagate to other parts of the system.

So we have these nice collection of subsystems that depend only of abstractions. But… how are we supposed to work with them and create a real program out of them? or, what is the same, how can we make them use the real dependencies at runtime? Well, that’s where dependency injections comes in. It’s the mechanism by which we can introduce real implementations to these subsystems that can do real work at runtime.

If you want to know more about SOLID, the dependency inversion principle and dependency injection check this article

Using Angular 2 Dependency Injection to Inject Our People Service in the People List Component

We start by importing the PeopleService from the people.service module:

1
import { PeopleService } from '../people.service';

Then we inject it into the PeopleListComponent via its constructor:

1
2
3
4
5
6
7
8
9
export class PeopleListComponent{
  people: Person[] = [];

  // this shorthand syntax automatically creates and
  // initializes a new private member in the class
  constructor(private peopleService: PeopleService) {
    this.people = peopleService.getAll();
  }
}

We use the TypeScript shorthand syntax to directly declare a private member of the PeopleListComponent. The private peopleService in the constructor is equivalent to:

1
2
3
4
5
6
7
8
9
export class PeopleListComponent {
  private peopleService: PeopleService;
  people: Person[] = [];

  constructor(peopleService : PeopleService){
    this.peopleService = peopleService
    this.people = this.peopleService.getAll();
  }
}

And we’re done, the whole PeopleListComponent now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { Component, OnInit } from '@angular/core';
import { Person } from '../person';
import { PeopleService } from "../people.service";

@Component({
  selector: 'app-people-list',
  template: `
  <!-- this is the new syntax for ng-repeat -->
  <ul>
    <li *ngFor="let person of people">
     {{person.name}}
    </li>
  </ul>
  `,
  styleUrls: ['./people-list.component.scss']
})
export class PeopleListComponent implements OnInit {
  people: Person[];

  constructor(private peopleService: PeopleService) {
    this.people = peopleService.getAll();
  }

  ngOnInit() {}
}

If you had your http server running (if you don’t run ng serve) you’ll see how… it didn’t work!!

Hmm… everything looks fine… PeopleListComponent uses PeopleService… open your browser dev tools and you’ll discover the following error message:

1
No provider for PeopleService! (PeopleListComponent -> PeopleService)

Aha! Angular 2 doesn’t know how to inject a PeopleService into PeopleListComponent. Let’s see how to do that next.

Registering Your Service With Angular 2

In order to register a service with Angular 2 you use the providers property within the Component decorator. For instance, to make the PeopleService available throughout our app we can register it within the AppComponent component. This will make it available to this component and all its children (like PeopleListComponent). We start by importing the service from its module:

1
import { PeopleService } from './people.service';

And adding it to the providers property within the Component decorator:

1
2
3
4
5
6
7
8
9
10
11
12
import { Component } from '@angular/core';
import { PeopleService } from './people.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  providers: [PeopleService]
})
export class AppComponent {
  title = 'Star Wars PPlz!!!';
}

In some way we can compare this providers property to the configuration section of an IoC (inversion of control) container.

In this particular example we are cheating a little bit because we are using concrete classes both in the PeopleListComponent constructor and in this providers property. Bear with me, I’ll try to keep it as simple as possible in these first articles in the series and I’ll expand on dependency injection in Angular 2 later on.

Ok, ok… hehe if you can’t wait, you can make your application depend on abstractions right away using this more verbose approach:

1
2
3
4
5
6
7
8
9
// In the AppComponent
providers: [ {provide:'IPeopleService', useClass: PeopleService} ]

// In the PeopleListComponent
constructor(@Inject("IPeopleService")private _peopleService : IPeopleService)

// In the PeopleService
export interface IPeopleService {...}
export class PeopleService implements IPeopleService {...}

Note that this type matching matters because we are using TypeScript. If we were to use ES6 or vanilla JavaScript then we would be able to inject whatever we wanted regardless of types.

You Can Also Register Services at the Module Level

With the NgModule decorator after RC5 and the new concept of Angular 2 Modules you can now register services to be used throughout a whole module. You can achieve that via the providers property of the NgModule decorator:

1
2
3
4
5
6
7
8
9
import { PeopleService } from './people.service';

@NgModule({
  imports: [ BrowserModule, routing ],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ],
  providers: [ PeopleService] // You can put your services here!
})
export class AppModule { }

When would you want to do that? – you may wonder. Well, whenever you want to use the same instance of a service for your whole application.

The Angular Cli Can Help You With Registering! Yey!

The Angular Cli can also help you register your services. When you generate a new service there is one additional flag that you can use to inject the registering code inside a module in addition to generating the new service: the --module flag (-m in short-hand).

For instance, the following example would have generated the PeopleModule and registered it inside our AppModule:

1
2
3
PS> ng generate service people --module app
# in short notation:
# ng g s people -m app

Cool right? The Angular cli strikes back and saves us some additional keystrokes.

Taking Advantage of Angular 2 Component Lifecycle

Another Angular 2 improvement over AngularJS is that now it is dead easy to hook into the component lifecycle. For instance, we can make our constructor much leaner and move the data initialization to the OnInit component.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Component, OnInit } from '@angular/core';
import { Person } from './person';
import { PeopleService } from './people.service';

@Component({
  selector: 'people-list',
  template: `
  <!-- this is the new syntax for ng-repeat -->
  <ul>
    <li *ngFor="let person of people">
        {{person.name}}
    </li>
  </ul>
  `
})
export class PeopleListComponent implements OnInit{
  people: Person[] = [];
  constructor(private _peopleService : PeopleService){ }

  ngOnInit(){
    this.people = this._peopleService.getAll();
  }

}

This is going to be helpful when writing unit tests because we can count on the fact that the component won’t do any heavy lifting when we instantiate it. Additionally, a component inputs aren’t yet available within a class constructor but only later on in the OnInit lifecycle hook (You’ll learn about inputs later in the series).

Enabling Dependency Injection in Your Service

We saw how to inject stuff in a component, but how do you inject stuff in a service? Well you follow the same process that you do with components by injecting the dependency in the constructor and registering in the providers property of the root component. And additionally you need to decorate the service with the Injectable decorator just as you can see in the example below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Injectable } from '@angular/core';
import { Person } from './person';
import { SomeOtherService } from './some-other.service';

@Injectable()
export class PeopleService{
  constructor(private _someOtherService: SomeOtherService){}

  getAll() : Person[] {
    return [
      {name: 'Luke Skywalker', height: 177, weight: 70},
      {name: 'Darth Vader', height: 200, weight: 100},
      {name: 'Han Solo', height: 185, weight: 85},
    ];
  }

}

Why don’t we use the Injectable decorator in components? We’ll that’s because the Component decorator enables dependency injection directly.

Whenever you use the Angular cli to generate your services it will add the Injectable decorator for you. So use the Angular cli and you can forget this section altogether and live happily ever after.

Want to Know More About Angular 2 Dependency Injection?

Would you like to learn more about Angular 2 dependency injection and you cannot wait till I write a following article about it? Worry not, check these two great articles:

Concluding

Well done! Now you know both about components, services and dependency injection. Up next we are going to learn more about Angular 2 data binding by creating a second component that will help us discover more information about these Star Wars heroes of yore.

Have a great day!

Comments