Pengguna app selalu — selalu — akan memasukkan data yang tidak kita antisipasi: teks di tempat angka, tahun 1999 saat data hanya mulai 2000, nama berkas yang tidak ada, atau sekadar spasi kosong. App yang baik tidak crash dan tidak melanjutkan dengan data salah; app yang baik menolak dengan pesan yang jelas lalu mempersilakan pengguna memperbaiki. Chapter ini menyusun lapisan validasi dari properti komponen sampai fungsi terpusat. Cakupannya:
- validasi otomatis dari properti komponen (
Limits,Items) - pemeriksaan manual teks:
strtrim,str2double,isempty,exist - mencegah eksekusi dengan
returnawal dan konfirmasiuiconfirm - pola validasi terpusat
[ok, pesan] = validasiInput(input)
Lapisan Pertama: Validasi Bawaan Komponen #
Sebagian aturan bisa kita tanam di komponennya sendiri — pengguna tidak pernah sempat memasukkan nilai salah:
efTahun = uieditfield(fig, "numeric", ...
"Limits", [2000 2025]); % rentang dijaga komponen
ddKota = uidropdown(fig, ...
"Items", ["Jakarta", "Bandung", "Surabaya"]);Edit Field "numeric" punya dua penjaga sekaligus: teks yang bukan angka ditolak otomatis (MATLAB menampilkan error tooltip dan mengembalikan nilai valid terakhir), dan Limits menjaga nilai tetap dalam rentang — dokumentasi resmi menyebutkan batas atas/bawah ini beserta pengaturan ke-ikut-sertaannya. DropDown lebih aman lagi: nilai selalu berasal dari daftar Items, sehingga “Bandungg” hasil ketik salah mustahil tersimpan. Prinsipnya: kurasikan aturan ke komponen sebisa mungkin — sisa ruang salahnya mengecil dengan sendirinya.
Sisa celahnya tetap ada: input teks bebas ("text"), kombinasi beberapa field yang masing-masing valid tetapi bersama-sama tidak (tanggal mulai setelah tanggal akhir), dan berkas eksternal. Celah inilah yang dijaga callback.
Lapisan Kedua: Pemeriksaan Manual dengan Guard Clause #
Pola callback validasi yang sehat memakai guard clause: periksa satu aturan, gagal → tampilkan pesan + return, sehingga badan callback hanya berisi kode untuk keadaan yang sudah pasti bersih:
function MuatButtonPushed(app, event)
namaBerkas = strtrim(app.EditFieldBerkas.Value); % sanitasi
if isempty(namaBerkas)
uialert(app.UIFigure, "Nama berkas masih kosong.", ...
"Input Kurang", Icon="warning");
return
end
if ~exist(namaBerkas, 'file')
uialert(app.UIFigure, ...
sprintf("Berkas tidak ditemukan: %s", namaBerkas), ...
"Berkas Hilang", Icon="error");
return
end
% Semua pemeriksaan lolos: baru aman mengeksekusi.
app.data = readmatrix(namaBerkas);
app.StatusLabel.Text = "Data berhasil dimuat.";
endMari kita bedah baris-baris kode di atas:
- Baris 2 (
strtrim(...)): sanitasi pertama — spasi dan tab di tepi teks dibuang sebelum diperiksa;" "menjadi""dan tertangkap aturan kosong. - Baris 4-8 (
isempty→uialert→return): aturan pertama sekaligus pola seluruh blok — pesan yang menyebut masalahnya, ikon yang sesuai, lalureturnmencegah baris berikutnya berjalan. - Baris 10-15 (
exist(namaBerkas, 'file')): pemeriksaan dunia luar — berkas yang pengguna klaim ada benar-benar dicek ke sistem berkas sebelum dipakai. - Baris 18-19 (
readmatrix): hanya dieksekusi setelah semua guard lolos — tidak ada cabangelsemenjuntai.
Untuk field teks yang berharap angka, pasangan str2double() + isnan() adalah pemeriksaan standar: str2double('abc') mengembalikan NaN tanpa error, sehingga kita menguji isnan(nilai) alih-alih menangkap galat.
Konfirmasi Sebelum Aksi Berat #
Untuk aksi yang mengubah data atau lama (menimpa berkas, reset semua input), validasi diganti konfirmasi. Fungsi uiconfirm menampilkan dialog dengan pilihan yang kita definisikan dan mengembalikan pilihan pengguna sebagai teks:
selection = uiconfirm(app.UIFigure, ...
"Timpa berkas yang sudah ada?", "Konfirmasi Simpan", ...
Icon="warning", ...
Options=["Timpa", "Simpan Baru", "Batal"], ...
DefaultOption=2, CancelOption=3);
switch selection
case "Timpa"
simpanData(app.dataPath, app.data);
case "Simpan Baru"
app.dataPath = namaBaru;
simpanData(app.dataPath, app.data);
otherwise
app.StatusLabel.Text = "Penyimpanan dibatalkan.";
endSintaks uiconfirm di atas terverifikasi dari dokumentasi resmi: Options menentukan tombol-tombolnya, DefaultOption posisi tombol Enter, CancelOption tombol yang dianggap membatalkan, dan hasil kembaliannya dibandingkan dengan switch. Dialog ini modal — jendela app tidak bisa disentuh sampai pengguna memilih.
Pola Validasi Terpusat #
Ketika form membesar, guard clause yang tersebar di banyak callback mulai sulit dijaga. Pola penutupnya: kumpulkan seluruh aturan ke satu fungsi validasiInput(input) yang mengembalikan [ok, pesan] — callback tinggal bertanya sekali:
function ProsesButtonPushed(app, event)
input = bacaForm(app); % kumpulkan nilai komponen
[ok, pesan] = validasiInput(input); % satu pintu semua aturan
if ~ok
uialert(app.UIFigure, pesan, "Input Tidak Valid", Icon="warning");
return
end
app.hasil = analisisCuaca(input); % eksekusi dengan data bersih
endKeuntungannya ganda: aturan form hidup di satu tempat (mudah diaudit dan diperbarui), dan fungsi tersebut teruji tanpa GUI. Inilah inti contoh.m — enam kasus uji digiring melewati fungsi yang sama:
kasus = { ...
struct('namaStasiun', 'Stasiun Jakarta', 'tahunTeks', '2023', 'bulan', 7); ...
struct('namaStasiun', ' ', 'tahunTeks', '2023', 'bulan', 7); ...
struct('namaStasiun', 'Stasiun Bogor', 'tahunTeks', 'abc', 'bulan', 7); ...
struct('namaStasiun', 'Stasiun Bandung', 'tahunTeks', '1999', 'bulan', 7); ...
struct('namaStasiun', 'Stasiun Surabaya', 'tahunTeks', '2023', 'bulan', 13); ...
struct('namaStasiun', 'Stasiun Medan', 'tahunTeks', '2023', 'bulan', 6)};
for k = 1:size(kasus, 1)
[ok, pesan] = validasiInput(kasus{k});
if ok
fprintf('kasus %d: VALID -> eksekusi lanjut (%s)\n', k, pesan);
else
fprintf('kasus %d: GAGAL -> uialert: %s\n', k, pesan);
end
endDi dalam validasiInput(), pemeriksaan berjalan berurutan dari yang paling mendasar — strtrim + isempty untuk nama, str2double + isnan untuk tahun, rentang 2000-2025, lalu bulan bulat 1-12 — dan return begitu satu aturan gagal, sehingga pesan yang dilaporkan selalu aturan pertama yang dilanggar.
Output yang Dihasilkan:
kasus 1: VALID -> eksekusi lanjut (Stasiun Jakarta, tahun 2023 bulan 7 siap diproses)
kasus 2: GAGAL -> uialert: Nama stasiun tidak boleh kosong.
kasus 3: GAGAL -> uialert: Tahun "abc" bukan angka.
kasus 4: GAGAL -> uialert: Tahun 1999 di luar rentang 2000-2025.
kasus 5: GAGAL -> uialert: Bulan 13 bukan bilangan bulat 1-12.
kasus 6: VALID -> eksekusi lanjut (Stasiun Medan, tahun 2023 bulan 6 siap diproses)Ringkasan #
Validasi input berlapis: properti komponen (Limits, Items) menutup salah di titik ketik; guard clause di callback (struktur uialert + return) menutup salah di titik eksekusi; dan konfirmasi uiconfirm menjaga aksi berat. Untuk form besar, seluruh aturan dikumpulkan dalam fungsi terpusat [ok, pesan] = validasiInput(input) yang callback panggil sekali sebelum eksekusi. Fungsi itulah yang teruji di contoh.m — enam kasus dari nama kosong sampai bulan 13 ditolak dengan pesan yang tepat, semuanya tanpa GUI.