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

Tutorial angular 2 – Chapter 3. Input, Output, Routing & ng-style

2
Degananda.com -

0. pre-requieste

Sebelum mengikuti ulasan chapter ke-3 ini pastikan telah mengikuti chapter 2 dan chapter 1. Material(source code) yang digunakan mengikuti chapter sebelumnya sehingga pastikan telah mengclone source pada chapter 2 atau menggunakan source code versi anda sendiri. Hal – hal yang harus difahami tentang angular 2 sebelum memulai chapter ini adalah

  1. Component
  2. Directive
  3. Input event (click, keypress, dan lain-lain)
  4. Rendering data menuju DOM ( bracket “{{}}” )

Jika belum memahami ketiga hal tersebut dimohon untuk membaca terlebih dahulu di chapter 1 dan 2 agar lebih mudah dalam mengikuti chapter ke-3. Repository untuk chapter ketiga dapat anda akses melalui link github dibawah ini :

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

1. Goal

Chapter ketiga ini memiliki beberapa tujuan yang harus tercapai terkait dengan tema chapter yaitu :

  1. Membuat website yang menampilkan daftar note dengan tombol edit, delete dan hide(menghilangkan note dari display dengan directive ng-style)
  2. Tombol edit dan delete hanya berupa event. Belum mengimplementasikan fungsi sebenarnya karena belum ada services / data. Implementasi event pada data sebenarnya akan dilakukan pada chapter ke-4.
  3. Menggunakan fitur @input dari angular untuk memberikan data(value) ke masing-masing child component.
  4. Menggunakan fitur @output dari angular untuk menimbulkan suatu event(emit event) dari sebuah child component.
  5. Melakukan routing ke halaman tertentu.
  6. Memahami mengenia module(angular module).

Kelima goal tersebut akan dibahas satu persatu pada tutorial ini. Agar lebih memudahkan proses belajar anda dapat mengunduh/clone repository chapter-3 ini pada github(opsional).

2. Angular RootModule

Menurut KBBI arti kata modul adalah satuan bebas yang merupakan bagian dari struktur keseluruhan atau komponen dari suatu sistem yang berdiri sendiri, tetapi menunjang program dari sistem itu; 5 unit kecil dari satu pelajaran yang dapat beroperasi sendiri, Angular Root Module adalah sistem yang terdapat pada angular dan memiliki fungsi sebagai organisator dari keseluruhan komponen, sehingga root module adalah modul yang mengorganisasi modul-modul lainnya. Aplikasi yang dibangun dengan menggunakan angular akan tersusun dari komponen-komponen kecil. Angular Root Module membantu dalam mengorganisasi komponen-komponen tersebut sehingga dapat saling berinteraksi untuk mencapai tujuan tertentu. Angular rootmodule terletak di app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';

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

Terdapat tiga parameter/metadata pada anotasi @NgModule yang berfungsi sebagai patokan saat proses kompilasi ataupun menjalan angular di browser. Metadata ini sangat penting pada saat kita menambahkan komponen ataupun library(external ataupun internal).

  1. imports
    angular memiliki browsermodule yang berfungsi agar angular dapat dijalankan pada browser. Tanpa ini angular tidak dapat berjalan.
  2. declaration
    metadata ini digunakan untuk mendefinisikan komponen yang kita buat.
  3. bootstrap
    metadata yang menentukan dimana letak halaman index atau biasa kita sebut seperti index.html dari aplikasi. Lebih tepatnya, komponen mana yang akan di load untuk pertama kali saat angular dibuka pada browser

2.1 Imports

Metadata imports pada anotasi @NgModule memiliki tipe array ([]). Sehingga pada metadata ini kita dapat memberikan banyak variabel ataupun value. fungsi dari metada data import adalah untuk menentukan modul pada angular yang akan digunakan pada aplikasi. Contohnya angular memiliki modul http(httpModule) maka modul tersebut harus disematkan pada metada imports agar dapat digunakan pada komponen nantinnya. Modul tersebut memiliki fungsi khusus, contohnya routerModule yang berfungsi untuk menangani routing pada halaman-halaman tertentu.

Imports : [] , bertipe array masukan modul-modul yang hendak digunakan disini.
Untuk kasus custom pipe, custom directive tidak perlu kita tambahkah secara manual jika menggunakan angular-cli.

Terdapat satu module yang secara default terdapat pada metadata import ini yakni browserModule(@angular/platform-browser). jangan menghapus module ini dari metadata import karena akan menyebabkan angular tidak dapat dijalankan pada browser. Seluruh module membutuhkan browserModule.

2.2 Declarations

seperti pada metadata imports, declaration memiliki tipe array. Fungsi utama dari declaration array ini adalah untuk mendefinisikan komponen, custom pipe atau custom directive yang terdapat pada project. Komponen tidak akan dapat diakses jika kita tidak mendefinisikannya pada metadata declarations. Contohnya kita memiliki komponen Note maka perlu kita tambahkan di metadata declarations ini(jangan lupa melakukan import terlebih dahulu). Namun jika project dibuat dengan angular-cli, maka hal tersebut akan secara otomatis dihandle olehnya(angular-cli).

declarations: [
    AppComponent,
    NoteComponent,
    CustomPipe,
    CustomDirective
]

jika ingin mengetahui cara untuk membuat custom pipe anda dapat melihat pada chapter 2.

2.3 Bootstraps

bootstrap menetukan komponen mana yang akan diload untuk pertama kalinnya. Istilahnya adalah landing component. Pada umumnya, bootstrap diletakan pada app.component.ts , kemudian pada app.component ini akan dipasangkan router untuk menghandle perpindahan halaman. Kita dapat mendefinisikan komponen yang digunakan sebagai  bootstrap kita sendiri namun secara umum menggunakan app.componen karena lebih familiar dan direkomendasikan oleh google sebagai pembuat angular.

3. @Input dan @Output

3.1 Input

Input decorator (@Input) adalah sebuah directive yang memiliki fungsi untuk menerima data dari komponen lain pada suatu komponen tertentu. Contohnya adalah komponen note, pada komponen tersebut terdapat dua data yang harus ditampilkan :

  1. isiNote
  2. tanggalNote

kedua data tersebut dapat kita masukan dengan berbagai cara. Misalnya cara pertama adalah didefinisikan pada komponen note itu sendiri. Namun, kasusnya adalah komponen note ini adalah sebuah child component. Sehingga data harus dimasukan melalui komponen parent atau services. Disinilah peranan input decorator sebagai penghubung untuk memasukan data dari komponen ke komponen lainnya. tag dari input dilambangkan dengan “[]”.

<someElement [input]="value"></someElement>

isi dari input directive tersebut berasal dari parent component yang berupa variabel pada class component(parent). Jangan masukan “string” pada value input decorator karena tag “[]” hanya menerima variabel dari sebuah class. Contoh sederhannya adalah “[src]” dan “src”, keduannya memiliki fungsi yang sama ketika diletakan pada <img> namun memiliki value yang berbeda. “src” tanpa “[]” menerima value berupa string sedangkan “[src]” menerima value berupa variabel dari classs. sehingga tujuan penggunaannya pun berbeda. Meski kita juga dapat memasukan variabel pada “src” dengan menggunakan bracket (“{{}}”). Format penulisan(deklarasi) input pada komponen adalah

@input() nama_variabel;

setelah input didefinisikan dan diterima oleh komponen baru kita dapat menggunakan variabel tersebut pada komponen.

3.2 Output

Output decorator(@output)  adalah sebuah directive yang berguna untuk mentrigger sebuah event. Pada kasus pembuatan aplikasi note ini, komponen note kita berfungsi sebagai child komponen. artinnya, data berasal dari parent. Sebuah note dapat dihapus, untuk mentrigger event “hapus” maka kita membutuhkan output dan yang akan memproses event hapus adalah parent component. Sehingga pada komponen note tidak ada kode untuk melakukan penghapusan note pada database/services. note child component hanya berfungsi sebagai vessel atau wadah untuk menampilkan dan styling(css). Untuk mendeklarasikan output yang melakukan emitting suatu event format penulisannya adalah :

@output() nama_variable = new EventEmiiter()

directive output ditulis pada child component pada parent component dengan “(nama_output)”. Kemudian pada parent component akan menambahkan fungsi yang merespon event output yang diberikan oleh child component. Untuk penjelasan yang lebih baik silahkan baca bab 5 untuk langsung masuk pada proses coding sehingga lebih mudah difahami.

4. Routing

routing adalah sebuah kegiatan dalam menentukan endpoint dari suatu url dan isi dari endpoint tersebut. Sehingga pada angular, routing adalah menentukan endpoint dari sebuah komponen dan parameter ataupun data yang diperlukan oleh komponen tersebut. Contohnya :

  1. http://localhost:4200/note/modify/:id , endpoint tersebut akan menggunakan komponen “form-note” untuk melakukan proses edit dari data note. Pada endpoint ini terdapat parameter “:id” berarti pada url kita dapat menggunakan “:id” untuk passing id dari note. Contoh : localhost/note/modify/1
  2. http://localhost:4200/ , endpoint tersebut menggunakan komponen “list-note” untuk menampilkan daftar note.

namun , penjelasan diatas adalah routing menggunakan url. Sebenarnya ada tiga metode routing pada angular ini yaitu :

  1. routing dengan memasukan url pada address bar (dibrowser).
  2. melakukan click pada sebuah link(hyperlink) pada sebuah halaman
  3. routing dengan tombol back.

sebenarnya dengan angular, kita dapat menavigasikan user ke halaman lain tanpa melalui “ketiga event tersebut” (tidak secara langsung) dengan menggunakan modul .navigate(). Misalnya pada saat login, ketika callback login berhasil maka secara otomatis akan melakukan redirect ke halaman / url tertentu. Beberapa fungsi routing yang akan digunakan pada chapter ini :

  1. <router-outlet> (directive)
    berguna untuk menampilkan component yang cocok dengan endpoint yang telah didefinisikan, dengan kata lain router-outlet adalah sebuah view dari suatu router.
  2. Routes (module)
    modul untuk melakukan mapping url (endpoint) pada suatu komponen tertentu beserta parameter dan datannya.
  3. RouterModule (module)
    sebagai module yang memanggil/menjalankan “services” untuk melakukan routing.

5. Lets code~

5.1 Mendeklarasikan input , ouput dan fungsi pada komponen note

pada tahapan ini yang akan kita lakukan(objective) adalah

  1. mendeklarasikan input + output untuk note.
    output : merupakan event yang akan terjadi ketika tombol “delete” pada note dipencet maka event delete akan tertrigger yang selanjutnya akan diproses oleh parent component(untuk proses delete).
    input : sumber data untuk isiNote (data yang ditampilkan pada komponen note). input akan diperoleh dari komponen list-note  (com-listnote.component.ts)
  2. membuat fungsi untuk show & hide komponen note(css styling)

berikut adalah kode dari com-note.component.ts yang mengakomodasi kedua objective tersebut, baca comment code (“//”) untuk mempermudah memahami isi kode.


import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

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

  @Input() isiNote;
  @Output() onDelete = new EventEmitter();

  // styling
  isVisible = true;

  tanggalNote : any;

  constructor() { 
  }

  /*
   Lifecycle hook.
  */

  ngOnInit() {
    // menginisiasi isi dari note.
    this.tanggalNote = new Date().getTime();
  }

  /*
    Event action.
  */
  
  deleteNote(){
    // emit id dari note, karena kita belum membuat model dan service dari note maka sementara kita isikan "1" sebagai id dummy. 
    console.log('oiii');
    this.onDelete.emit('1');
  }
  
  /*
    Element styling
  */

  onShowHide(){
    // set nilai isVisible dengan nilai kebalikannya.
    this.isVisible = !this.isVisible;
  }

  setShowHideStyle(){
    if(this.isVisible){
      // jika nilai visible true maka element di tampilkan
      return 'block';
    } else {
      // jika nilai visible false maka element di hilangkan
      return 'none';
    }
  }

}


penjelasan dari beberapa fungsi pada kode diatas :

  1. onShowHide()
    fungsinya adalah memberikan nilai boolean pada varibel isVisible di komponen note. Jika isVisible true maka komponen akan muncul jika false maka akan menghilang. Variabel isVisible ini akan digunakan pada directive ng-style (com-note.component.html)
  2. setShowHideStyle()
    memberikan nilai style.display berdasarkan variabel “isVisible“. jika nilai visible true maka element di tampilkan (style.display = block) dan jika nilai visible false maka element di hilangkan (style.display = hidden)

5.2 Membuat komponen list-note

komponen ini adalah parent dari komponen note. Fungsinnya adalah :

  1. sebagai sumber data untuk komponen note
  2. menghandle proses “delete note” yang tertrigger dari output yang dihasilkan oleh komponen note.

buat komponen list-note dengan menggunakan angular-cli

ng g component com-listnote

berikut adalah isi dari com-list.note.ts yang sangat mudah difahami.


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

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

  noteList : Array<string> = [];

  constructor() { }

  ngOnInit() {
    this.noteList[0] = "hello ini note pertama";
    this.noteList[1] = "hello ini note kedua";
    this.noteList[2] = "hello ini note ketiga";
  }

  onDeleteNote(id){
    alert('melakukan delete note');
    console.log(id);
  }

}

data dari note kita bentuk dalam tipe “array” sehingga, kita dapat membuat list dari note dengan data yang berbeda-beda. Kedepannya (pada chapter selanjutnya) evetually kita akan gunakan data dari sebuah database mongo/mysql berdasarkan web services yang telah dibuat.

5.3 Membuat router

perintah untuk membuat router adalah

ng g class app.router

nama dari router class tidak ada standarnya, sehingga anda dapat dengan bebas menggunakan nama yang anda inginkan. Setelah class app.router telah terbentuk maka tulis kan kode dibawah ini untuk mendefinisikan url endpoint,komponen terkait, parameter, dan data pada komponen. Data yang dimasukan biasannya berupa breadcumb.


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

router akan diexport dengan menggunakan module routerModule yang berupa konstanta(routes) seingga dapat digunakan pada metadata imports(imports : []) di app.module.ts agar routing dapat berfungsi.

5.3.1 Memasukan router ke app.module.ts

 

tujuannya adalah agar angular mengenali dan dapat menggunakan router kita. langkah pertama adalah melakukan import class router

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

menambahkan myRouter (hasil import) pada metadata imports []


  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    myRouter
  ],

menambahkan directive router-outlet sebagai view dari router pada bootstrap(app.component.html) sehingga router dapat terpasang pada komponen bootstrap.

<!-- router outlet -->
<router-outlet></router-outlet>
<!-- router outlet -->

5.4 Render data ke komponen(view)

5.4.1 com-listnote.component.html

pada view ini tujuan kita adalah menampilkan child component (com-note.component) sebanyak tiga dbuah sesuai dengan data yang kita miliki pada com-listnote.component.ts serta kita juga memasukan data noteList[0] pada input “isiNote” yang sudah kita buat pada com-note.component.ts. Yang terahir kita juga menggunakan directive (onDeleteNote) yang juga telah kita buat sebagai output pada note.component.ts dan ketika output tersebut tertrigger maka akan memanggil fungsi onDeleteNote dengan parameter berupa $event. $event didapatkan dari nilai yang kita emit(lihat ke fungsi : deleteNote() pada line onDelete.emit() ) dari com-note.component.ts


<app-com-note [isiNote]="noteList[0]" (onDelete)="onDeleteNote($event)"></app-com-note>
<app-com-note [isiNote]="noteList[1]" (onDelete)="onDeleteNote($event)"></app-com-note>
<app-com-note [isiNote]="noteList[2]" (onDelete)="onDeleteNote($event)"></app-com-note>

5.4.2 com-note.component.html

pada view ini fungsi kita adalah menggunakan directive [style.display] dan menghubungkannya ke fungsi “showhide” yang telah kita buat pada com-note.component.ts. Serta membuat sebuah element/tombol yang saat diklik akan melakukan showhide pada komponen note(fungsi : onShowHide() ). click() adalah user input directive yang akan tertrigger jika user mengklik sebuah element dan akan memanggil fungsi tertentu.


<div class="note">
  <div class="note_content">

    <!-- bagian kiri note untuk menampilkan isi dari note dan tombol action -->
    <div class="left_side">

      <!-- wrapper isi note -->
      <div class="isi_note_wrapper" [style.display]="setShowHideStyle()">

        <!-- isi note -->
        <div class="isi_note">
          {{isiNote}} <br />
          {{isiNote | uppercase | pipeSensor}} <br />
          date created : {{tanggalNote | date:'medium' }}
        </div>

        <!-- tombol action dari note -->
        <div class="note_action">
          <span class="btn" routerLink="/note/modify/1">Edit</span> 
          <span class="btn" (click)="deleteNote()">Delete</span>
        </div>

      </div>

    </div>

    <!-- bagian kanan note untuk menampilkan opsi hide -->
    <div class="right_side">
      <!-- opsi hide -->
      <span (click)="onShowHide()">

        <span *ngIf="isVisible">
          hide
        </span>

        <span *ngIf="!isVisible">
          show
        </span>

      </span>
    </div>

    <div style="clear: both;"></div><!-- clear float -->

  </div>
</div>