20 February 2016

Spring-boot, MVC, Freemarker, dan Twitter-Bootstrap (bag 4)

Pada tulisan sebelumnya di serial artikel tutorial ini, kita sudah membahas tentang directives dalam freemarker, dan mudah-mudahan kita semua dapat mengetahui fungsi dan kegunaan directives tersebut serta dapat memanfaatkan pengetahuan tersebut untuk membuat sebuah aplikasi web yang efektif dan efisien. Freemarker sebagai sebuah framework dalam rangkaian desain pattern mvc menempati posisi sebagai viewer atau penampil data dari sebuah aplikasi web. Fitur directives yang sudah kita pelajari sebelumnya menjadi kunci dimana kita bisa membuat komponen-komponen halaman aplikasi web yang reusable.
Dalam dokumentasi freemarker, sebetulnya pemakaian directives pun memiliki referensi bagaimana cara pakai directives tersebut, dan apa yang kita pelajari pada tulisan yang lalu hanya meliputi sedikit saja tentang bagaimana cara memakai directives tersebut. Kita hanya membahas directives yang memang kita perlukan dalam pembuatan sebuah aplikasi web, lebih lanjut jika kita ingin menggali lebih dalam tentang directives, dokumentasi tentang directives tersebut dapat di temukan di sini.
Sekarang kita akan membahas fitur lain dalam freemarker yang tidak kalah penting dengan directives yang pernah kita pelajari sebelumnya. Fitur itu adalah fitur built-in. Fitur ini disediakan oleh freemarker untuk menangani tampilan-tampilan data dalam freemarker template language, fitur  built-in ini dibagi menjadi banyak macamnya dilihat dari sisi pemakaiannya, dokumentasi lengkap tentang fitur built-in ini ada di sini. Dan dalam tulisan ini kita juga hanya membahas beberapa fitur built-in saja, dan tentunya masih dalam koridor pengembangan aplikasi web yang kita sudah kita buat sebelumnya. 
Sekarang kita akan mencoba menggunakan salah satu fitur built-in yaitu datetime. Buatlah sebuah controller baru dengan nama IndexController. Adapun isi dari file controller tersebut adalah seperti dibawah ini:
package org.josescalia.blog.simple.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Date;
import java.util.Map;
/**
 * Created by josescalia on 17/02/16.
 */

@Controller
public class IndexController {

    @RequestMapping("/")
    public String getIndex(Map<String, Object> objectMap){
        objectMap.put("model", new Date());
        return "index";
    }
}
Dalam class controller tersebut kita membuat sebuah fungsi yang akan dipanggil jika ada request pada urlhttp://localhost:8080/”. Bisa dikatakan kita hendak membuat default landing page dalam aplikasi web ini. Kita juga meletakkan object Map dengan variabel objectMap dalam fungsi tersebut yang berfungsi membawa data dari layer controller ke layer viewer (freemarker template). Selanjutnya fungsi ini akan memanggil file index yang terletak di dalam foldersrc/main/resources/templates/”. Dan tentu saja kita juga harus membuat file index.ftl yang kita letakkan dalam foldersrc/main/resources/templates/” tersebut, agar fungsi ini tidak terjadi error ketika kita memanggil url tersebut. Adapun isi dari file index.ftl ini adalah sebagai berikut:
<#import "layout/main_layout.ftl" as layout>
<@layout.mainLayout>
<div class="container">
    <h3>Welcome</h3>
    <p>Date : ${model?string('dd MMM yyyy')}</p>
</div>
</@layout.mainLayout>
Ada yang menarik pada isi file index.ftl tersebut. Yaitu variabel model yang kita bawa dari controller ditambahkan ?string('dd MMM yyyy'), mengapa demikian?. Nilai variabel model yang kita isi pada controller memiliki tipe data java.util.Date. Jika model tersebut dituliskan dalam freemarker seperti apa adanya (${model}) saja, freemarker tidak bisa menerjemahkan langsung object java.util.Date tersebut, sebab object tersebut tidak tergolong object yang displayable pada freemarker template language (ftl), namun freemarker memiliki sebuah fungsi built-in yang bisa membuat object ini menjadi displayable, salah satunya yaitu dengan menambahkan ?string('dd MMM yyyy') dibelakang variabel model. Fungsi built-in ini memerintahkan kepada freemarker untuk menerjemahkan object java.util.Date tersebut sebagai string dan dengan format “dd MMM yyyy”.  Sekarang mari kita jalankan aplikasi web tersebut dan buka urlhttp://localhost:8080/” pada browser. Maka seharusnya tampilan halaman aplikasi web tersebut seperti gambar dibawah ini:

