lifelong learner — urip iku urup, currently working on accenture.

chapter 9 – CRUD Http request

0
Degananda.com -

CRUD (Create , read , update dan delete) pada angular memanfaatkan modul http melalui package @angular/http. Namun bagaimana cara mengimplementasikannya ? apakah terdapat modul lain yang diperlukan ? bagaimana melakukan http request yang menggunakan basic auth. Silahkan simak ulasan berikut untuk menjawab pertanyaan-pertanyaan tersebut.

Selayang pandang

Pada ulasan ke-9 mengenai pembuatan aplikasi angular note menggunakan angular 4.0 ini akan dibahas cara implementasi http request yang mengkonsumsi API (Application programming interfaces) yang telah dibuat dengan mongodb dan nodejs. Titik beratnya adalah kita dapat melakukan http request dengan keadaan API tersebut membutuhkan autentikasi. Namun tidak hanya itu, setiap API tentunnya memiliki konfigurasi tersendiri misalnya ada yang menggunakan JSON ataupun XML sebagai format pertukaran data. Oleh karena itu kita akan http request yang sesuai dengan spesifikasi api yang telah dibuat.

Note : pada chapter-9 ini tidak ada demo (live preview) dikarenakan server degananda.com hanya difokuskan untuk menghosting wordpress ini. Mungkin jika ada waktunnya akan kami coba hosting ke heroku. Karena memang chapter-9 membutuhkan nodejs+mongodb hosting. Terimakasih mohon dan maaf. Namun kami tetap menyediakan repository pada github untuk chapter-9. Anda dapat mencobannya langsung pada komputer/laptop masing-masing. Berikut ini adalah repository yang digunakan untuk chapter-9 ini

https://github.com/degananda/angular-note/tree/9.0

What we build

Kita akan membuat CRUD namun bedannya kali ini akan mengkonsumsi API yang telah dibuat yakni menggunakan nodejs dan mongoDB. Berikut ini adalah user interfaces aplikasi pengelola note setelah di integrasikan dengan API.

sekilas perubahan yang nampak hanyalah penambahan field berupa “judul note”. Namun dibelakang itu seluruh services yang sebelumnya hanya menggunakan singleton sebagai penyimpan data saat ini langsung berhubungan dengan API dan kemudian data akan disimpan melalui mongoDB.

Pre requisites

API (Application programming interface)

API yang digunakan merupakan hasil ulasan yang ada pada ulasan dibawah ini

RESTful web service dengan express dan mongodb part 2/2

namun tentunnya dengan beberapa perubahan agar API tersebut siap dikonsumsi oleh http request. Perubahan-perubahan tersebut dapat anda lihat pada ulasan yang masih berhubungan pada ulasan diatas di :

Menyiapkan web services (API) yang siap dikonsumsi oleh http(client) secara sederhana

namun jika anda hanya ingin berfokus untuk pembuatan http requestnya (tidak ingin mengetahui pembuatan Web services dengan menggunakan nodeJS dan mongodb) anda dapat melakukan clone atau download API yang digunakan pada tutorial ini pada link github dibawah ini

https://github.com/degananda/api-angular-note

Spesifikasi RESt API

Spesifikasi API yang kita gunakan adalah sebagai berikut (hal ini sangat penting karena terkait dengan fungsi http request yang kita buat). Tentunnya API dengan http auth dan JWT akan berbeda cara penanganannya. Sehingga, tutorial ini sangat spesifik untuk spesifikasi API dibawah. Namun jangan kuatir anda tetap dapat merubahnya sesuai dengan spesifikasi RESt API yang anda gunakan nantinnya. Hanya tinggal memodifikasi.

Jenis Software / modul Produk yang digunakan
Database MongoDB
Engine NodeJS versi 6.9.2
Format pertukaran data JSON
Web framework Express
Auth Http basic auth
CORS Enabled
Package pendukung body-parser (untuk JSON), Express validator (untuk melakukan validasi terhadap JSON).

Daftar endpoint yang ada pada API

Endpoint Fungsi Request body
[GET] /note Menampilkan seluruh daftar node dari database tidak ada
[GET] /note/:id Menampilkan note (single) berdasarkan id pada endpoint tidak ada
[POST] /note Menambahkan note pada database “judul_note”,

“isi_note”

[PUT] /note/:id Mengubah note berdasarkan id pada endpoint “judul_note”,

“isi_note”

[DELETE] /note/:id Menghapus note berdasarkan id pada endpoint tidak ada

Pada chapter-chapter sebelumnya kita telah membuat aplikasi pengelola note (CRUD) dengan memanfaatkan singleton class which is itu merupakan local variable yang akan hilang ketika aplikasi di tutup dan dibuka kembali. Oleh karena itu akan terjadi perombakan masal dibeberapa file untuk mengakomodasi RESt API Diatas. Silahkan di simak dengan baik karena cukup banyak file yang berubah terutama dibagian services.

Implementasi

Http request angular

Sebelum melangkah ke bagian project kita akan memahami kode untuk melakukan http request. Pada angular modul yang digunakan untuk membuat http request adalah modul http dari package @angular/http. Kita dapat menggunakan modul http tersebut pada services. Tentunnya sebelum dapat digunakan harus dilakukan DI (Depedency injection) seperti biasannya. Banyak opsi yang dapat kita berikan pada http request diantarannya :

  1. Header option
  2. Request body
  3. Endpoint

Pada spesifikasi API kita , maka tiga hal tersebut diperlukan tergantung dari endpoint. Contohnya jika melakukan request pada endpoint yang tidak membutuhkan request body kode yang digunakan adalah

let headers = new Headers({'Content-Type': 'application/json'});
let username : string = 'degananda';
let password : string = 'indonesiaraya';           
headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
let options       = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:7250/note', options).map(
    (res : Response) => res.json()
);

kita tidak perlu menspesifikasikan request body , karena memang tidak dibutuhkan. Berbeda jika kita membutuhkan request body maka kode yang digunakan adalah

let requestBody = JSON.stringify(note);
let headers = new Headers({'Content-Type': 'application/json'});
let username : string = 'degananda';
let password : string = 'indonesiaraya';           
headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
let options       = new RequestOptions({ headers: headers });
return this.http.post('http://localhost:7250/note', requestBody, options).map(
  (res : Response) => res.json()
);

http request diatas diseting untuk melakukan autentikasi dengan menggunakan basic auth. Jika api anda tidak menggunakan basic auth dapat dihilangkan bagian headers.append(‘authorization’). Intinnya tinggal sesuaikan dengan API yang anda desain. JSON.stringify digunakan untuk merubah object javascript menjadi JSON. Hal ini dilakukan karena API yang kita buat hanya menerima format berupa JSON.

Perlu di ingat bahwa http akan melakukan return terhadap OBSERVABLE. Sehingga nantinnya untuk mengambil data (respon) dari kode diatas harus menggunakan subscribe. Respon dari http request diatas akan ditransformasikan menjadi JSON melalui fungsi map() dan json(). Sehingga pastikan response dari API kita mereturn JSON.

Langkah-langkah implementasi

1. Membuat services yang mengkonsumsi seluruh endpoint

langkah pertama adalah mengubah seluruh fungsi yang sebelumnya menggunakan singleton menjadi http request. Berikut ini adalah service-note.services.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
// class
import { NoteModel } from './note-model';
// model 

@Injectable()
export class ServiceNoteService {

  listNote : any = [];

  constructor(
    private http : Http
  ) { 
  }

  getNote() : Observable<NoteModel[]>{
    let headers = new Headers({'Content-Type': 'application/json'});
    let username : string = 'degananda';
    let password : string = 'indonesiaraya';           
    headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
    let options       = new RequestOptions({ headers: headers });
    return this.http.get('http://localhost:7250/note', options).map(
      (res : Response) => res.json()
    );
  }

  addNote(note : NoteModel) : Observable<any>{
    //this.listNote.push(note);
    let requestBody = JSON.stringify(note);
    let headers = new Headers({'Content-Type': 'application/json'});
    let username : string = 'degananda';
    let password : string = 'indonesiaraya';           
    headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
    let options       = new RequestOptions({ headers: headers });
    return this.http.post('http://localhost:7250/note', requestBody, options).map(
      (res : Response) => res.json()
    );


  }

  getNoteSingle(id) : Observable<NoteModel>{
    let headers = new Headers({'Content-Type': 'application/json'});
    let username : string = 'degananda';
    let password : string = 'indonesiaraya';           
    headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
    let options       = new RequestOptions({ headers: headers });
    return this.http.get('http://localhost:7250/note/'+id, options).map(
      (res : Response) => res.json()
    );
  }

  ubahNote(note : NoteModel,id) : Observable<any>{
    let requestBody = JSON.stringify(note);
    let headers = new Headers({'Content-Type': 'application/json'});
    let username : string = 'degananda';
    let password : string = 'indonesiaraya';           
    headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
    let options       = new RequestOptions({ headers: headers });
    return this.http.put('http://localhost:7250/note/'+id, requestBody, options).map(
      (res : Response) => res.json()
    );
  }

  deleteNote(id){
    let headers = new Headers({'Content-Type': 'application/json'});
    let username : string = 'degananda';
    let password : string = 'indonesiaraya';           
    headers.append("Authorization", "Basic " + btoa(username + ":" + password)); 
    let options       = new RequestOptions({ headers: headers });
    return this.http.delete('http://localhost:7250/note/'+id, options).map(
      (res : Response) => res.json()
    );
  }

}

2. Merubah model

jika pada chapter-chapter sebelumnya kita hanya menggunakan dua buah property maka pada chapter-9 ini akan bertambah 1 property pada model yakni “judul_note” sesuai dengan spesifikasi API yang digunakan pada chapter ini. Berikut ini adalah note.model.ts saat ini

export class NoteModel {

    isi_note : String;
    judul_note : String;
    tanggal_note : String;

    constructor(judul_note: String, isi_note : String, tanggal_note : String){
        this.isi_note = isi_note;
        this.tanggal_note = tanggal_note;
        this.judul_note = judul_note;
    }

}

class property judul note yang ditambahkan memiliki tipe berupa string sama halnya dengan dua property yang sudah ada. Perlu di ingat tipe proprty pada model di client / angular berbeda dengan tipe data pada mongodb. Model di angular ini disesuaikan dengan request body yang dibutuhkan. Sehingga kami menggunakan string.

3. Merubah seluruh fungsi formhandler pada komponen Create dan Modify

karena kita menggunakan http request notabenenya menggunakan observable maka fungsi handler juga harus dirubah. Observable merupakan reactive programming sehingga kita dapat mengetahui jika request tersebut selesai dengan menggunakan subscribe

3.1 com-addnote.component.ts

berikut ini adalah kode dari komponen diatas

import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
// services
import { ServiceNoteService } from './../service-note.service';
// model
import { NoteModel } from './../note-model';
// angular material
import {MdSnackBar} from '@angular/material';


@Component({
  selector: 'app-com-addnote',
  templateUrl: './com-addnote.component.html',
  styleUrls: ['./com-addnote.component.css']
})
export class ComAddnoteComponent implements OnInit {

  noteForm : FormGroup;

  constructor(
    private fb : FormBuilder,
    private serviceNoteService : ServiceNoteService,
    private snackBar : MdSnackBar
  ) { }

  openSnackBar() {
     this.snackBar.open('note berhasil dibuat', 'sukses', {
      duration: 2000,
    });
  }

  ngOnInit() {
    // inisiasi form
    this.noteForm = this.fb.group({
      isi_note : this.fb.control('', [Validators.required]),
      judul_note : this.fb.control('', [Validators.required])
    })
  }

  getCurrentNote(){
    this.serviceNoteService.getNote().subscribe(
      (res) => {
        this.serviceNoteService.listNote = res;
      }
    )
  }

  onSubmit(value){
    // menghandle saat form disubmit
    let dateNow = new Date().getTime().toString();
    value.tanggal_note = dateNow;
    let myNote : NoteModel = new NoteModel(value.judul_note, value.isi_note, value.tanggal_note);
    this.serviceNoteService.addNote(myNote).subscribe(
      (res) => {
        this.getCurrentNote();
      }
    )
    // reset form
    this.noteForm.reset();
    this.openSnackBar();
  }

}

Perubahan yang terjadi adalah pada onSubmit. Saat ini kita menggunakan subscribe untuk mengetahui apakah request (http) telah selesai atau belum. Jika telah selesai maka toast baru dimunculkan dan melakukan reset form. Selanjutnya adalah kita juga memanggil fungsi getCurrentNote() untuk mengupdate list. Karena memang api yang kita buat belum realtime sehingga perlu diupdate ketika melakukan perubahan pada form ini.

3.2 form-note.component.ts

berikut ini adalah kode dari komponen diatas

import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
// service
import { ServiceNoteService } from './../service-note.service';
// model 
import { NoteModel } from './../note-model';
// angular material
import {MdSnackBar} from '@angular/material';

@Component({
  selector: 'app-form-note',
  templateUrl: './form-note.component.html',
  styleUrls: ['./form-note.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
  
})
export class FormNoteComponent implements OnInit {

  // form
  noteForm : FormGroup;
  // data
  noteIndex;
  noteData : NoteModel;
  // subscription
  routeSubscription : Subscription;

  constructor(
    private fb : FormBuilder,
    private activatedRoute : ActivatedRoute,
    private serviceNoteService : ServiceNoteService,
    private snackBar : MdSnackBar,
    private router : Router
  ) { 

  }


  openSnackBar() {
     this.snackBar.open('note berhasil diubah', 'sukses', {
      duration: 2000,
    });
  }

  ngOnDestroy(){
    // unsubscribe activated route saat komponen destroyed.
    if(this.routeSubscription){
      this.routeSubscription.unsubscribe();
    }
  }

  ngOnInit() {
    // insiiasi from setelah note data 
    this.noteForm = this.fb.group({
      isi_note : this.fb.control('', [Validators.required]),
      judul_note : this.fb.control('', [Validators.required])
    });

    // get note index
    this.routeSubscription = this.activatedRoute.params.subscribe(
      (res) => {
        this.noteIndex = res['id'];

        // set value dari mongodb
        this.serviceNoteService.getNoteSingle(this.noteIndex).subscribe(
          (res) => {
            this.noteForm.controls['isi_note'].setValue(res[0]['isi_note']);
            this.noteForm.controls['judul_note'].setValue(res[0]['isi_note']);
          }
        )

      }
    )
  }

  onSubmit(value){
    let newTimetamp = new Date().getTime();
    value.date_updated = newTimetamp.toString();
    this.serviceNoteService.ubahNote(value, this.noteIndex).subscribe(
      (res) => {
        this.openSnackBar();
      }
    )
  }

}

Perubahan yang terjadi sama halnya dengan pada com-note.component.ts

3.3 Merubah fungsi mengambil data pada com-listnote.component.ts

http request yang kita lakukan mereturn observable sehingga untuk mendapatkan datannya kita menggunakan salah satu fungsi pada rxjs adalah subscribe. Oleh karena itu cara mengambil data dari API tersebut perlu disesuaikan atau dirubah dari yang sebelumnya hanya berupa object biasa dari singleton maka saat ini dilakukan melalui observable.

Berikut adalah kode com-listnote.component.ts

import { Component, OnInit } from '@angular/core';
import { ServiceNoteService } from './../service-note.service';
import { NoteModel } from './../note-model';
// angular material
import {MdSnackBar} from '@angular/material';

@Component({
  selector: 'app-com-listnote',
  templateUrl: './com-listnote.component.html',
  styleUrls: ['./com-listnote.component.css'],
})
export class ComListnoteComponent implements OnInit {

  searchQuery = "";
  onSearchLoading = false;
  searchTypeValue: string = 'asc';

  sortOption = [
    {value: 'asc', viewValue: 'ascending'},
    {value: 'desc', viewValue: 'descending'},
  ];

  constructor(
    public serviceNoteService : ServiceNoteService,
    private snackBar : MdSnackBar
  ) { }

  openSnackBar() {
     this.snackBar.open('note telah dihapus', 'sukses', {
      duration: 2000,
    });
  }

  doSearch(value){
    // set serachQuery property
    this.searchQuery = value;
  }

  onLoadingChange(value){
    this.onSearchLoading = value;
  }
  
  ngOnInit() {
    this.getCurrentNote();
  }

  getCurrentNote(){
    this.serviceNoteService.getNote().subscribe(
      (res) => {
        this.serviceNoteService.listNote = res;
      }
    )
  }

  onDeleteNote(id){
    this.serviceNoteService.deleteNote(id).subscribe(
      (res) => {
          this.openSnackBar();
          this.getCurrentNote();
      }
    )
  }

}

Subscribe digunakan untuk menghandle observalbe. Dimana observable tersebut akan mereturn value. Value yang direturn adalah json dari api (kita sudah seting pada bagian services dengan mentransformasi response menjadi json dengan fungsi json() ). done, cukup singkat namun banyak sekali perubahan yang harus dilakukan. Sehingga garis besarnya adalah melakukan perubahan pada services dan penggunaan observable. Untuk perubahan spesifik tiap file dapat anda check pada git.