İçeriğe geç
academia.sh

Ders 17 / 22

Form Durum Yönetimi

Alan değerinin belgede mi uygulamanın durumunda mı tutulacağı, iki yaklaşımın bildirim maliyeti, form durumunun değerlerden başka taşıdığı bilgiler ve başlangıç değeri ile kirliliğin gönderim akışındaki rolü.

İçindekiler

Veri erişimi konusu akışı tek yönde kurdu: sunucudan ekrana. Kuzey Yamaç arayüzünün ikinci yarısı ters yönde çalışır. Gözlemci ölçüm giriş formunu açar, istasyonu seçer, değeri yazar, notu ekler ve gönderir. Bu akışın ilk sorusu teknik olarak küçük ama sonuçları büyüktür: alanın o anki değeri nerede durur?

İki cevap vardır. Değer belgenin kendisinde durabilir — alan yazılanı tutar, uygulama gerektiğinde okur. Ya da değer uygulamanın durumunda durur — alan yalnızca durumu gösterir, her tuş vuruşu durumu günceller ve alan güncellenmiş durumdan yeniden çizilir. Bu ders iki cevabın ne kazandırıp ne götürdüğünü ve form durumunun değerlerden başka neler taşıdığını ele alır.

İki Doğruluk Kaynağı

Denetimsiz alan (uncontrolled input), değerin belgede tutulmasıdır. Alan kendi durumunu yönetir; uygulama başlangıç değerini verir, sonra araya girmez. Değer gönderim anında ya da gerektiğinde okunur. Web’in Temelleri ve HTML kursunda tanımlanan giriş listesi bu yaklaşımın doğal karşılığıdır: tarayıcı alanları zaten toplar.

Denetimli alan (controlled input), değerin uygulamanın durumunda tutulmasıdır. Alan gösterilen değeri durumdan alır ve her değişiklikte durumu güncelleyen bir olay bildirir. Uygulama, kullanıcının gördüğü değeri her an bilir ve değiştirebilir.

Ayrımın kaynağı Tarayıcı ve Web Platformu kursundaki öznitelik–özellik ayrımıdır: alanın başlangıç değeri işaretlemede bildirilen özniteliktir, o anki değeri ise ögenin özelliğidir. Denetimsiz alan ikisinin ayrı yaşamasına izin verir; denetimli alan her değişiklikten sonra ikisini eşitler.

Denetimlilik neyi mümkün kılar? Değerin yazılırken dönüştürülmesini — büyük harfe çevirme, biçim maskesi uygulama, izin verilmeyen karakteri düşürme. Alanlar arası bağımlılığı — istasyon değişince birim listesinin daralması. Anlık doğrulamayı ve değere bağlı arayüz kararlarını. Bir de kaydedilmemiş taslağın saklanmasını: değer zaten uygulamanın elindedir.

Neyi pahalılaştırır? Her tuş vuruşu bir durum güncellemesi ve bir yeniden çizim üretir. Otuz alanlı bir formda bu maliyet ölçülebilir hâle gelir.

Bildirim Maliyeti

Maliyetin büyüklüğü abonelik tanesine bağlıdır: durum değiştiğinde kim haberdar ediliyor? Aşağıdaki model iki uç yaklaşımı sayıyor — form durumunun tamamına abone olan alanlar ile yalnız kendi değerine abone olan alanlar.

// abonelik-tanesi.mjs — form duzeyinde ve alan duzeyinde bildirim maliyeti
const ALANLAR = ["istasyon", "tarih", "deger", "birim", "not",
                 "gozlemci", "cihaz", "ruzgar", "nem", "basinc", "kar", "aciklama"];

function depo(taneler) {                 // taneler: "form" | "alan"
  const aboneler = new Map();            // yol -> islev listesi
  const cizim = new Map();               // alan -> cagrilma sayisi
  return {
    abone(yol, alan) {
      const anahtar = taneler === "form" ? "*" : yol;
      if (!aboneler.has(anahtar)) aboneler.set(anahtar, []);
      aboneler.get(anahtar).push(alan);
      cizim.set(alan, 0);
    },
    yaz(yol) {
      const anahtar = taneler === "form" ? "*" : yol;
      for (const alan of aboneler.get(anahtar) ?? []) {
        cizim.set(alan, cizim.get(alan) + 1);
      }
    },
    toplam: () => [...cizim.values()].reduce((t, s) => t + s, 0),
  };
}

// Kullanicinin tus dizisi: uc alana toplam 30 degisiklik.
const TUSLAR = [
  ...Array(14).fill("aciklama"),
  ...Array(10).fill("deger"),
  ...Array(6).fill("not"),
];

for (const taneler of ["form", "alan"]) {
  const d = depo(taneler);
  for (const a of ALANLAR) d.abone(a, a);
  for (const yol of TUSLAR) d.yaz(yol);
  console.log(
    `${taneler.padEnd(5)} tanesi:`,
    `${TUSLAR.length} değişiklik →`,
    `${String(d.toplam()).padStart(4)} alan çizimi`);
}

// Denetimsiz alan: deger belgede durur, gonderimde bir kez okunur.
const d = depo("alan");
for (const a of ALANLAR) d.abone(a, a);
console.log("denetimsiz  :", `${TUSLAR.length} değişiklik →`,
            `${String(d.toplam()).padStart(4)} alan çizimi`);
form  tanesi: 30 değişiklik →  360 alan çizimi
alan  tanesi: 30 değişiklik →   30 alan çizimi
denetimsiz  : 30 değişiklik →    0 alan çizimi

Sayılar oranı gösteriyor: form tanesinde her değişiklik on iki alanı çizdiriyor, alan tanesinde yalnız değişeni. On iki katlık fark, alan sayısıyla doğru orantılı büyür.

Buradan çıkan kural, “denetimli alan pahalıdır” değil, **“denetimli alanı form tanesinde kurmak pahalıdır”**dır. Değer uygulamanın durumunda tutulsa bile abonelik alan bazında kurulursa maliyet tek alanla sınırlı kalır. Çoğu durumda bu, form durumunu tek bir nesne olarak paylaşmak yerine alan başına ayrı bir okuma yolu sunmakla sağlanır.

Üçüncü satır bir sınır durumudur: değer belgede kaldığında hiçbir çizim olmaz. Bunun bedeli, uygulamanın kullanıcının yazdığını gönderim anına kadar bilmemesidir. Karma bir düzen de mümkündür ve yaygındır: alanların çoğu denetimsiz, yalnız birbirine bağımlı olan ya da yazılırken dönüştürülen alanlar denetimli.

Form Durumu Değerlerden İbaret Değildir

Değerlerin nerede durduğu kararı verildikten sonra da form, ekranın ihtiyaç duyduğu birkaç bilgi daha taşır. Hata iletisinin ne zaman gösterileceği, gönder düğmesinin ne zaman kapatılacağı, sayfadan ayrılırken uyarı çıkıp çıkmayacağı bu bilgilere bağlıdır.

// form-durumu.mjs — olcum giris formunun durum indirgeyicisi
const BASLANGIC = {
  istasyon: "KY-01", tarih: "2026-01-14", deger: "", birim: "C", not: "",
};

const yeniDurum = () => ({
  degerler: { ...BASLANGIC },
  dokunulan: new Set(),
  gonderiliyor: false,
  gonderimSayisi: 0,
});

const kirliAlanlar = (d) =>
  Object.keys(BASLANGIC).filter((a) => d.degerler[a] !== BASLANGIC[a]);

function indirge(d, [olay, alan, deger]) {
  switch (olay) {
    case "degisti":
      return { ...d, degerler: { ...d.degerler, [alan]: deger } };
    case "dokunuldu":
      return { ...d, dokunulan: new Set([...d.dokunulan, alan]) };
    case "gonderimBasladi":
      return { ...d, gonderiliyor: true, gonderimSayisi: d.gonderimSayisi + 1,
               dokunulan: new Set(Object.keys(BASLANGIC)) };
    case "gonderimBitti":
      return { ...d, gonderiliyor: false };
    case "kabulEdildi":                       // sunucu kaydi aldi: yeni taban
      return { ...yeniDurum(), gonderimSayisi: d.gonderimSayisi };
    case "sifirla":
      return { ...yeniDurum(), gonderimSayisi: d.gonderimSayisi };
    default:
      throw new Error(`bilinmeyen olay: ${olay}`);
  }
}

const OLAYLAR = [
  ["degisti", "deger", "-"],
  ["degisti", "deger", "-4"],
  ["degisti", "deger", "-4.2"],
  ["dokunuldu", "deger"],
  ["degisti", "not", "sabah okuması"],
  ["gonderimBasladi"],
  ["gonderimBitti"],
  ["kabulEdildi"],
];

let d = yeniDurum();
const yaz = (etiket) => console.log(
  etiket.padEnd(26),
  `deger=${JSON.stringify(d.degerler.deger).padEnd(7)}`,
  `kirli=[${kirliAlanlar(d).join(",")}]`.padEnd(18),
  `dokunulan=${d.dokunulan.size}`.padEnd(13),
  `gönderiliyor=${d.gonderiliyor}`.padEnd(19),
  `gönderim=${d.gonderimSayisi}`);

