Angular Services
I am using Angular for work now and I have been able to feel my way around making fixes and small features.
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
- with Docker installed, run:
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:
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 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:
- imported
- listed as a component provider
- injected as a dependency of the component
- 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:
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!