Kita dapat melihat bahwa variabel model dalam halaman aplikasi web tersebut diterjemahkan menjadi tanggal saat ini dengan format 'dd MMM yyyy'. Cobalah anda rubah sendiri format tampilan tanggalnya menjadi format yang lain sesuai dengan referensi Date Format dalam pemrograman java. Selain built-in ?string() diatas, ada juga fitur built-in lain yang bisa menerjemahkan object java.util.Date seperti: ?date, ?datetime, dan lain-lain, silahkan eksplor sendiri mengacu pada dokumentasi referensi freemarker di sini.

Fitur built-in selanjutnya yang akan kita coba adalah built-in for string. Sekarang coba kita modifikasi file IndexController dengan menambahkan baris ini pada fungsi getIndex:
objectMap.put("modelName", "josescalia");
Kemudian file index.ftl kita modifikasi lagi menjadi seperti ini:
<#import "layout/main_layout.ftl" as layout>
<@layout.mainLayout>
<div class="container">
    <h3>Welcome</h3>
    <p>Date : ${model?date}</p>
    <div class="col-lg-6 col-md-6 col-sm-12">
        <table class="table table-bordered table-striped table-condensed">
            <tr>
                <td>Model Normal</td>
                <td class="text-info">${modelName}</td>
            </tr>
            <tr>
                <td>Huruf Depan Kapital</td>
                <td class="text-info">${modelName?capitalize}</td>
            </tr>
            <tr>
                <td>Lower Case</td>
                <td class="text-info">${modelName?lower_case}</td>
            </tr>
            <tr>
                <td>Upper Case</td>
                <td class="text-info">${modelName?upper_case}</td>
            </tr>
            <tr>
                <td>Jumlah Char</td>
                <td class="text-info">${modelName?length}</td>
            </tr>
            <tr>
                <td>3 huruf pertama</td>
                <td class="text-info">${modelName?substring(0,3)}</td>
            </tr>
            <tr>
                <td>3 huruf pertama dengan upper case</td>
                <td class="text-info">${modelName?substring(0,3)?upper_case}</td>
            </tr>
        </table>
    </div>
</div>
</@layout.mainLayout>
Sekarang silahkan jalankan dan lihat hasil modifikasi yang sudah kita lakukan, pada browser saya hasilnya seperti gambar dibawah ini:
Kalo kita perhatikan file index.ftl yang sudah kita modifikasi tadi, sebetulnya built-in string sangat familiar dengan fungsi-fungsi manipulasi string yang biasa kita temui dalam pemrograman java. Jadi seharusnya ini bukan menjadi kendala yang berat buat kita untuk bisa beradaptasi dengan fungsi-fungsi built-in for string pada freemarker template language (ftl) ini.
Pada beberapa tulisan lalu kita sudah berlatih menambahkan fitur tambah data pada aplikasi web kita, tapi kita belum memiliki fitur edit data. Tidak ada salahnya kita membahas penambahan fitur edit data ini pada aplikasi web kita, pengetahuan yang baru kita bahas di atas, bisa kita pakai untuk membuat tampilan data-data pada aplikasi web kita menjadi lebih menarik dan lebih informatif tentunya. Tapi sebelumnya kita akan kembangkan dulu model data Author menjadi seperti dibawah ini:
package org.josescalia.blog.simple.model;

import javax.persistence.*;
import java.util.Date;
/**
 * Created by josescalia on 25/10/15.
 */
@Entity
@Table(name = "author")
public class Author {

    private Long id;
    private String authorName;
    private String authorAddress;
    private Date createdDate;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID", length = 11)
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    @Column(name = "AUTHOR_NAME", length = 100,nullable = false, unique = true)
    public String getAuthorName() {
        return authorName;
    }
    public void setAuthorName(String authorName) {
        this.authorName = authorName;
    }
    @Column(name = "AUTHOR_ADDRESS", length = 255,nullable = true)
    public String getAuthorAddress() {
        return authorAddress;
    }
    public void setAuthorAddress(String authorAddress) {
        this.authorAddress = authorAddress;
    }
    @Column(name = "CREATED_DATE", nullable = true)
    public Date getCreatedDate() {
        return createdDate;
    }
    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }
    @Override
    public String toString() {
        return "Author{" +
                "id=" + id +
                ", authorName='" + authorName + '\'' +
                ", authorAddress='" + authorAddress + '\'' +
                ", createdDate=" + createdDate +
                '}';
    }
}
Pada model data Author tersebut kita menambahkan sebuah properti model dengan nama createdDate, dan properti ini memiliki tipe data Date. Perubahan yang kita buat pada class model Author tersebut juga meliputi penambahan getter-setter dan perubahan fungsi toString. Penambahan  baris kode pada fungsi getter dalam class model Author tersebut termasuk mendefinisikan nama field dalam tabel yang nantinya akan terbuat secara otomatis dalam database. Perhatikan kita memberikan attribut boleh null (nullable=true) pada kolom CREATED_DATE tersebut. Ini  sebuah trik yang bisa dilakukan untuk tidak merusak data dalam database jika penambahan fitur tersebut dilakukan ketika data sudah ada sebelumnya dan data tersebut tidak diijinkan untuk dihapus.
Langkah selanjutnya mari kita rubah file list.ftl yang ada pada foldersrc/main/templates/author/” sehingga properti createdDate yang baru kita tambahkan pada model Author dapat tampil pada layer viewer. Adapun perubahan pada file list.ftl ini sebagai berikut:
...
<table class="table table-bordered table-striped table-condensed">
    <thead>
    <tr>
        <th>Id</th>
        <th>Name</th>
        <th>Address</th>
        <th>Created</th>
    </tr>
    </thead>
    <tbody>
    <#list authorList as author>
        <tr>
            <td style="text-align:center;"><a href="edit?id=${author.id}">${author.id}</a></td>
            <td>${author.authorName}</td>
            <td>${author.authorAddress}</td>
            <td>${author.createdDate?date('dd MMM yyyy HH:mm:ss')}</td>
        </tr>
    </#list>
    </tbody>
</table>
</div>
...
Pada kode di atas kita menerapkan pengetahuan yang baru kita pelajari di atas, kita memformat data createdDate sesuai dengan yang kita pelajari untuk bisa tampil dalam halaman aplikasi web kita. Sekarang silahkan jalankan aplikasi web tersebut, dan buka url http://localhost:8080/author/list, bagaimana hasil yang anda dapatkan, seharusnya error seperti gambar saya dibawah ini:
Error di atas terjadi karena memang data createdDate pada tabel author dalam database kita null dan kita sengaja membiarkan null tersebut terjadi dalam database akibat dari penambahan kolom CREATED_DATE yang memperbolehkan null sebelumnya. Dan file list.ftl ini tidak dikondisikan untuk menampilkan data dengan nilai null. Secara logika memang sudah tidak masuk akal, bagaimana mungkin nilai null kita format menjadi suatu tampilan tertentu sesuai dengan yang kita inginkan. Tanpa diformat pun sudah pasti akan error seperti di atas, apalagi diformat.

Lalu bagaimana cara kita menangani permasalahan di atas, tepatnya menangani error akibat data yang null. Freemarker memiliki suatu kemampuan untuk beradaptasi dengan kondisi branching (if else) menggunakan satu baris kode atau biasa kita kenal dengan sebutan one line if. Kita akan menangani permasalahan di atas dengan menerapkan one line if yang pengertiannya kira-kira seperti pseudo code ini “jika data createdDate ada tampilkan dengan format dd MMM yyyy HH:mm:ss dan jika tidak ada kosongkan saja”. Pseudo code tersebut jika kita terjemahkan dalam baris syntax freemarker akan menjadi seperti ini : 
<td><#if author.createdDate??>${author.createdDate?string('dd MMM yyyy HH:mm:ss')}</#if></td>
Dengan kode seperti ini, freemarker akan bisa menerjemahkan dengan baik pseudo code yang kita inginkan. Silahkan buka kembali aplikasi web anda dan lihat hasilnya, pada gambar saya seperti dibawah ini sebab saya memang menambahkan secara manual salah satu data author pada database.

Pada tulisan lalu kita belum membahas bagaimana edit data pada aplikasi web tersebut. Mekanisme edit data memang saya kesampingkan dulu, karena untuk membuat sebuah mekanisme edit data yang efektif dan efisien kita membutuhkan beberapa trik freemarker yang kita pelajari belakangan ini. Jika saya langsung masuk ke dalam mekanisme edit data sebelum kita mempelajari beberapa trik tersebut, pastinya akan banyak pertanyaan. Sekarang saya rasa waktu yang tepat untuk membahas masalah edit data ini.
Kita akan membuat mekanisme edit dan delete data Author sebagai penutup dalam tutorial kali ini. Sebelum kita memulainya, flow berikut ini akan menjadi panduan bagi kita untuk membuat mekanisme edit data tersebut. “Dalam tabel list Author akan ada dua buah tombol action, yg terdiri dari edit dan delete data pada tiap tiap baris data Author. Jika user mengklik tombol delete, maka akan tampil pesan konfirmasi berisi pertanyaan 'Apakah yakin data ini akan di hapus?', jika user mengklik tombol OK pada pesan konfirmasi, maka aplikasi web akan mem-post sebuah http request ke dalam controller yang memanggil fungsi delete, namun jika user mengklik tombol Cancel, maka pesan konfirmasi tersebut akan hilang dan tidak akan melakukan aksi apa-apa. Kemudian jika user mengklik tombol edit pada salah satu baris data Author dalam tabel list Author tadi, maka aplikasi web kita akan mengarahkan kepada halaman edit form Author, kemudian jika user mengklik tombol Save pada form edit data Author tersebut, perubahan data akan disimpan dalam database dan aplikasi akan menampilkan pesan konfirmasi penyimpanan, namun jika user mengklik tombol Cancel pada form tersebut, maka aplikasi web kita akan kembali mengarahkan user ke halaman yang berisi tabel list Author”.
Untuk mendukung flow ini,  langkah pertama yang harus kita lakukan adalah memodifikasi tabel list Author sehingga bisa menjadi seperti gambar dibawah ini:
Dalam tampilan tabel tersebut kita merubah header tabel paling kiri menjadi “Action” dan setiap baris pada data Author memiliki dua tombol yaitu edit dan delete dengan icon-icon bawaan bootstrap. Untuk merubah tampilan seperti ini yang kita lakukan adalah meng-edit filelist.ftl” yang ada pada foldersrc/main/resources/template/author/” menjadi seperti dibawah ini:
...
<table class="table table-bordered table-striped table-condensed">
    <thead>
    <tr>
        <th>Action</th>
        <th>Name</th>
        <th>Address</th>
        <th>Created</th>
    </tr>
    </thead>
    <tbody>
    <#list authorList as author>
        <tr>
            <td style="text-align:center;">
                <a href="edit?id=${author.id}" class="btn btn-info btn-sm"><span class="glyphicon glyphicon-edit"></span> </a>
                <a href="#" class="btn btn-danger btn-sm" onclick="deleteData('${author.id}')"><span class="glyphicon glyphicon-trash"></span> </a>
            </td>
            <td>${author.authorName}</td>
            <td>${author.authorAddress}</td>
            <td><#if author.createdDate??>${author.createdDate?string('dd MMM yyyy HH:mm:ss')}</#if></td>
        </tr>
    </#list>
    </tbody>
</table>
...
Perhatikan perubahan yang kita lakukan pada file tersebut, kita hanya menggunakan tag a dan menghiasnya menjadi tombol dengan class-class yang dimiliki bootstrap. Namun pada tombol edit kita menambahkan sebuah attribut onclick, dimana attribut itu berfungsi jika tombol delete itu diklik maka akan memanggil sebuah fungsi javascript yaitu deleteData dan fungsi ini memiliki sebuah parameter. Kemudian kita teruskan memodifikasi file list tersebut, yaitu menambahkan sebuah fungsi javascript dengan nama deleteData seperti contoh berikut ini:
<script type="text/javascript">
    ...
    function deleteData(id){
        if(confirm("Apakah anda yakin untuk menghapus data ini ?")){
            $.post("delete", "id=" + id, function(textMessage){
                if(textMessage == "Delete Succeed"){
                    alert(textMessage);
                    window.location.reload(true);
                }
            })
        }
    }