yaz("başlangıç");
for (const olay of OLAYLAR) {
  d = indirge(d, olay);
  yaz(olay.filter(Boolean).join(" "));
}

// --- Ayrilmadan uyari karari ------------------------------------------------
console.log("");
for (const senaryo of [
  ["boş form", yeniDurum()],
  ["yarım doldurulmuş", indirge(yeniDurum(), ["degisti", "deger", "-4.2"])],
  ["gönderim sonrası", d],
]) {
  const [ad, durum] = senaryo;
  const kirli = kirliAlanlar(durum);
  console.log(`${ad.padEnd(20)} kirli=${kirli.length} → ayrılma uyarısı: ` +
              (kirli.length > 0 ? "gösterilir" : "gösterilmez"));
}
başlangıç                  deger=""      kirli=[]           dokunulan=0   gönderiliyor=false  gönderim=0
degisti deger -            deger="-"     kirli=[deger]      dokunulan=0   gönderiliyor=false  gönderim=0
degisti deger -4           deger="-4"    kirli=[deger]      dokunulan=0   gönderiliyor=false  gönderim=0
degisti deger -4.2         deger="-4.2"  kirli=[deger]      dokunulan=0   gönderiliyor=false  gönderim=0
dokunuldu deger            deger="-4.2"  kirli=[deger]      dokunulan=1   gönderiliyor=false  gönderim=0
degisti not sabah okuması  deger="-4.2"  kirli=[deger,not]  dokunulan=1   gönderiliyor=false  gönderim=0
gonderimBasladi            deger="-4.2"  kirli=[deger,not]  dokunulan=5   gönderiliyor=true   gönderim=1
gonderimBitti              deger="-4.2"  kirli=[deger,not]  dokunulan=5   gönderiliyor=false  gönderim=1
kabulEdildi                deger=""      kirli=[]           dokunulan=0   gönderiliyor=false  gönderim=1

boş form             kirli=0 → ayrılma uyarısı: gösterilmez
yarım doldurulmuş    kirli=1 → ayrılma uyarısı: gösterilir
gönderim sonrası     kirli=0 → ayrılma uyarısı: gösterilmez

Dört ayrı bilgi görünüyor ve dördü farklı soruyu yanıtlıyor.

Değer, alanın içeriğidir. Dokunulma, kullanıcının o alanı terk edip etmediğidir; Form Olayları dersindeki hata gösterme anı kuralı bu bilgiye dayanır — ilk üç satırda değer değişti ama dokunulan sayısı sıfır kaldı, yani kullanıcı hâlâ yazıyor ve ona hata göstermek erken olur.

Kirlilik, değerin başlangıçtan farklı olmasıdır. Bu, ayrı bir bayrak olarak tutulmaz; başlangıç değerleriyle karşılaştırılarak hesaplanır. Bayrak olarak tutulsaydı, kullanıcı bir alanı değiştirip geri eski değerine döndürdüğünde form yanlışlıkla kirli kalırdı.

Gönderiliyor ve gönderim sayısı ayrı bilgilerdir. Birincisi düğmenin kapalı tutulmasını sağlar; ikincisi, kullanıcının bir kez gönderim denediğini kalıcı olarak işaretler ve bundan sonra hataların dokunulma beklenmeden gösterilmesini sağlar. Altıncı satırda gönderim başlarken bütün alanların dokunulmuş sayıldığı görülüyor — bu tek satır, “gönder’e basınca tüm hatalar görünsün” davranışını kurar.

Başlangıç, Kirlilik ve Sıfırlama

Başlangıç değerleri form durumunun bir parçasıdır, kodun içine gömülü bir sabit değil. Bunun iki nedeni vardır.

Birincisi kirliliğin hesaplanabilmesi. Karşılaştırma taban değeri yoksa kirlilik bilinemez. Çıktının son bloğu bu hesabın nereye bağlandığını gösteriyor: kirli alanı olan bir formdan ayrılmaya çalışan kullanıcıya uyarı gösterilir, temiz formda gösterilmez. Uyarıyı her koşulda göstermek — kullanıcı hiçbir şey yazmamışken bile — arayüzü rahatsız edici kılar ve zamanla göz ardı edilir.

İkincisi, başlangıç değerlerinin değişebilmesi. Var olan bir ölçümü düzenleyen form, başlangıç değerlerini sunucudan alır. Kayıt yüklendiğinde form durumunun yeni tabana göre kurulması gerekir; aksi hâlde yüklenen değerler kirli görünür ve boş bir formda “kaydedilmemiş değişiklik” uyarısı çıkar.

Üç ayrı işlem sıklıkla karıştırılır. Sıfırlama, değerleri başlangıca döndürür. Temizleme, değerleri boşaltır — bu başlangıca dönmek değildir; düzenleme formunda ikisi çok farklı sonuç verir. Yeni taban belirleme, o anki değerleri yeni başlangıç kabul eder; sunucu kaydı kabul ettikten sonra yapılan işlem budur. Çıktıda kabulEdildi olayı bunu yapıyor: form temizlendi ve kirlilik sıfırlandı, ama gönderim sayacı korundu.

Gönderim Anı

Gönderim, form durumunun en kırılgan olduğu andır ve üç kural gerektirir.

Çift gönderim engellenir. Gönderiliyor bayrağı doğruyken yeni bir gönderim başlatılmaz. Düğmeyi görsel olarak devre dışı bırakmak yeterli değildir; klavyeyle tetiklenen gönderim ya da hızlı çift tıklama düğmenin durumundan önce gelebilir, bu yüzden engel durumda kurulur. Bu, Yeniden Deneme ve Geri Çekilme dersindeki tekillik anahtarının form tarafındaki karşılığıdır — engel istemcide, güvence sunucudadır.

Sunucudan gelen alan hataları forma bağlanır. REST İstemcisi dersinde 422 yanıtının gövdesi ayrinti alanında taşınıyordu; o gövdedeki alan adları form alanlarıyla eşleşir ve hatalar ilgili alanların yanına yerleşir. Eşleşmeyen bir alan adı gelirse hata form düzeyinde gösterilir, yutulmaz.

Başarısız gönderimde değerler korunur. Kullanıcının doldurduğu formu sunucu reddetti diye boşaltmak, işi baştan yaptırmaktır. Değerler yerinde kalır, yalnız hatalar eklenir.

Gönderimin kullanıcı algısındaki süresi ayrı bir konudur. Kaydın sunucuda kabul edileceğini varsayıp ekranı hemen güncellemek — ve hata gelirse geri almak — Durum Yönetimi konusundaki iyimser güncellemenin form tarafındaki uygulamasıdır. Yarım kalan formun taslak olarak saklanması ise Kalıcı Durum dersindeki depolama kararına dayanır; saklanan taslağın hangi başlangıç değerleriyle karşılaştırılacağı, yukarıdaki taban kuralıyla belirlenir.

Özet

  • Alan değeri ya belgede tutulur (denetimsiz) ya uygulamanın durumunda (denetimli); ayrımın kaynağı öznitelik ile özelliğin ayrı yaşayıp yaşamamasıdır.
  • Denetimlilik yazılırken dönüştürme, alanlar arası bağımlılık ve anlık doğrulamayı mümkün kılar; bedeli her değişiklikte üretilen bildirimdir.
  • Bildirim maliyetini belirleyen şey denetimlilik değil abonelik tanesidir; alan bazında abonelik on iki alanlı bir formda çizim sayısını on ikide bire indirir.
  • Form durumu değerlerin yanında dokunulma, kirlilik, gönderiliyor bayrağı ve gönderim sayısını taşır; her biri farklı bir arayüz kararını besler.
  • Kirlilik bayrak olarak tutulmaz, başlangıç değerleriyle karşılaştırılarak hesaplanır; sıfırlama, temizleme ve yeni taban belirleme üç ayrı işlemdir.
  • Gönderimde çift gönderim durum düzeyinde engellenir, sunucudan gelen alan hataları ilgili alanlara bağlanır ve başarısızlıkta kullanıcının girdiği değerler korunur.

Sonraki Adım

Bu ders formun değerlerini tuttu ama değerlerin kabul edilebilir olup olmadığına hiç bakmadı. Ölçüm değeri sayı olmalı, sıcaklık için makul bir aralıkta durmalı, tarih gelecekte olmamalı. Bu kuralların iki yerde bilinmesi gerekir: kullanıcıya anında geri bildirim vermek için istemcide, isteğin sayfa hiç kullanılmadan da kurulabildiğini bilerek sunucuda. Aynı kuralı iki yere ayrı ayrı yazmak, ikisinin zamanla ayrışması demektir. Sonraki ders kuralları tek bir bildirimde toplar ve aynı bildirimin iki tarafta aynı sonucu verdiğini gösterir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat