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

Chapter 4.1 – form, service, class, singleton dan model (part B/coding)

0
Degananda.com -

Setelah membuat proses routing, input dan output maka kita akan melanjutkannya ke tahapan ke-4. Pada ulasan ini akan melakukan implementasi mengenai pembuatan aplikasi manajemen note / catatan yakni membuat aplikasi tersebut dapat menginputkan dan menampilkan data yang telah kita inputkan dengan menggunakan temporary database(singleton). Kami rekomendasikan untuk mengetahui teori mengenai form, services, class, singleton ataupun model anda dapat mengikuti ulasan part A dibawah ini karena pada tutorial part B ini akan menitikberatkan pada coding atau implementasi bukan pada teorinya.

https://degananda.com/2017/06/05/chapter-4-form-service-class-singleton-dan-model-part-ateori/

 

untuk memudahkan dalam mengikuti tutorial chapter ke-4 ini anda dapat melakukan clone repository github untuk source code yang digunakan pada chapter ini di :

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

repository diatas adalah repository versi ke-4 artinnya terdapat 3 versi terdahulu yang disesuaikan dengan chapter tutorial diblog ini. Anda dapat mengganti versi repository dengan mengubah angka “4” menjadi nomor chapter (“X.0”, dengan x = nomor chapter.). Misalnya ingin menuju ke repository untuk tutorial chapter 3 anda tinggal menggubahnya menjadi angka “3”.

0. Pre-requisites

tutorial chapter-4 ini memerlukan beberapa hal yang harus dimengerti terutama soal angular. Anda dapat mempelajarinnya pada chapter 1 , 2 dan 3. Beberapa materi yang sudah harus dimengerti untuk chapter 4 ini adalah :

  1. mengerti mengenai penggunaan decorator @input (angular)
  2. mengerti mengenai penggunaan decorator @output (angular)
  3. mengerti mengenai pembuatan routing (angular)
  4. faham tentang konsep komponen, propery, attribut, expression, directive
  5. dasar-dasar html tentang : form, input, option, textarea, button
  6. event dasar javascript (onsubmit, onclick) dan user input event pada angular

jika anda belum memahami tentang konsep diatas maka sangat disarankan untuk membaca dan mempraktekan chapter 1 hingga 3 yang telah mencakup beberapa item diatas. Untuk item yang tidak dibahas dichapter 1 hingga 3 yakni mengenai dasar-dasar html. Anda dapat mempelajari dasar html di luar website ini(kami belum memiliki ulasan mengenai dasar-dasar html).

1. What will we build ?

http://demo.degananda.com/angular/chapter-4/

Langsung akses pada halaman diatas untuk melihat demo dari apa yang akan kita kerjakan di chapter 4 ini. Pada chapter-chapter sebelumnya tidak ada demo dikarenakan apa yang kita kerjakan pada chapter sebelumnya tidak terlalu komplex. Namun pada chapter ini cukup complex sehingga pada awal tutorial diharapkan anda sudah tahu apa yang akan dibuat sehingga memberikan sedikit gambaran.

Aplikasi sangat sederhana tanpa dilakukan styling karena masih mencapai chapter 4 , styling akan dilakukan pada chapter – chapter selanjutnya namun, pada intinnya , aplikasi angularnote pada chapter 4 ini memiliki empat fungsi utama yaitu :

  1. Melihat daftar note
    Note yang akan ditampilkan diambil dari services yang mengacu pada model “note” yang telah kita buat.
  2. Menghilangkan dan menampilkan note (show/hide)
    ini telah kita bahas pada chapter-3, untuk detail pembuatan show/hide ini anda dapat melihat pada chapter-3
  3. Menambahkan note (menggunakan singleton)
    Penambahan note ini dilakukan tanpa DBMS maka object note ini akan disimpan dalam array yang terletak diproperty services. Atau biasa kita sebut sebagai singleton class.
  4. Mengubah note
    Mengubah object note yang ada pada services .
  5. Menghapus note
    Menghapus object note yang ada pada services.

2. Lets Code~

2.1 Menyiapkan model

kita memerlukan model karena angular menggunakan konsep object oriented programming (OOP) selain itu juga untuk memudahkan orang lain yang nantinnya membaca kode kita karena OOP saat ini menjadi standar dari pemrograman bukan malah fungsional programming. Model adalah bentuk dari data yang akan kita sajikan. Kita hanya memiliki satu buah model yakni note. Note atau catatan memiliki beberapa property yakni :

property deskripsi
isi_note isi note adalah text yang ada dalam catatan. Merupakan konten utama yang berada dalam note.
tanggal_note tanggal note merupakan property yang menunjukan kapan note tersebut dibuat atau jika note pernah diedit maka akan menunjukan kapan terahir kali note tersebut mengalami perubahan

kita akan membuat class model dengan menggunakan angular-cli, perintah yang digunakan adalah :

ng g class note-model

model disini dituliskan sebagai “class” biasa yang tidak memiliki decorator apapun. Yang harus ada pada class model adalah setter pada constructor yang berfungsi untuk memberikan nilai ke property class sehingga dapat menjadi satu object baru(Object oriented programming).


//note model
export class NoteModel {

    isi_note : String;
    tanggal_note : String;

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

}

property isi_note akan bertipedata string, karena user dapat menuliskan apa saja kedalam note tersebut tanpa batasan karakter (untuk saat ini). Sedangkan property tanggal note juga kita beri tipedata string nantinnya akan diberikan nilai timestamp(waktu saat ini) untuk membuat setiap data note pada array menjadi unique. Jika tidak unique maka data tersebut tidak dapat dihapus atau diubah. sehingga harus unik.

2.2 menyiapkan interfaces

umumnya sebelum mendefinisikan services kita membutuhkan interface sebagai template pembuatan services. Namun pada chapter ke-4 ini kami rasa tidak membutuhkan hal itu(interfaces) mengingat kita hanya membuat satu buah services saja yakni note services oleh karena itu kami memilih untuk tidak menggunakan interface agar lebih fleksibel(dan lebih cepat) saat mendefinisikan fungsi maupun property pada services.

Jika anda menggunakan interfaces maka anda harus menyaman isi dari services(property dan fungsi) sesuai dengan interfaces yang dibuat. Kami menyarankan untuk menggunakan interfaces pada project yang memiliki scope menengah hingga besar. Karena pada project scope kecil kompleksitas sangat rendah sehingga (menurut kami) interfaces hanya akan menambah waktu pengerjaan dan tidak memberikan value added yang cukup.

2.3 Membuat services note

pada tahapan ke-tiga ini kita akan membuat services note yang berfungsi untuk berbagai hal sebagaimana yang telah terdefinisikan pada baigna 1 mengenai what we build.Perintah yang digunakan untuk membuat services dengan angular-cli adalah

ng g service nama_service

Services ini akan menggunakan model note yang telah kita buat untuk menstandarkan tipe data dan mengikuti aturan object oriented programming. Perlu di ingat bahwa services ini akan menggunakan decorator @injectable agar dapat dilakukan dependency injection(DI) pada komponen lainnya. Tanpa DI maka services tidak dapat digunakan. Services note ini akan berfungsi sebagai singleton(temporary local database) yang menyimpan data note dalam bentuk array yang bertipe object note(dari model note yang kita buat).


// note service
import { Injectable } from '@angular/core';
// class
import { NoteModel } from './note-model';
// model 

@Injectable()
export class ServiceNoteService {

  listNote : Array<NoteModel> = [];

  constructor() { 
  }

  getNote(){
    return this.listNote;
  }

  addNote(note : NoteModel){
    this.listNote.push(note);
  }

  getNoteSingle(noteIndex){
    return this.listNote[noteIndex];
  }

  getNoteIndex(note : NoteModel){
    // mencari index dari note terkait.
    return this.listNote.indexOf(note);
  }

  ubahNote(noteIndex, newNote : NoteModel){
    // mencari index dari note terkait.
    this.listNote[noteIndex] = newNote;
  }

  deleteNote(note : NoteModel){
    // mencari index dari note terkait.
    let noteIndex = this.listNote.indexOf(note);
    this.listNote.splice(noteIndex, 1);
  }

}

constructor akan kita biarkan kosong karena memang tidak ada property yang harus di inisiasi pada saat object dibuat. Services tidak memiliki life cycle hooks seperti komponen sehingga ketika anda ingin menginisiasi property maka lakukan diconstructor bukan pada ngOnInit(). getNote() berfungsi untuk memberikan return terhadap seluruh data note dalam array. getNoteSingle() akan mereturn array dengan id tertentu maka secara spesifik akan memberikan data note tertentu. getNoteIndex() berfungsi untuk mendapatkan nilai index dari suatu object note terhadap property listNote di note services. Nilai index ini akan digunakan untuk mengupdate sebuah object note dalam array, pada array kita membutuhkan nilai index dari array tersebut untuk mengubahnya. ubahNote() memiliki fungsi untuk mengubah nilai array pada index tertentu yang memiliki dua buah parameter yakni index dari note tersebut dan nilai baru dari note. deleteNote() berfungsi untuk menghapus object note pada array dengan index tertentu. Setiap note akan memiliki nilai yang unik karena terdapat property tanggal_array, tanggal array ini bernilai timestamp maka akan unik sehingga proses delete dilakukan dengan mencari indexnya terlebih dahulu kemudian di delete.

2.4 Registrasi services provider

sebelum dapat menggunakan services maka kita harus melakukan registrasi service tersebut di metadata “provider” pada ngModule di app.module.ts. langkahnya adalah lakukan import terhadap services kemudian masukan nama class dari services tersebut pada array yang ada di metadata provider di ngModule.

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

// component
import { AppComponent } from './app.component';
import { ComNoteComponent } from './com-note/com-note.component';
import { PipeSensorPipe } from './pipe-sensor.pipe';
import { FormNoteComponent } from './form-note/form-note.component';
import { ComListnoteComponent } from './com-listnote/com-listnote.component';

// routing
import { myRouter } from './app.router';

// services
import { ServiceNoteService } from './service-note.service';
import { ComAddnoteComponent } from './com-addnote/com-addnote.component';

// dari newbie untuk newbie
@NgModule({
  declarations: [
    AppComponent,
    ComNoteComponent,
    PipeSensorPipe,
    FormNoteComponent,
    ComListnoteComponent,
    ComAddnoteComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    HttpModule,
    myRouter
  ],
  providers: [
    ServiceNoteService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

maka note service kita siap digunakan pada komponen-komponen lainnya.

2.5 Membuat komponen add note

kami memisahkan antara komponen form untuk menambahkan note dan untuk mengubah note. Karena memang data yang dibutuhkan oleh kedua form tersebut berbeda. Form mengubah note membutuhkan data note yang akan diubah sedangkan untuk menambahkan note tidak perlu memerlukan data apapun. langkah awal untuk membuat komponen adalah dengan perintah

ng g component com-addnote

setelah itu maka pada com-addnote.component.ts kita akan melakukan coding untuk membuat form dan menghandle proses dari pembuatan note dari form tersebut. Modul yang dibutuhkan adalah form builder , note services yang telah kita buat, note model yang telah kita buat dan validators. Formbuilder berfungsi untuk membuat form sebagaimana telah kami jelaskan pada part A dari tutorial ini. Validators berfungsi untuk menghandle validasi form. Validasi yang dilakukan adalah form tidak akan disubmit apabila terdapat field/form control yang kosong. Note model digunakan untuk menstandarkan object dan menyesuaikan format object yang diterima oleh services note yang telah kita definisikan.

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';

@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
  ) { }

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

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

}

Pada lifecycle hooks ngOnInit() kita akan menginisiasi noteForm dengan satu form control yakni isi_note dengan validasi required (agar form item/control) harus di isi, jika tidak di isi tidak dapat dilakukan submit). onSubmit() akan adalah fungsi yang berguna untuk memanggil service addNote(). Onsubmit akan menangkap data yang dikirim user melalui form kemudian akan diteruskan ke service note untuk dimasukan pada database temporary lokal (singleton) melalui fungsi addNote(). pada fungsi onSubmit juga akan mendefinisikan tanggal_note dengan timestamp saat ini, sehingga nilai timestamp menjadi nilai waktu terahir note tersebut diubah / dibuat. Setelah data diteruskan ke services maka form akan direset (nilainya sehingga seluruh form control / item menjadi kosong) dengan menggunakan fungsi form.reset().

setelah itu kita akan membuat komponen view untuk formnya. Pada com-addnote.component.html tuliskan kode dibawah ini

<div class="add_note_form">
  <form [formGroup]="noteForm" (ngSubmit)="onSubmit(noteForm.value)">
    <p>
      <textarea type="text" formControlName="isi_note" placehoder="Masukan note" rows="10" style="width: 100%;"></textarea>
    </p>
    <p>
      <button type="submit" [disabled]="!noteForm.valid">Tambah</button>
    </p>
  </form>
</div>

formGroup adalah attribute yang menandakan nama form yang telah kita definisikan di com-addnote.component.ts. Pada bagian <button> kita akan menambahkan directive [disabled] dengan isi expression berupa !noteForm.valid yang berarti button akant terdisable jika form tidak valid. Nilai valid an dari form ini dipengaruhi oleh model validasi yang kita tentukan. Model validasi yang kita tentukan adalah form control / item harus di isi. Jika di isi maka akan valid, namun jika tidak di isi form menjadi tidak valid.

2.6 Mengubah isi komponen form-note

pada chapter-3 kita telah membuat form note namun masih belum dilakukan coding. Pada chapter-4 ini form-note akan memiliki isi kurang lebih sama dengan com-addnote. Hanya saja perbedaanya terletak pada data yang menjadi nilai default/awal dari form dan perlakuan form tersebut saat dilakukan submit. Pada komponen form-note ini setelah form disubmit akan mengeksekusi fungsi onSubmit() yang akan memanggil service note dan mengeksekusi fungsi ubahNote(). Maka note yang sedang di edit akan berubah sesuai dengan nilai pada form. Berikut adalah isi dari form-note.component.ts

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

@Component({
  selector: 'app-form-note',
  templateUrl: './form-note.component.html',
  styleUrls: ['./form-note.component.css']
})
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
  ) { 

  }

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

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

        // get note setelah subscription karena kita membutuhkan noteIndex dari parameter routing.
        this.noteData = this.serviceNoteService.getNoteSingle(this.noteIndex);
        if(this.noteData){
        // insiiasi from setelah note data 
        this.noteForm = this.fb.group({
          isi_note : this.fb.control(this.noteData.isi_note, [Validators.required])
        });
        }

      }
    )
  }

  onSubmit(value){
    let newTimetamp = new Date().getTime();
    this.noteData.isi_note = value.isi_note;
    this.noteData.tanggal_note = newTimetamp.toString();
    this.serviceNoteService.ubahNote(this.noteIndex, this.noteData);
  }

}

modul yang dibutuhkan adalah

  1. formbuilder , validator
  2. subscription dari rxjs
  3. notemodel
  4. note services
  5. ActivatedRoute

Form builder , validator, note service dan note model penggunaanya sama dengan com-addnote yang telah kita bahas pada bagian 2.5. Sedangkan yang baru pada komponen form-note ini adalah activatedRoute dan subscription. ActivatedRoute berguna untuk mendapatkan nilai paramter pada url. karena form-note ini akan mengubah nilai dari object note maka diperlukan id dari note tersebut. id note itu berada pada url. Maka activatedRoute ini digunakan untuk mengambil nilai parameter tersebut. ActivatedRoute ini bertipe observable sehingga kita harus melakukan subscription untuk mendapatkan nilainya. Untuk saat ini , kita tidak membahas mengenai observable karena sangat panjang dan akan dibahas pada chapter selanjutnya. Pada intinnya, obseravable adalah object yang berjalan secara async maka kita perlu melakukan subscription atau menandai bahwa kegiatan async tersebut telah selesai baru kita dapat mendapatkan nilainya. Pada lifecycle hooks ngOnInit() di komponen ini akan memanggil services getNoteSingle() untuk mendapatkan data note yang akan diedit sesuai dengan ID nya dan proses pemanggilan getNoteSingle() ini dilakukan setelah proses subscription dilakukan karena activatedRoute bersifat async seperti yang telah kita bahas diatas.

isi dari form-note.html adalah seperti dibawah ini

<div class="edit_note_form">
  <div>
    <span routerLink="/">Back</span>
  </div>
  <form [formGroup]="noteForm" (ngSubmit)="onSubmit(noteForm.value)">
    <p>
      <textarea type="text" formControlName="isi_note" placehoder="Masukan note" rows="10" style="width: 100%;"></textarea>
    </p>
    <p>
      <button type="submit" [disabled]="!noteForm.valid">Ubah</button>
    </p>
  </form>
</div>

button akan terdisable jika nilai form tidak valid

2.7 Mengubah router

pada chapter sebelumnya routes yang kita buat belum menerima paramter. Oleh karena itu kita akan buat routes untuk mengubah note menerima parameter “id” dari note tersebut. route ini berhubungan dengan komponen form-note untuk mengubah isi dari note.

import { RouterModule, Routes } from '@angular/router'

// import component yang digunakan untuk routing
import { FormNoteComponent } from './form-note/form-note.component';
import { ComListnoteComponent } from './com-listnote/com-listnote.component';

// buat konstanta bertipe routes yang menyimpan detail routing
const  routingDetails : Routes = [
    {
        path : '',
        component : ComListnoteComponent,
        data : {
            breadcumb : 'home / listnote'
        }
    }, 
    {
        path : 'note/modify/:id',
        component : FormNoteComponent,
        data : {
            breadcumb : 'home / note / modify'
        }
    }
]

// export router
export const myRouter = RouterModule.forRoot(routingDetails);

parameter selalu diawali dengan titik dua (“:”) dan untuk mendapatkan nilainya pada komponen kita memerlukan package angular router dengan modul ActivatedRoute yang telah kita bahas pada bagian 2.6.

2.8 Mengubah isi dari komponen list-note

pada chapter sebelumnya , komponen list-note ini akan menampilkan daftar note dari array yang berada di file list-note.component.ts. Namun, pada chapter-4 ini list note akan menerima data note melalui services sehingga kita akan menggunakan note services untuk mendapatkan data dari note. Maka inilah nilai list-note.component.ts yang baru(pada chapter-4 ini).

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

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

  listNote : Array<NoteModel>;

  constructor(
    public serviceNoteService : ServiceNoteService
  ) { }

  ngOnInit() {
    this.listNote = this.serviceNoteService.getNote();
  }

  onDeleteNote(note : NoteModel){
    this.serviceNoteService.deleteNote(note);
  }

}

sedangkan isi dari komponen view list-note (list-note.component.html) tidak ada yang berubah


<h1>
    Angular 2/4 CRUD dengan Singleton (tanpa DBMS)
</h1>

<app-com-addnote></app-com-addnote>

<div *ngFor="let note of serviceNoteService.listNote;">
    <app-com-note [isiNote]="note" (onDelete)="onDeleteNote($event)"></app-com-note>
</div>

done, tutorial yang panjang ini mungkin akan “sulit” untuk cepat difahami karena memang banyak sekali istilah yang baru seperti async, subscription. Hal inilah yang membuat angular memiliki learning curve yang cukup berat. Olehkarena itu jika ada yang kurang difahami maka anda dapat bertanya disini. dan jangan lupa untuk mengklone repository chapter-4 ini digithub agar mudah mempelajarinnya. Anda juga dapat melihat demo aplikasi chapter-4 (link tertera dibagian atas).