Belajar Angular - Bagian 3 - Membuat Service

Written by Irfan Maulana

angularangularidfrontendjavascriptprogramming
Belajar Angular - Bagian 3 - Membuat Service
Belajar Angular - Bagian 3 - Membuat Service

Belajar Angular - Bagian 3 - Membuat Service - Service atau Provider dalam Angular sendiri sudah diperkenalkan sejak AngularJS v.1.x, dimana service menjadi bagian yang Injectable dari kode Angular sehingga pada prakteknya sering digunakan sebagai pembungkusan (Encapsulation) dari suatu logic. Angular sendiri sudah menggunakan depedency injection (DI) sejak AngularJS versi 1.x sehingga lebih mudah untuk meng-inject atau memasukkan service kedalam komponen yang ingin menggunakannya.

Tulisan ini merupakan serial pembahasan mengenai framework Angular, Anda mungkin ingin membaca tulisan lainnya berikut :

  1. Bagian 1 - Pengenalan dan Inisialisasi Project
  2. Bagian 2 - Membuat Komponen
  3. Bagian 3 - Membuat Service
  4. Bagian 4 - Membuat Route
  5. Bagian 5 - Membuat Custom Pipe atau Filter
  6. Bagian 6 - Persiapan File Production

Bila kita sudah belajar Angular v.1.x kita biasa melakukan injection service dengan cara kurang lebih seperti ini :

var irfanCtrl = angular.module('irfan.basic.controller',[]);

irfanCtrl.controller('irfan.ctrl', ['$scope', 'irfanShareObject', irfanCtrlFunc]);

function irfanCtrlFunc($scope, irfanShareObject){
  // no data
}

Bisa dilihat kode diatas adalah bagaimana AngularJS 1.x melakukan depedency injection service irfanShareObject ke dalam controller, tidak perlu ada instance baru untuk tiap kali penggunaan service dalam controller, cukup mendefinisikan service yang akan digunakan dalam pembuatan controllernya.

Untuk isi service nya pun, bisa mulai dari wrap logic sederhana sampai logic yang dirasa terlalu berat untuk langsung dibuat di controller sehingga developer memilih untuk membungkusnya dalam suatu service. Berikut contoh kode service dalam AngularJS 1.x :

var irfanServices = angular.module('irfan.basic.service', []);
irfanServices.factory('irfanShareObject', [irfanServicesFunction]);

function irfanServicesFunction() {

    var fullName= "";

    return {
        setFullName: function(firtname, lastname) {
            if(typeof firtname !== 'undefined' && firtname !== '') {

               fullName = firtname;
            }
            if(typeof lastname !== 'undefined' && lastname !== '') {

               fullName += lastname;
            }

            return fullName;
        },
        getFullName: function() {
            return fullName;
        }
    };
}

Isinya logic sederhana untuk mencari fullname dari sebuah firstname dan lastname.

Setelah sekilas kita #throwback ke AngularJS 1.x maka kita coba membuat service di Anglular versi 2 keatas, untuk studi kasus sendiri saya akan coba contohkan membuat service yang mem-wrap logic untuk GET data dari suatu public API untuk kemudian memformat sesuai dengan kebutuhan client. Berikut langkah-langkahnya :

Kita akan menggunakan API dari Starwars API (https://swapi.co), dimana saya akan mencontohkan GET data Film dari API tersebut. Url API dari list Film sendiri ada di https://swapi.co/api/films/.

Langkah pertama sebelum memulai semua nya, saya akan menyiapkan beberapa class Film yang akan digunakan sebagai Object untuk menampung data hasil response dari API tersebut. Karena datanya terlalu banyak, dan saya capek untuk menulis semuanya maka saya hanya akan ambil beberapa data saja. Berikut contoh kode class Film :

export class Film {
  constructor(
    public title: string,
    public episode_id: number,
    public opening_crawl: string,
    public director: string,
    public producer: string,
    public release_date: string,
  ) { }
}

Ya, seperti itulah penampakannya. Dengan typescript kita bisa mendefinisikan tipe data dari masing-masing properti Film yang akan kita ambil.

Setelahnya saya akan membuat beberapa helper class yang akan saya butuhkan nanti (*ini tidak harus dipisah, tapi saya lebih senang menggunakan helper dibandingkan langsung menulis logic ditempat yang sama).

Saya akan membuat helper UrlCollection yang isinya :

export class UrlCollection {
  public static readonly FILM = "https://swapi.co/api/films/"
  public static readonly PEOPLE = "https://swapi.co/api/people/"
  public static readonly PLANET = "https://swapi.co/api/planets/"
  public static readonly SPECIES = "https://swapi.co/api/species/"
  public static readonly STARSHIP = "https://swapi.co/api/starships/"
  public static readonly VEHICLE = "https://swapi.co/api/vehicles/"
}

Ini merupakan list url dari API yang akan di hit nanti, sebenarnya akan lebih baik jika menggunakan enum / enumeration untuk membuat class semacam ini. Namun karena pada saat menulis artikel ini saya belum pernah menggunakan enum di Angular maka saya cari yang cepat di kerjakan saja. ðŸ™‚

Berikutnya saya akan membuat helper class ObjectConverter yang isinya :

import { Film }  from '../Film/Film'

export class ObjectConverter {

  public convertResponseToFilm(r:any): Film{

    let film = ({
      title: r.title,
      episode_id: r.episode_id,
      opening_crawl: r.opening_crawl,
      director: r.director,
      producer: r.producer,
      release_date: r.release_date,
    });

    return film
  };

}

Class ini saya gunakan untuk mem-casting data dari tipe any ke tipe Film, namun setelah saya baca-baca lagi ternyata Typescript mempunyai syntax yang lebih sederhana dibandingkan cara manual yang saya lakukan. Silahkan baca : http://acdcjunior.github.io/typescript-cast-object-to-other-type-or-instanceof.html. Karena pada saat saya membuat artikel ini saya belum sempat baca artiel diatas jadi sudahlah biarkan saja saya membodohi diri sendiri dengan kode diatas ya.

Berikutnya kita akan membuat Service untuk meng-GET data, berikut kodenya :

import { Injectable } from '@angular/core'
import { Http, Response} from '@angular/http'
import { Film } from './Film'
import {Observable} from 'rxjs/Rx'

// Import RxJs required methods
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch'

import { UrlCollection } from '../Helpers/UrlCollection'
import { ObjectConverter } from '../Helpers/ObjectConverter'

@Injectable()
export class FilmService {
  // Resolve HTTP using the constructor
  constructor (
    private http: Http,
  ) {}

  getFilms() : Observable<Film[]> {
    let objectConverter = new ObjectConverter()

    function mapFilmResponse(response:Response): Film[]{

      return response.json().results.map(objectConverter.convertResponseToFilm)
    }

    return this.http.get(UrlCollection.FILM)
      .map(mapFilmResponse)
      .catch((error:any) => Observable.throw(error.json().error || 'Server error'))

  }

}

Bisa dilihat kode diatas, pertama kita membutuhkan depedency import { Injectable } from '@angular/core', ini merupakan salah satu syarat agar service kita bisa digunakan di component nantinya. Dengan menggunakan depedency ini kita bisa menambahkan decorator @Injectable() pada saat pembuatan class service ini.

Kita juga menambahkan depedency import { Http, Response} from '@angular/http' untuk melakukan getting data dan mendapatkan balikan dalam bentuk Object Response. Oh iya, http ini mesti di definisikan sebagai constructor ya.

Terakhir kita butuh bantuan dari RxJS yakni import {Observable} from 'rxjs/Rx', import 'rxjs/add/operator/map' dan import 'rxjs/add/operator/catch'. Tapi tenang saja karena by default semuanya sudah menjadi depedency wajib bagi Angular, jadi kita tidak perlu lagi menambahkan di list depedency di package.json kita.

Untuk sisanya bisa coba dipahami kode diatas.

Setelah membuat service ini kita perlu menambahkan file ini sebagai provider di file app.module.ts, berikut kurang lebih kodenya :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { HeaderComponent } from './Header/header.component';
import { FilmListComponent } from './Film/film-list.component';


import { FilmService } from './Film/film-list.service';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    FilmListComponent 
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [
    FilmService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Setelah ini kita akan buat contoh bagaimana memanggil file service ini di sebuah component. Kita membuat component /Film/film-list.component dengan kode kurang lebih seperti ini :

import { Component, OnInit } from '@angular/core'

import { FilmService } from './film-list.service'
import { Film } from './Film'

@Component({
  moduleId: module.id,
  selector: 'film-list',
  templateUrl: './film-list.component.html'
})
export class FilmListComponent implements OnInit {

  films: Film[]

  constructor (
    private filmService: FilmService
  ){}

  ngOnInit () {
    this.loadDataFilms()
  }

  loadDataFilms () {
    let self = this
    self.filmService.getFilms()
      .subscribe(
        films => self.films = films, //Bind to view
        err => {
          console.log(err)
        })
  }

  trackByEpisodId (index:number, film:Film) {
    return film.episode_id
  }
}

Kita hanya perlu menambahkan FilmService sebagai constructor di component kita maka dengan begitu kita bisa menggunakan fungsi di dalam service tersebut. Seperti pada kode diatas kita bisa memanggil filmService.getFilms() dengan mudahnya.

Saya juga menambahkan fungsi trackByEpisodId(index:number, film:Film) untuk digunakan di view template karena pada saat saya membuat contoh kode ini saya belum menemukan built-in trackBy seperti pada AngularJS v.1.x.

Sedangkan isi html view template dari component ini sebagai berikut :

<h2>Films</h2>
<table class="table">
    <thead class="table__head">
        <tr>
            <th>#</th>
            <th>Title</th>
            <th>Episode</th>
            <th>Release Date</th>
        </tr>
    </thead>
    <tbody class="table__body">
        <tr *ngFor="let film of films; let i = index; trackBy:trackByEpisodId;">
            <td>{{ i+1 }}</td>
            <td>{{ film.title }}</td>
            <td>{{ film.episode_id }}</td>
            <td>{{ film.release_date }}</td>
        </tr>
    </tbody>
</table>

Kode yang digunakan dalam tutorial ini diambil dari repository https://github.com/mazipan/ng2-starwars dan bisa dilihat demo nya di : http://mazipan.github.io/ng2-starwars/.

Silahkan dipelajari source code nya.

Demikian sekilas pembahasan awal mengenai framework Angular, semoga bermanfaat tulisan yang tidak seberapa ini.

Ditulis oleh Irfan Maulana.

Salam.