</script>
Yap, kita sudah membuat fungsi javascript deleteData tersebut. Kemudian perhatikan kembali pada file list tentang penambahan tombol-tombol pada tabel list Author lagi, ada tombol edit disana yang jika user mengklik tersebut, maka aplikasi web akan mengarahkan ke halaman dengan urlhttp://localhost:8080/author/edit?id=”. Dua perubahan ini harus kita akomodir dengan menambahkan dua fungsi dalam controller yaitu fungsi edit dan fungsi delete dengan request mapping berbeda satu sama lain. Tambahkan dua fungsi dibawah ini pada class AuthorController.
@RequestMapping(value = "/author/edit", method = RequestMethod.GET)
public String edit(@RequestParam Long id, Map<String, Object> objectMap) {
    Author author = authorRepository.findOne(id);
    objectMap.put("model", author);
    return "/author/edit";
}

@RequestMapping(value = "/author/delete", method = RequestMethod.POST)
public @ResponseBody String delete(@RequestParam Long id) {
    try{
        authorRepository.delete(id);
        return "Delete Succeed";
    }catch (Exception e){
        e.printStackTrace();
        return "Delete Failed :" + e.getMessage();
    }
}
Fungsi edit yang kita buat pada class AuthorController di atas, memiliki dua buah paramater. Paramater yang pertama adalah paramater yang merupakan url paramater (edit?id=) dan parameter kedua merupakan paramater dengan tipe data Map yang menjadi penghubung antara controller dengan freemarker seperti yang pernah kita pelajari sebelumnya, dan fungsi ini akan dipanggil jika http get request mengarah pada urlhttp://localhost:8080/author/edit”. Fungsi ini berisi bagaimana cara aplikasi web mengambil data dari dalam database dengan parameter id melalui object authorRepository, kemudian data yang diambil dari database tersebut dimasukkan ke dalam variabel model dan diletakkan pada object Map untuk di bawa ke layer viewer (freemarker). Kemudian kembalian dari fungsi edit ini akan meminta kepada aplikasi web untuk menggunakan sebuah file freemarker template bernama “edit” dalam folder/src/main/templates/author/” sebagai layer viewer-nya.
Sementara fungsi delete dalam class AuthorController diatas memiliki sebuah paramater dengan tipe data Long dan akan dipanggil jika ada sebuah http post request dengan alamat “http://localhost:8080/author/delete”, fungsi ini akan mengembalikan sebuah teks yang akan dicetak dalam halaman aplikasi web dan tidak memerintahkan aplikasi web untuk menampilkan file freemarker sebagai viewer-nya, melainkan hanya mencetak langsung string tersebut. Dalam fungsi delete ini terlihat bahwa proses delete data dalam database akan dilakukan melalui object authorRepository dengan paramater id, dan proses ini ada dalam blok try-catch untuk mencegah kemungkinan error yang terjadi. Jika proses penghapusan data berhasil maka fungsi ini akan mengembalikan sebuah teks “Delete Succeed” dan jika gagal (terjadi error) akan mengembalikan teks “Delete Failed” beserta dengan alasan kenapa tidak bisa menghapus data tersebut.
Perhatikan perbedaan metode HTTP di dalam dua fungsi tersebut, fungsi edit menggunakan HTTP GET, dan fungsi delete menggunakan metode HTTP POST, pertimbangan menggunakan dua metode HTTP yang berbeda lebih kepada konsep sedikit mengamankan data ketika terjadi proses penghapusan data. Seperti yang mungkin sama-sama kita ketahui bahwa paramater yang di-supply ketika menggunakan HTTP POST tidak bisa dituliskan langsung dalam URL browser, sebab jika dituliskan aplikasi web akan mengenali sebagai metode HTTP GET, dan proses penghapusan tidak akan pernah terjadi. Jadi proses penghapusan data ini untuk sebagian user awam dengan aksi hapus data yang hanya bisa dilakukan lewat aplikasi web. Kedua perbedaan metode ini juga mengakibatkan pemakaian fungsi yang berbeda pada fungsi deleteData dalam javascript yang kita tuliskan sebelumnya.
Kemudian yang kita lakukan selanjutnya adalah membuat fileedit.ftl” dan kita letakkan di dalam folder/src/main/templates/author/”, file ini merupakan file tujuan dari metode edit pada controller yang kita buat tadi. Adapun isi dari file edit.ftl ini adalah sebagai berikut:
<#import "../layout/main_layout.ftl" as layout>
<@layout.mainLayout>
<div class="col-lg-6 col-md-6 col-sm-12">
    <form method="post" id="editForm" class="form-horizontal">
        <div class="panel panel-info">
            <div class="panel-heading">
                <h4>Edit Author</h4>
            </div>
            <div class="panel-body">
                <#include "_form.ftl">
                <input type="hidden" id="id" name="id" value="${model.id}">
            </div>
            <div class="panel-footer text-right">
                <#include "../include/_edit_component.ftl">
            </div>
        </div>
    </form>
</div>
</@layout.mainLayout>
<script type="text/javascript">
    $(function(){
        $("#btnUpdate").click(function(){
            $.post("/service/json/author/save", $("#editForm").serialize(), function(responseText){
                alert(responseText);
                if(responseText == "Save Succeed"){
                    window.location = "list"
                }
            });
        });
    })
</script>
Pada file tersebut, kita banyak me-reuse komponen yang kita buat. Seperti komponen form dan komponen _edit_component.ftl. Kita memang belum membuat komponen  _edit_component.ftl, namun sebetulnya komponen tersebut tidak berbeda jauh dengan komponen _add_new_component.ftl yang kita buat sebelumnya, hanya saja id dari tombol Save pada komponen tersebut kita ganti menjadi btnUpdate, adapun isi lengkap dari file _edit_component tersebut seperti dibawah ini:
<a href="#" id="btnUpdate" class="btn btn-info btn-sm"><span class="glyphicon glyphicon-floppy-disk"></span> Update</a>
<a href="#" id="btnCancel" class="btn btn-danger btn-sm" onclick="history.back()"><span class="glyphicon glyphicon-step-backward"></span> Back</a>
Relatif sama bukan?, untuk btnCancel kita kasih sentuhan javascripthistory.back()”, sentuhan ini akan membuat aplikasi web akan kembali ke halaman sebelumnya jika tombol Cancel di klik oleh user. Kita kembali ke file edit.ftl tadi, perhatikan bahwa ada setelah baris include _form.ftl, kita menyelipkan sebuah komponen hidden dengan nama “id”. Komponen ini bertugas menampung “id” dari data Author. Dan oleh karena id author merupakan primary key dalam model data Author, maka sudah seharusnya nilai dari id ini tidak boleh berubah, dan untuk mengakomodir tidak dibolehkannya id tersebut berubah itulah kita menggunakan komponen html hidden, ada komponennya namun tidak terlihat dari sisi user.
Kemudian pada bagian bawah file edit.ftl tersebut kita tuliskan sebuah fungsi javascript. Fungsi ini memerintahkan aplikasi web melakukan HTTP POST Request ke URL/service/json/author/save” dan kemudian kita tangkap kembalian dari metode HTTP POST tersebut ke dalam sebuah alert dan kondisi jika kembalian teks berisi “Save Suceed” maka kita arahkan kembali ke halaman list.
Sebelum kita menjalankan aplikasi web tersebut, ada hal lagi yang mesti kita modifikasi, yaitu file _form.ftl. Perhatikan isi file _form.ftl, semua komponen html yang berbentuk input text box belum kita isi nilainya, artinya kalo kita langsung jalankan aplikasi web kita tanpa merubah file _form.ftl tersebut, maka niscaya hasilnya akan seperti gambar di bawah ini:
Kenapa kok kosong, tidak ada data, padahal dari controller sudah di lemparkan variabel dengan nama “model” lewat object Map?. Ya karena variabel “model” tersebut belum di-apply ke masing-masing komponen. Nah sekarang tinggal kita apply saja variabel model tersebut ke dalam masing-masing komponen pada file _form.ftl ini, menjadi seperti berikut ini:
<div class="form-group">
    <label for="authorName" class="col-lg-4 col-md-6 col-sm-12">Author Name</label>
    <div class="col-lg-8 col-md-6 col-sm-12">
        <input type="text" class="form-control" id="authorName" name="authorName" 
           value="${model.authorName}">
    </div>
</div>
<div class="form-group">
    <label for="authorAddress" class="col-lg-4 col-md-6 col-sm-12">Address</label>
    <div class="col-lg-8 col-md-6 col-sm-12">
       <textarea id="authorAddress" name="authorAddress" class="form-control">
          ${model.authorAddress}</textarea>
    </div>
</div>
Lihatlah pada kode di atas bagaimana cara kita apply model terhadap komponen-komponen tersebut. Sama seperti kita meng-apply komponen hidden pada file _edit.ftl. Sekarang silahkan jalankan aplikasi web tersebut dan cobalah pilih salah satu data pada tabel Author untuk di edit, jalankan fungsinya secara normal, apakah semuanya sudah sesuai dengan skenario kita?

Tapi tunggu dulu, apakah kita telah selesai? saya katakan meski sudah berhasil melakukan edit data dan fungsinya sudah berjalan normal sesuai skenario yang kita buat, tapi kita belum selesai, cobalah klik tombol Add New pada halaman list, apakah form tambah data akan keluar?, jika tidak keluar coba lihat log-nya, seharusnya ada log seperti dibawah ini kira-kira :
FTL stack trace ("~" means nesting-related):
- Failed at: ${model.authorName}  [in template "author/_form.ftl" at line 4, column 90]
- Reached through: #include "_form.ftl"  [in template "author/list.ftl" at line 41, column 17]
~ Reached through: #nested  [in template "layout/main_layout.ftl" in macro "mainLayout" at line 17, column 9]
~ Reached through: @layout.mainLayout  [in template "author/list.ftl" at line 2, column 1]
----] with root cause

freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:

==> model  [in template "author/_form.ftl" at line 4, column 92]

Wah, ternyata ada error dibelakang aplikasi web kita, yup error itu terjadi karena freemarker tidak sanggup mencetak nilai model.authorName dan model.authorAddress yang null, sebab memang pas pada saat kita ingin buat sebuah data Author yang baru, object model sudah didefinisikan di form-nya, padahal di controller-nya tidak ada variabel model yang dilempar ke layer viewer. Lalu bagaimana cara kita menangani hal ini?, Ok langsung saja kita lakukan. Yang pertama kita modifikasi file AuthorController, pada fungsi getList tambahkan baris kode menjadi seperti dibawah ini:
…
@RequestMapping(value = "/author/list", method = RequestMethod.GET)
public String getList(Map<String, Object> objectMap) {
    objectMap.put("authorList", (List<Author>) authorRepository.findAll());
    objectMap.put("model", new Author());
    return "author/list";
}
…
Yang kedua modifikasi kembali file _form.ftl menjadi seperti dibawah ini:
<div class="form-group">
    <label for="authorName" class="col-lg-4 col-md-6 col-sm-12">Author Name</label>
    <div class="col-lg-8 col-md-6 col-sm-12">
        <input type="text" class="form-control" id="authorName" name="authorName" 
         value="${model.authorName!""}">
    </div>
</div>
<div class="form-group">
    <label for="authorAddress" class="col-lg-4 col-md-6 col-sm-12">Address</label>
    <div class="col-lg-8 col-md-6 col-sm-12">
       <textarea id="authorAddress" name="authorAddress" class="form-control">
           ${model.authorAddress!""}</textarea>
    </div>
</div>
Perhatikan file _form.ftl tersebut, kita menambahkan !”” pada masing-masing pemakaian attribut model dalam setiap komponen. Tambahan ini memungkinkan freemarker akan mencetak character kosong jika properti object Author memiliki nilai null, pseudo code-nya begini kira kira “jika properti model Author tidak sama dengan null, maka cetaklah ke dalam komponen tersebut, namun jika null, cetak saja karakter kosong pada halaman aplikasi web tersebut".

Silahkan dicoba sendiri hasilnya. Seharusnya tidak ada lagi masalah, pada saat tambah atau edit data Author.  Demikianlah pembahasan tentang penggunaan fungsi built-in pada freemarker ditambah dengan pelengkapan mekanisme “edit” dan “delete” pada aplikasi web kita tersebut. Mudah-mudahan tutorial ini dapat memberikan pengertian dan pengetahuan lebih mendalam tentang spring-boot pada khususnya. Untuk tulisan tentang spring-boot selanjutnya, kita akan membahas tentang memodifikasi spring repository, modifikasi ini sangat berguna jika kita hendak melakukan operasi data yang tidak sekedar CRUD (Create, Read, Update, Delete) saja. 

Semoga bermanfaat.
Depok, 21 Februari 2016


Josescalia

No comments: