Panduan Validasi Di Plugin simple-jpa


Plugin simple-jpa 0.4 dapat di-install dengan memberikan perintah install-plugin simple-jpa 0.4

simple-jpa dapat melakukan validasi domain class dengan memanfaatkan Beans Validation (JSR 349) dengan implementasi berupa Hibernate Validator.  Pengguna perlu memberikan annotation pada domain class.   Daftar annotation bawaan seperti @Size, @Future, @NotNull, dsb dapat ditemukan di lokasi http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html_single/#validator-defineconstraints-builtin.   Bila ini dianggap masih kurang, pengguna dapat membuat annotation sendiri untuk jenis validasi yang belum ada.   Baca dokumentasi di http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html_single untuk informasi lebih lanjut.

simple-jpa akan menyuntikkan method validate() pada controller yang membutuhkan sebuah parameter berupa domain object yang akan divalidasi.   Method ini akan mengembalikan nilai true bila validasi sukses atau nilai false bila validasi gagal. Contoh penggunaan:

Buku buku = new Buku('isbn': model.isbn, 'judul': model.judul, 
   'harga': model.harga, 'tahun': model.tahun, 
   'listPengarang': model.listPengarang.selectedValues)
if (!validate(buku)) return_failed()

Lalu, bagaimana dengan pesan kesalahan?  Setiap model (view model) secara otomatis akan memiliki sebuah atribut bernama errors yang bertipe ObservableMap.   Method validate() bila mengembalikan nilai false (validasi gagal) akan menyimpan pesan kesalahan di variabel errors ini.

ObservableMap adalah sebuah struktur data yang terdiri atas key dan value. Nilai key adalah atribut yang mengalami kesalahan. Nilai value adalah pesan kesalahannya. Berikut ini adalah contoh isi errors pada saat validasi gagal:

isbn: "size must be between 9 and 13"
judul: "may not be empty"
harga: "may not be null"
listPengarang: "may not be empty"

Selain memiliki variabel errors, setiap view model juga memiliki sebuah method bernama hasError() yang akan mengembalikan true bila terdapat pesan kesalahan di errors.

Dari sisi service layer, fungsi validasi sudah berjalan dengan sempurna sampai disini.  Dengan kata lain, sebuah domain object sudah dapat diperiksa apakah isinya valid atau tidak.   Selain itu, pesan kesalahan untuk setiap atribut di domain class juga sudah bisa diketahui.

Akan tetapi, dari sisi presentation layer, masih ada beberapa kendala yang harus dijawab:

  1. Bagaimana cara menampilkan pesan kesalahan tersebut?
  2. Kapan menghapus pesan kesalahan yang telah ditampilkan?

Menampilkan Pesan Kesalahan

Untuk menjawab pertanyaan pertama, simple-jpa menyediakan node errorLabel().   Node ini membutuhkan nilai untuk atribut path yang mewakili sebuah key di map errors, umumnya berupa nama atribut.   Nantinya, node ini akan dirender dalam bentuk JLabel.  Bila terdapat kesalahan di errors untuk key ini, maka JLabel akan menampilkan pesan kesalahan untuk key tersebut. Berikut ini contoh deklarasi sebuah errorLabel:

errorLabel(path: 'judul', constraints: 'wrap')

Pada deklarasi di atas, bila errors di view model mengandung pesan kesalahan dengan key berupa 'judul' maka errorLabel akan menampilkan pesan kesalahan tersebut.

errorLabel() hanya menampilkan kesalahan saja. Selama errors masih mengandung pesan kesalahan, maka selama itu pula pesan kesalahan akan ditampilkan! Oleh sebab itu, pertanyaan kedua perlu dijawab.

PENTING: Sebuah path hanya boleh memiliki sebuah errorLabel() saja.   Bila terdapat lebih dari satu errorLabel() yang memakai path yang sama, maka hanya errorLabel() terakhir yang berfungsi.

TIPS: Nilai path tidak harus selalu sesuai dengan field di domain class.  Pengguna dapat menampilkan notifikasi untuk pesan kesalahan yang bersifat global dengan dengan menggunakan path yang belum dipakai untuk field, misalnya “global_error”. Untuk memberikan pesan kesalahan, pengguna dapat menggunakan kode program seperti model.errors.put("global_error", "ini pesan kesalahan global").

Notifikasi Pada Komponen Dan Menghapus Pesan Kesalahan

Kapan harus menghapus pesan kesalahan sehingga errorLabel() tidak menampilkan apa-apa?  Biasanya, ini dilakukan ketika komponen untuk entry data (misalnya JTextField) sudah diubah nilainya oleh pengguna.   Untuk mempermudah, simple-jpa memungkinkan menambah atribut errorPath pada deklarasi komponen, seperti berikut ini:

textField(id: 'judul', columns: 20, text: bind('judul', target: model, mutual: true), 
   errorPath: 'judul')

Penggunaan errorPath: 'judul' di atas akan menyebabkan:

  • Bila terdapat pesan kesalahan di errors dengan key 'judul', saat user mengetik di textField tersebut, maka pesan kesalahan dengan 'judul' tersebut akan dihapus.
  • Bila terdapat pesan kesalahan di errors dengan key berupa 'judul', maka textField tersebut akan di-highlight dengan warna merah muda.

Notifikasi Pada Komponen

Notifikasi pesan kesalahan yang dipakai secara default adalah BasicHighlightErrorNotification yang akan mengubah background komponen menjadi berwarna Color.PINK bila terjadi kesalahan.

Bila pengguna menginginkan notifikasi yang berbeda, ia dapat membuat turunan dari class ErrorNotification dan membuat  kode program yang akan men-format komponen di method performNotification().   Kemudian, ia perlu menambahkan atribut errorNotification di deklarasi node komponen tersebut dimana isinya adalah class turunan ErrorNotification tersebut.

Sebagai contoh, saya akan membuat sebuah turunan ErrorNotification baru yang akan mengubah border dan warna huruf menjadi merah bila terdapat kesalahan. Isinya akan terlihat seperti berikut ini:

package validation

import simplejpa.validation.ErrorNotification
import javax.swing.*
import javax.swing.border.Border
import java.awt.*

class BorderErrorNotification extends ErrorNotification {

    Border defaultBorder
    Color defaultForeground

    protected BorderErrorNotification(JComponent node, ObservableMap errors, String errorPath) {
        super(node, errors, errorPath)
        defaultBorder = node.border
        defaultForeground = node.foreground
    }

    @Override
    void performNotification() {
        if(errors.containsKey(errorPath)) {
            node.border = BorderFactory.createLineBorder(Color.RED, 4, true)
            node.foreground = Color.RED
        } else {
            node.border = defaultBorder
            node.foreground = defaultForeground
        }
    }
}

Lalu, saya mendaftarkan penggunaan BorderErrorNotification tersebut pada sebuah node komponen, misalnya:

textField(id: 'isbn', columns: 20, text: bind('isbn', target: model, mutual: true), 
   errorPath: 'isbn', errorNotification: BorderErrorNotification.class)

Saya menambahkan nilai untuk atribut errorNotification berupa class BorderErrorNotification yang saya buat (jangan lupa menambahkan import bila diperlukan).

Bila program dijalankan dan terdapat perubahan pada errors, maka method performNotification() milik BorderErrorNotification akan dikerjakan, seperti yang terlihat pada gambar berikut ini:

Membuat ErrorNotification Yang Berbeda

Membuat ErrorNotification Yang Berbeda

Bila saya ingin notifikasi kesalahan untuk seluruh komponen yang ada memakai BorderErrorNotification, maka saya dapat menambahkan baris berikut ini pada file Config.groovy:

griffon.simplejpa.validation.defaultErrorNotificationClass = "validation.BorderErrorNotification"

Bila program dijalankan, sekarang seluruh komponen akan memakai notifikasi validasi dari class BorderErrorNotification yang terlihat seperti pada gambar berikut ini:

Perubahan ErrorNotification Secara Global

Perubahan ErrorNotification Secara Global

PENTING: Bila sebuah komponen memiliki atribut errorNotification, maka notifikasi kesalahan yang akan dipakai akan berdasarkan nilai atribut errorNotification di komponen tersebut. Bila komponen tidak memiliki atribut errorNotification, maka notifikasi kesalahan yang dipakai akan berdasarkan nilai griffon.simplejpa.validation.defaultErrorNotificationClass di Config.groovy. Bila pengaturan di Config.groovy tidak dilakukan, maka notifikasi kesalahan yang akan dipakai adalah berdasarkan class BasicHighlightErrorNotification.

TIPS: Untuk memberikan dialog yang berisi pesan kesalahan, ubah method save() di controller di bagian:

if (!validate(buku)) return

menjadi:

if (!validate(buku)) {
    String pesan = model.errors.collect { k, v -> "$k $v" }.join("\n")
    execInsideUISync {
    	JOptionPane.showMessageDialog(view.mainPanel, pesan, "Kesalahan", JOptionPane.ERROR_MESSAGE)
    }
    return
}

Dialog kesalahan akan terlihat seperti pada gambar berikut ini:

Tampilan Notifikasi Kesalahan Dalam Bentuk Dialog

Tampilan Notifikasi Kesalahan Dalam Bentuk Dialog

TIPS: simple-jpa juga memungkinkan pengguna untuk mematikan notifikasi kesalahan (misalnya pada kasus dimana pengguna ingin memberikan notifikasi kesalahan dalam bentuk dialog secara manual). Untuk itu, tambahkan baris berikut ini
di Config.groovy:

griffon.simplejpa.validation.defaultErrorNotificationClass = "simplejpa.validation.NopErrorNotification"

Menghapus Pesan Kesalahan

Setiap komponen memiliki cara masing-masing untuk menandakan bahwa pesan kesalahan perlu dihapus.  Misalnya pada JTextField, event-nya adalah saat user mengetik di textField tersebut.  Pada JComboBox, event-nya adalah pada saat user memiliki item baru.  Dan masih banyak lagi, belum tersebut JComponent buatan developer sendiri.  Oleh sebab itu, simple-jpa hanya mendukung komponen yang sering dipergunakan saja.

Bila developer memakai komponen yang belum didukung, maka ia perlu membuat sebuah class yang meng-implementasi-kan interface ErrorCleaner, kemudian mendaftarkan class tersebut pada Config.groovy, seperti pada contoh berikut ini:

griffon.simplejpa.validation.errorCleaners = [
    "com.snake.swing.customComponent1": "co.id.snake.validation.CustomComponent1ErrorCleanes",
    "com.snake.swing.customComponent2": "co.id.snake.validation.CustomComponent2ErrorCleaner",
]

Selain memberikan key berupa nama lengkap dari class, pengguna juga dapat memberikan key berupa:

  • default menunjukkan bahwa ErrorCleaner ini akan dikerjakan pada seluruh komponen yang tidak memiliki ErrorCleaner terdaftar. Secara default, nilainya adalah JTextFieldErrorCleaner.
  • * menunjukkan bahwa ErrorCleaner ini bersifat global dan akan dikerjakan untuk seluruh komponen yang ada. Bila terdapat ErrorCleaner untuk sebuah komponen, maka ErrorCleaner tersebut tidak akan dikerjakan.

Sebagai contoh, konfigurasi berikut ini akan menyebabkan seluruh ErrorCleaner tidak akan berfungsi:

griffon.simplejpa.validation.errorCleaners = [
    "*": "simplejpa.validation.NopErrorCleaner",
]

PENTING: Nilai ErrorCleaner yang ada pada Config.groovy akan menimpa nilai ErrorCleaner bawaan.

Contoh yang lebih berguna, misalnya, saya membuat sebuah ErrorCleaner baru yang akan menghapus pesan kesalahan setelah beberapa detik, yang isinya seperti berikut ini:

package validation

import simplejpa.validation.ErrorCleaner

import javax.swing.JComponent
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener
import javax.swing.Timer

class DelayedErrorCleaner implements ErrorCleaner {

    @Override
    void addErrorCleaning(JComponent component, ObservableMap errors, String errorPath) {
        if (errors.getPropertyChangeListeners().findAll { PropertyChangeListener pcl ->
                pcl instanceof MyPropertyChangeListener &&
                ((MyPropertyChangeListener) pcl).errors == errors
            }.isEmpty()) {

            errors.addPropertyChangeListener(new MyPropertyChangeListener(errors))

        }

    }

    class MyPropertyChangeListener implements PropertyChangeListener {

        ObservableMap errors
        Timer timer

        public MyPropertyChangeListener(ObservableMap errors) {
            this.errors = errors
            timer = new Timer(5000, new ActionListener() {
                @Override
                void actionPerformed(ActionEvent e) {
                    errors.clear()
                }
            })
        }

        @Override
        void propertyChange(PropertyChangeEvent evt) {
            if (!errors.isEmpty() && !timer.isRunning()) {
                timer.start()
            }
        }
    }
}

Setelah itu, saya menambahkan baris berikut ini pada file Config.groovy:

griffon.simplejpa.validation.errorCleaners = [
    "*": "validation.DelayedErrorCleaner"
]

Bila saya menjalankan program, maka sekarang pesan kesalahan dan notifikasi kesalahan akan hilang sendiri setelah beberapa detik, seperti yang terlihat pada gambar berikut ini:

Contoh Perubahan ErrorCleaner Secara Global

Contoh Perubahan ErrorCleaner Secara Global

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: