Red Green Repeat Adventures of a Spec Driven Junkie

Angular Services

I am using Angular for work now and I have been able to feel my way around making fixes and small features.

Angular
Logo

I want to get a better understanding of the Angular framework by creating a quick ‘service’ in Angular, which forms the basis of external services.

The eventual goal is to connect the angular app to an external API.

This article will focus on the minimal amount of code to get an Angular service working. I will take a lot of shortcuts and will not follow best practice. The intention is to demonstrate how Angular connect the app component and service.

Steps to connect a service in Angular:

  • Install Docker
  • Create new project
  • Modify project to display function
  • Add & connect service to main app component
  • Display service function

Requirements

  • Docker installed
  • Docker image: angular-rgr
    • with Docker installed, run: $ docker pull andrewrgr/angular-rgr

Create Project

For this, I will start from the default Angular project. Run the command:

$ docker run --mount type=bind,source="$(pwd)",target=/angular angular-rgr ng new number-generator-service

The current directory will have a new folder named: number-generator-service and the contents of src/app folder are:

.
├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets
├── environments
│   ├── environment.prod.ts
│   └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts

Everything Working?

Let’s do a quick dry run of the sample project just to make sure everything is working. Run the following command:

$ cd number-generator-service
$ docker run --mount type=bind,source="$(pwd)",target=/angular -p 4200:4200 angular-rgr ng serve

Use a web browser and navigate to address: http://localhost:4200. The browser will show:

Angular default project
page

If things are not working, double check the docker command and look at output errors.

Stopping & Restarting Server

The current setup of ng serve does not sense file changes, so it won’t reload the application when you edit files. To stop the server, in another terminal run:

$ docker ps | grep angular-rgr | awk '{ print $1 }' | xargs docker kill

To restart, in the project directory run:

number-generator-service$ docker run --mount type=bind,source="$(pwd)",target=/angular -p 4200:4200 angular-rgr ng serve

Add Function app.component

Let’s change the front page of the default app. Open the src/app/app.component.html file and change the contents from:

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC\
8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwL\
jl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
  </li>
  <li>
    <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
  </li>
</ul>

Replace the contents with:

This is output of app.component:

<ul>
  <li>value from app.component numberGenerator(): {{ numberGenerator() }} </li>
  <li>value of app.component numberGenerator: {{ numberGenerator }} </li>
</ul>

Changes to app.component.ts

Let’s start by adding the corresponding function in app.component.ts file:

numberGenerator(): number {
  return 42;
};

This will provide functions on the app.component.html page.

So the whole app.component.ts file contents are:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';

  numberGenerator(): number {
    return 42;
  };

}

Restarting the angular server and reloading the web page, we see:

Angular Component
Function

Angular follows structure such that, any function in the app.component.ts is accessible in the app.comonent.html file, just like the function numberGenerator().

On the second list item in app.component.html, I use the function numberGenerator instead and it outputted:

function () { return 42; }

Which is just the function definition listed in app.component.ts.

Adding Service

Now let’s add a service component to the project. First, stop the Docker container and run this command to create the service:

$ docker run --mount type=bind,source="$(pwd)",target=/angular angular-rgr ng generate service numberGenerator

This generates files:

  • src/app/number-generator.service.spec.ts
  • src/app/number-generator.service.ts

We will focus on the number-generator.service.ts file first, which has contents:

import { Injectable } from '@angular/core';

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

Let’s add a function to the service, one similar to the one before:

  value(): number {
    return 420;
  }

That makes the NumberGeneratorService file contents as:

import { Injectable } from '@angular/core';

@Injectable()
export class NumberGeneratorService {
  constructor() {
    value(): number {
    return 420;
  }
}

This creates a function value() in the NumberGeneratorService that will return 420.

Hooking Up Service to app.component

Now to use this service in the app.component, there’s four steps that have to happen. The service must be:

  1. imported
  2. listed as a component provider
  3. injected as a dependency of the component
  4. connected to a component function

Importing

Angular imports libraries using the format at the top of the file:

import { <item name> } from '<file name>';

In our specific case:

import { NumberGeneratorService } from 'app/number-generator.service';

Component Provider

In the @Component section, add the NumberGeneratorService as a provider. Initially, there are no providers, so add a new providers entry:

providers: [NumberGeneratorService]

The @Component section will look like:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],

  providers: [NumberGeneratorService]
})

Dependency Injection

Angular uses dependency injection heavily in its framework. This means for a component to use another function, say from a service, the function must be “injected”, or passed in as a function argument.

The way to pass in the service to the app.component, is through the component’s constructor:

constructor() {}

In our case, it is NumberGeneratorService we created, so the constructor will become:

constructor( private _numberGeneratorService: NumberGeneratorService ) {}

This makes the NumberGeneratorService accessible in the function through the private variable: _numberGeneratorService.

Connecting Service

To use the injected service, the needs to be a call to the variable assigned to the service.

In our case, I will create another function in app.component that will directly call the service function. The setup is the same as the original function.

fromNumberGeneratorService(): number {
  return this._numberGeneratorService.value();
}

App Component Contents

The contents of the app.component.ts file are now:

import { Component } from '@angular/core';
import { NumberGeneratorService } from 'number-generator.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],

  providers: [NumberGeneratorService]
})
export class AppComponent {
  title = 'app';

  constructor( private _numberGeneratorService: NumberGeneratorService ) {}

  numberGenerator(): number {
    return 42;
  };

  fromNumberGeneratorService(): number {
    return this._numberGeneratorService.value();
  };

}

The final step is to use the numberGeneratorService function, which means getting it into the app.comoponent.html file.

Displaying

To display the value from the service, add one line to: app.component.html:

<li>value from NumberGeneratorService(): {{ fromNumberGeneratorService() }} </li>

The contents of the app.component.html file are now:

This is output of app.component:
<ul>
  <li>value from app.component numberGenerator(): {{ numberGenerator() }} </li>
  <li>value of app.component numberGenerator: {{ numberGenerator }} </li>
  <li>value of app.component fromNumberGeneratorService(): {{ fromNumberGeneratorService() }} </li>
  <li>value of app.component fromNumberGeneratorService: {{ fromNumberGeneratorService }} </li>
</ul>

Restart the Docker image using commands:

$ docker ps | grep angular-rgr | awk '{ print $1 }' | xargs docker kill
$ docker run --mount type=bind,source="$(pwd)",target=/angular -p 4200:4200 angular-rgr ng serve --host 0.0.0.0

Open a browser and navigate to: http://localhost:4200 and the display is:

Angular Number Generator
Service

Conclusion

Whew, this is a basic way to get a service hooked up in Angular. I took a lot of shortcuts and got things working:

  • A new app was created.
  • A function was created on app.component so we can see how functions work.
  • A service was created with a similar function as the app.component function.
  • Hooking up the service to app.component required these steps:
    • importing the service
    • registering the service as a provider
    • injecting the service to the app.component constructor
    • linking a function in app.component to the service

Next time I will focus on getting the service connected with a server!