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 return awal dan konfirmasi uiconfirm
  • 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:

matlab
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:

matlab
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.";
end

Mari kita bedah baris-baris kode di atas:

  1. Baris 2 (strtrim(...)): sanitasi pertama — spasi dan tab di tepi teks dibuang sebelum diperiksa; " " menjadi "" dan tertangkap aturan kosong.
  2. Baris 4-8 (isempty → uialert → return): aturan pertama sekaligus pola seluruh blok — pesan yang menyebut masalahnya, ikon yang sesuai, lalu return mencegah baris berikutnya berjalan.
  3. Baris 10-15 (exist(namaBerkas, 'file')): pemeriksaan dunia luar — berkas yang pengguna klaim ada benar-benar dicek ke sistem berkas sebelum dipakai.
  4. Baris 18-19 (readmatrix): hanya dieksekusi setelah semua guard lolos — tidak ada cabang else menjuntai.

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:

matlab
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.";
end

Sintaks 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:

matlab
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
end

Keuntungannya 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:

matlab
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
end

Di 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:

text
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.