Angular 5 Services

Angular application is built from components – organized in modules with hierarchy. The component represent a user interface element with properties, methods, input and output parameters and more. When we want interaction between components we use input and output parameters but if the interaction is not between parent and child (for example 2 brothers components share functionality) it can be complex to make it using the parent. this is where Angular services help

The main idea behind a service is to provide an easy way to share code and data between components. Using dependency injection you can control how the service instances are shared

Lets start with a simple service to understand the basic concepts

To create a service use Angular CLI

# ng g s Math

Lets implement a simple random function:

@Injectable()
export class MathService {
 constructor() { }
 lowlim:number = 10;
 uplim:number = 20;
 getRand():number{
    let range:number;
    range = this.uplim - this.lowlim;
    return   +Math.floor(Math.random()*range) + +this.lowlim;
  }
}

To inject the service to the app component add it to the providers list in the component decorator. Declare an object and use it:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [MathService]
})
export class AppComponent {
  title = 'app';
  num:number;
  num2:number;
  constructor(private _ser:MathService)
  {
    this._ser.lowlim = 100;
    this._ser.uplim = 200;
  }
  fn()
  {
    this.num = this._ser.getRand();
  }
}

This will inject a service instance to the component

Note that if you declare 2 objects , you will get the same instance

Now lets create 2 child components Comp2, Comp3:

import { Component, OnInit } from '@angular/core';
import {MathService} from "../math.service";

@Component({
  selector: 'comp3',
  template: `
    <input type="text" [(ngModel)]="low"/>
    <input type="text" [(ngModel)]="high"/>
    <button (click)="setlim()">Set Limits</button>
    <button (click)="gennum()">Generate</button>
    {{num}}
  `,
  styles: [],
  providers:[MathService]
})
export class Comp2Component {
  constructor(private _ser:MathService) { }
  low:number;
  high:number;
  num:number

  setlim(){
    this._ser.uplim = this.high;
    this._ser.lowlim = this.low;
  }
  gennum(){
    this.num = this._ser.getRand();
  }
}

The component generate 2 text fields for the random limit and a button to generate a random number in the range. We inject the service to the component decorator and also do the same with comp3 that has the same code , if we put the components in the parent we get 2 different instances

If we declare the provider on the parent component or on the Module we will get the same instance and in this way we can share also data between the components:

@NgModule({
  declarations: [
    AppComponent,
    Comp2Component
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [MathService],
  bootstrap: [AppComponent]
})
export class AppModule { }

In this example we set a different range on the components but when we generate random we get numbers from the second range (the last we set)

 

Asynchronous Operations

It is very common to use a service for asynchronous tasks – for example, creating http request. You can use a Promise object or (and better) observable

Observable is an implementation of publisher subscriber pattern, it is like a stream that you can sent multiple events and subscribe to multiple events. It is part of rxjs

Simple example

import {Observable} from "rxjs";

let t= Observable.interval(1000);
t.subscribe(i => console.log("hello "+i));

In the above example, we create an observable that generate an event every 1 second. We need to subscribe to see something happen – in this example we print a simple message and we get an index as a parameter (incremented counter)

The previous example runs forever, to limit the interval to 5 times add take:

let t= Observable.interval(1000).take(5);

to add other operation add do:

let t= Observable.interval(1000).take(5).do(i => console.log("hello "+i));

Note that you still have to subscribe to make things happens, we can subscribe more than once

t.subscribe(); //subscribe without additional tasks
t.subscribe(j => console.log("more:" + j)); // add more task

You can also apply some operations on the observable :

let t= Observable.interval(1000).take(10).filter(i => i%2==0);
t.subscribe(i => console.log("hello "+i));
t.subscribe(i => console.log("bye "+i));

 

Working With HTTP

To work with http we need to create http requests, with http we can generate:

GET request – select data (return records as Json object)

localhost:3000/ins
localhost:3000/ins/100
localhost:3000/ins?id=400
localhost:3000/ins?rate=3

POST – insert new data

Header:
Content-Type: application/json

Body:
{
    "id": 500,
    "Name": "noya",
    "Price": 444,
    "rate": 3
}

PUT – update data

localhost:3000/ins/500

Content-Type: application/json

{
    "id": 500,
    "Name": "noyale",
    "Price": 4443,
    "rate": 3
}

DELETE – delete data

localhost:3000/ins/200

 

To make http requests, first we generate a new service

# ng g s Data

 

Add the HttpModule to the app.module.ts file

imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],

Create the service

import { Injectable } from '@angular/core';
import {Http} from "@angular/http";
import {Book} from "./Book";
import {Observable} from "rxjs/Observable";

@Injectable()
export class DataService {
  constructor(private _http:Http) { }
  getData():Observable<Book[]>{
    return this._http.get("http://localhost:3000/books").map(r => <Book[]>r.json());
  }
}

http module provides methods for all HTTP request types – here we use get, we convert the result from Response object to a json object and cast it Book array

 

declare a class:

export class Book{
  id:number;
  Name:string;
  Price:number;
  rate:number;
}

Build the template:

<button (click)="load()">load</button>
<table  border='1' align="center" width='70%' *ngIf='books && books.length > 0'>
  <tbody>
  <tr *ngFor='let b of books'>
    <td> {{b.id}} </td>
    <td> {{b.Name }} </td>
    <td> {{b.Price }} </td>
    <td> {{b.rate}} </td>
  </tr>
  </tbody>
</table>

 

Add the code in the component class:

books:Book[] = [];


load(){
    this._data.getData().subscribe(b => this.books = b);
}

 

The new HttpClient module

One new feature of Angular 5 is the HttpClient Module that is in release version (previously was beta). It make the use of http services much more easy

Add HttpClientModule to import section in app.module

imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule
  ],

Change the service:

import {HttpClient} from "@angular/common/http";


@Injectable()
export class DataService {
  constructor(private _htc:HttpClient) { }


  getData():Observable<Book[]>{
    return this._htc.get<Book[]>("http://localhost:3000/books");
  }

}

As you can see we don’t need to map the result and the generic function makes the casting so the code is much more clear

Tagged