May.27

HTML5 API’leri ile Çalışmak – Ders 7: WebSQL API – HTML5 + jQuery ile Web SQL API Kullanarak Fonksiyonel Not Defteri Programlama

Projemizin yapısına bakacak olursak; ilk girişte eğer veritabanı oluşturulmamışsa kullanıcıya veritabanı oluşturulsun mu şeklinde bir mesaj çıkacak. Kullanıcı eğer onaylarsa, veritabanı oluşturacağız. Bu aşamaya kadar Web SQL’de veritabanının var olup olmadığını ve eğer yoksa veritabanı oluşturmayı öğreneceğiz. Sonrasında da veritabanında kayıt var mı yok mu diye kontrol edeceğiz. Eğer kayıt yoksa kayıt yok diye bir mesaj çıkaracağız. Bu aşamada da veritabanındaki tablomuzdaki kayıt sayısını okumayı öğrenmiş olacağız. Ardından basit bir form ile not defterinin başlığını, içeriğini alacağız. Tarihi de kendimiz otomatik olarak ekleyeceğiz. Aldığımız kayıtları da veritabanına gireceğiz. Bu aşamada da veritabanına kayıt yapmayı öğrenmiş olacağız. Her kayıt işleminden sonra da veritabanındaki kayıtları tekrar çekip bir listede göstereceğiz. Bu aşamada da veritabanından kayıtları almayı ve parse etmeyi göreceğiz. Her bir kaydın yanında güncelle ve sil butonları da olacak. Bunlarla da veritabanındaki ilgili kaydı güncellemeyi ve silmeyi de göreceğiz. Bir de bir buton ile kullanıcının veritabanını komple kaldırmasını sağlayacağız ve böylelikle veritabanını da kaldırmayı öğreneceğiz. Yani baya bir şey öğrenmiş olacağız bu uygulamayla…

Bu projemizde CSS’e biraz fazlaca ihtiyacımız olacak, konudan da çok fazla çıkmamak adına örneğimizde CSS işlerini halletmesi için Bootstrap kullanacağız. Böylece dikkatimizi JavaScript ve HTML5’e verebiliriz. DOM manipülasyonu için de jQuery’den faydalanacağız.

web-sql.html

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1, ;
shrink-to-fit=no">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css">
   </head>
   <body style="margin:10px">
      <div class="container">
         <div class="row justify-content-center">
            <div class="col-sm-12" >
               <div id="tabloKontrol" class="text-center">
                  <h5 class="text-danger">Tablo oluşturulmamış, oluşturmak ister misin?</h5>
                  <button id="tabloOlustur" type="button" class="btn btn-primary ">Evet</button>
               </div>
               <div id="kayitDefteri">
                  <div class="row">
                     <div class="col-12 text-right">
                        <button id="tabloSil" type="button" class="btn btn-danger ">Tabloyu Sil</button>
                     </div>
                  </div>
                  <br>
                  <div class="row " style="max-height: 80vh !important; overflow-y:scroll">
                     <table class="table table-hover table-responsive">
                        <thead>
                           <tr>
                              <th>#</th>
                              <th>Not Başlığı</th>
                              <th>Not İçeriği</th>
                              <th>Tarih</th>
                              <th></th>
                           </tr>
                        </thead>
                        <tbody id="tablo">
                        </tbody>
                     </table>
                  </div>
                  <br>
                  <div class="row">
                    <div class="col-12">
                        <div class="card">
                            <div class="card-body">
                               <form>
                                  <div class="row">
                                     <div class="col-5">
                                        <input type="text" id="notBasligi" class="form-control" placeholder="Not başlığı">
                                     </div>
                                     <div class="col-5">
                                        <input type="text" id="notMetini" class="form-control" placeholder="Not metini">
                                     </div>
                                     <div class="col-2">
                                        <button type="button" id="notKaydet" class="btn btn-primary">Yeni Kayıt Ekle</button>
                                     </div>
                                  </div>
                               </form>
                            </div>
                         </div>
                    </div>
                  </div>
               </div>
            </div>
         </div>
      </div>
      <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" ></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" > </script>
      <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js"> </script>
      <script src="script.js"></script>
   </body>
</html>

Evet, kodlarımız biraz uzun, JavaScript kodlarımız daha da uzun. Ama en basitinden bir veritabanı yönetimi (CRUD = Create, Read, Update, Delete) bile bolca ve akıllıca kod yazmamızı gerektiriyor.

Şimdi yaptığımız şeyi bir inceleyelim…

İlk olarak Bootstrap 4 ile bir sayfa syntax’ı oluşturduk. View port’u belirledik ve ’nin en sonunda gerekli olan JavaScript dosyalarını CDN’den ekledik. Hemen peşine de kendi script.js dosyamızı ekledik.

Sayfa ilk yüklendiğinde JavaScript tarafında, Web SQL’de notlar isimli bir tablomuz var mı diye kontrol ediliyor. Eğer yoksa id’si tabloKontrol olan div görünür yapılıyor, eğer varsa da görünmez yapılıyor. Bununla birlikte eş zamanlı olarak da id ismi kayitDefteri olan div de tam tersi görünür veya görünmez yapılıyor. Yani tablo yoksa “Tablo oluşturulmamış, oluşturmak ister misin?” diye bir mesaj ve id’si tabloOlustur olan Evet butonu görünüyor. Her bir element için bir id tanımladık. Bu id’ler ile jQuery tarafında gerekli kontrolleri yapmak için elementlere erişebileceğiz.

Eğer bir tablomuz yoksa Evet butonuna tıklanması ile local_veritabanı isimli bir veritabanı ve içinde de notlar isminde bir tablo oluşturuluyor. Ardından eğer kayıt varsa, bu kayıtlar jQuery’de dinamik olarak bir tablo şeklinde oluşturup listeleniyor. Listede kayıt id’si, başlık, metin, tarihi ve bu kaydı güncellemek ve silmek için gerekli olan butonlar listelenecek.

Listenin alt kısmında da yeni kayıt girilebilmesi için bir Card component’i içinde yeni kayıt için başlık ve metni alıp kaydeden bir buton olacak.

Ek olarak, ekranın sağ üst köşesinde de tabloyu silecek bir buton oluşturacağız.

İsterseniz öncelikle projenin bitmiş haline bir bakalım, böylece kafamızda daha netleşir.

Gördüğünüz üzere 2 adet kayıt girilmiş. Google Developer Tools ile de Inspect edip Application sekmesinden sol menüdeki Web SQL kısmında da local_veritabani isimli veritabanımızı, içinde de yer alan notlar tablosunu görebiliyoruz. Ekranın orta kısmında da 2 adet kaydımızın tablo yapısı yer alıyor. Sütun başlıklarımız id, baslik, metin ve tarih (milisecons olarak kaydedilmeli) görünüyor. (ilk sütun id’yi Web SQL kendi koyuyor, o sadece sıra numarasını belirtir.) Eklediğimiz her kayıt burada yer alacak.

Özellikle tabloda yer alan şu koda dikkat edin.

<tbody id="tablo">
</tbody>

Burada, id’si tablo olan

içine, jQuery ile tr-td…td-tr ile veritabanındaki içerikleri ve bu içerikleri yönetecek olan butonları basacağız. Yine kodlarda yer alan kalın harflerle yazılmış olan buton isimlerine ve id isimlerine dikkat edin, bu referanslara göre JavaScript kodlarımızı yazmaya başlayalım.

script.js

$(document).ready(function() {

    // DIV'LERİ GİZLE
    $("#tabloKontrol").hide();
    $("#kayitDefteri").hide();

    // WEBSQL DESTEKLİYOR MU DİYE BAK
    if (window.openDatabase) {
        // VERİTABANINI OLUŞTUR
        var veritabani = openDatabase('local_veritabani', '1.0', 'Web SQL Veritabanı', 10 * 1024 * 1024);  
        console.log("local_veritabani isimli veritabanı 1.0 versiyonu ile 10MB olacak şekilde oluşturuldu veya zaten varsa yeniden oluşturulmadı!");
        // KAYITLARI ÇEK VE LİSTELE
        kayitlariOku();
    }else{
         alert("Maalesef tarayıcınızda Web SQL desteği bulunmamaktadır.")
    }

    // EVET BUTONUNA TIKLANINCA TABLOYU OLUŞTUR
    $("#tabloOlustur").click(function() {
            veritabani.transaction(function(tx) {
            tx.executeSql('CREATE TABLE notlar(id INTEGER PRIMARY KEY, baslik VARCHAR(128), metin VARCHAR(512), tarih DATETIME )', [], function(islem, sonuc) {
                console.log(sonuc);
                console.log('Notlar tablosu oluşturuldu.');
                // DIV'LERIN GÖRÜNÜRLÜĞÜNÜ DÜZENLE
                $("#tabloKontrol").hide();
                $("#kayitDefteri").show();

            }, function(islem, hata) {
                console.log("Hata: ", hata);
            });
        }, islemHatali, islemBasarili);
    });
 
    // TABLO SİL BUTONUNA TIKLANINCA TABLOYU SİL
    $("#tabloSil").click(function() {
        veritabani.transaction(function(tx) {
            tx.executeSql('DROP TABLE notlar', [], function(islem, sonuc) {
                console.log(sonuc);
                console.log('Notlar tablosu silindi.');
                // DIV'LERIN GÖRÜNÜRLÜĞÜNÜ DÜZENLE
                $("#tabloKontrol").show();
                $("#kayitDefteri").hide();
                // TEKRAR KAYITLARI OKU, VERİ OLMADIĞI İÇİN TABLO SIFIRLANACAK
                kayitlariOku ();
            }, function(islem, hata) {
                console.log("Hata: ", hata);
            });
        }, islemHatali, islemBasarili);
    });

    // KAYDET BUTONUNA TIKLANINCA YENİ BİR KAYIT GİRİLİYOR
    $("#notKaydet").click(function() {
        veritabani.transaction(function(tx) {

            // JQUERY İLE INPUT DEĞERLERİ ALINIYOR
            var baslik = $("#notBasligi").val();
            var metin = $("#notMetini").val();

            // EĞER İKİ DEĞER DE GİRİLMİŞSE...
            if(baslik && metin){
                // VERİLER GİRİLİYOR
                tx.executeSql('INSERT INTO notlar (baslik, metin, tarih) VALUES (?,?,?)', [baslik, metin, new Date().getTime()], function(islem, sonuc) {
                    console.log(sonuc);
                    console.log('Notlar tablosu silindi.');
                    // INPUT'LAR SIFIRLANIYOR
                    $("#notBasligi").val("");
                    $("#notMetini").val("");
                    // KAYITLAR TEKRAR OKUNARAK TABLOYA AKTARILIYOR
                    kayitlariOku();
                }, function(islem, hata) {
                    console.log("Hata: ", hata);
                });
            }
        }, islemHatali, islemBasarili);
    });

    // KAYITLARI OKUYAN VE TABLOYU OLUŞTURAN FONKSİYON
    function kayitlariOku (){
        veritabani.transaction(function(tx) {
            tx.executeSql('SELECT * FROM notlar', [], function(islem, sonuc) {
                console.log("Kayıtlar listeleniyor:")
                console.log(sonuc.rows);
                // KAYIT VARSA DIV'LER DÜZENLENİYOR
                $("#tabloKontrol").hide();
                $("#kayitDefteri").show();
                // TABLO SIFIRLANIYOR, SIFIRLANMAZSA PEŞİNE EKLER
                $("#tablo").empty();
                // EACH METODU İLE DÖNGÜYE GİRİLİYOR
                // KAYITLAR sonuc PARAMETRESİNİN rows DEĞERİNDE GELİYOR
                // CONSOLE.LOG()'LARI İNCELEYİN
                // SIRADAKİ HER BİR DEĞER value PARAMETRESİNE ATANIYOR
                jQuery.each(sonuc.rows, function(index, value) {
                    // tablo ID'LI <tbody>'E TEK TEK EKLEME YAPILIYOR.
                     $("#tablo").append(
                        "<tr>" +
                             "<td>"+value.id+"</td>"+
                             "<td><input type='text' id='baslik"+value.id+"' value='"+value.baslik+"' /></td>"+
                             "<td><textarea id='metin"+value.id+"' >"+value.metin+"</textarea></td>"+
                             "<td>"+ new Date(value.tarih).toLocaleString()+"</td>"+
                             "<td><button type='button' data-index='"+value.id+"' class='guncelle btn btn-light'>Güncelle</button>&nbsp&nbsp<button type='button' data-index='"+value.id+"' class='sil btn btn-light' >Sil</button></td>"+
                        "</tr>");
/* <TR> İLE YENİ SATIR TANIMLANIYOR
<TD> İLE SÜTUN TANIMLANIYOR.
1. OLARAK BAŞLIK İÇERİĞİ <input> OLUŞTURULARAK ATANIYOR
2. OLARAK METİN İÇERİĞİ <textarea> OLUŞTURULARAK ATANIYOR
3. OLARAK TARİH İÇERİĞİ OKUNABİLİR TARİH FORMATINA ÇEVRİLEREK DÜZ METİN OLARAK ATANIYOR
4. OLARAK DÜZENLE VE SİL BUTONLARI OLUŞTURULUYOR
HER BİRİ İÇİN TANIMLANAN CLASS VE ID METOTLARINI İYİCE İNCELEYİN
ID TANIMLAMALARI DİNAMİK OLARAK INDEX NUMARASINA GÖRE YAPILIYOR
BÖYLECE HANGİSİNİN SİLİNİP GÜNCELLENECEĞİNİ INDEX'TEN ANLAYACAĞIZ */
                 });
            }, function(islem, hata) {
                console.log("Tabo Yok")
                console.log("Hata: ", hata);
                $("#tabloKontrol").show();
                $("#kayitDefteri").hide();
            });
        });

    }

    /* GÜNCELLE BUTONU DİNAMİK OLDUĞUNDAN FARKLI BİR METOT İLE TRIGGER EDİYORUZ */
    // CLASS İSMİ guncelle OLAN BUTONA TIKLANDIĞINDA
    $(document).on('click','.guncelle',function(){
        // index DEĞERİ data-index'TEN GELİYOR
        // baslik VE metin DEĞELERİ OKUNUYOR.
        // BU OKUMA İŞLEMİNDE index PARAMETRESİNE DİKKAT EDİN.
        // index PARAMETRESİ de this İLE GELİYOR
        // this DEMEK, TIKLANAN ELEMENTİ TANIMLAR.
        var index = $(this).attr('data-index');
        var baslik = $("#baslik"+index).val();
        var metin = $("#metin"+index).val();
        // EĞER TABLODAKİ INPUT'LARDA VERİLER GİRİLMİŞSE
        // TABLIDA id SUTÜNU İLE index DEĞERİ EŞİT OLAN KAYIT GÜNCELLENİYOR
        if(baslik && metin){
            veritabani.transaction(function(tx) {
                tx.executeSql('UPDATE notlar SET baslik=?, metin=?, tarih=? WHERE id=?', [baslik, metin, new Date().getTime(), index], function(islem, sonuc) {
                    console.log(sonuc);
                    console.log('Kayıt güncellendi.');
                    // GÜNCELLEME SONRASI KAYITLAR TEKRAR OKUNUYOR
                    kayitlariOku();
                }, function(islem, hata) {
                    console.log("Hata: ", hata);
                });
            });
        }
    });

    // CLASS İSMİ sil OLAN BUTONA TIKLANDIĞINDA
    $(document).on('click','.sil',function(){
        // HANGİ ELEMENT TIKLANMIŞSA ONDAKİ data-index'E GÖRE INDEX OKUNUYOR
        var index = $(this).attr('data-index');
        veritabani.transaction(function(tx) {
            /* OKUNAN index DEĞERİ İLE VERİTABANINDAKİ id DEĞERİ EŞİT OLAN KAYIT SİLİNİYOR */
            tx.executeSql('DELETE FROM notlar WHERE id = ?', [index], function(islem, sonuc) {
                console.log(sonuc);
                console.log('Kayıt silindi.');
                // KAYITLAR TEKRAR GÜNCELLENİYOR
                kayitlariOku();
            }, function(islem, hata) {
                console.log("Hata: ", hata);
            });
        });

    });

/* YUKARIDA GÖRDÜPĞÜNÜZ SORGULARIN BAŞARILI VE BAŞARISIZ DURUMLARINA GÖRE CONSOLE.LOG()'LAR e PARAMETERESİNDEKİ DEĞERLERE GÖRE BASILIYOR */
    function islemHatali(e) {
        console.log("İşlem hatası ! Kod:" + e.code + " Mesaj : " + e.message);
    }

    function islemBasarili() {
        console.log("İşlem başarılı bir şekilde gerçekleştirildi!");
    }

});

Kodlarımız baya uzun, ancak gözünüz korkmasın. Öncelikle kodlar arasında yer alan comment (açıklama) satırlarını ve console.log() ile konsola yazdırılan mesajları okuyun, genel olarak kodlar hakkında bilgi edineceksiniz.

Web SQL’de bilmeniz gereken 3 temel kavram var, bunlar;

openDatebase() : Veritabanını oluşturmak için kullanılan metottur. 4 adet parametre alır. Sırasıyla veritabanının ismi (Türkçe ve özel karakterler kullanmayın), versiyon, veritabanının görünen ismi, byte cinsinden boyutu (1024*1024=1MB eder). Oluşturulan bu veritabanı, mutlaka bir değişkene atanmalıdır. Örneğimizde veritabanı isimli değişkene atanmıştır.

var veritabani = openDatabase('local_veritabani', '1.0', 'Web SQL Veritabanı',  10 * 1024 * 1024);  

transaction() : Sorgu anlamına gelir. Veritabanının oluşturulduğu değişkenin metotdudur. Parametre olarak bir fonksiyon alır ve fonksiyonun içinde de bir nesne ile (örneğimizde tx kullandık) sorgu çalıştırılır.

veritabani.transaction(function(tx) {
});

executeSql() : transaction() içinde yer alır. Aldığı fonksiyon parametresi ile (tx) sorgu çalıştırır. Parametre olarak SQL sorgusunu, sorguda kullanılacak değerleri ve sorgunun çalışması ile birlikte çalıştırılacak olan fonksiyon. Web SQL’de sorgular güvenli olarak çalıştırılır. Yani tüm değerler sorgu içine yazılmaz, sorguya parametre olarak gönderilir (SQL konusunda bunu görmemiştik, şimdi görmüş olduk).

tx.executeSql(
‘UPDATE notlar SET baslik=?, metin=?, tarih=? WHERE id=?', 
[ baslik, metin, new Date().getTime(), index ]
, function(islem, sonuc) {
	console.log(sonuc);
}, function(islem, hata) {
console.log("Hata: ", hata);
});

Buradaki sorgumuzda UPDATE ile güncelleme yapılıyor. Dikkat edin, 4 adet ? işareti bulunuyor. Bu soru işaretlerinin yerlerine değerlerin atanması lazım. Atanması gereken değerler de ikinci parametrede bir dizi içinde [] sırası ile giriliyor. Sırasıyla her biri ? işaretinin yerine atanacak. Son olarak fonksiyon parametresi ile de işlem ve sonuç okunabilir. Eğer işlem başarılı olursa, yani sorgu çalışırsa bu fonksiyon içine girer. Eğer sorguda bir sorun olursa, ikinci fonksiyona girer. Bu fonksiyon da aldığı parametre ile (hata) hata durumunu belirtir.

Örneğimizde farklı olarak birkaç jQuery metodu da gördük, bunlardan da kısaca bahsedeyim…

‘SELECT * FROM notlar’ sorgusu ile tüm kayıtları çektiğimizde gelen sonuçları sonuc parametresine aktardık. Bu parametreden de sonuc.rows ile kayıtları çekebildik (Konsoldan inceleyin). Bu kayıtlar bize JSON nesnesi olarak geldi. Aşağıdaki each() metodu ile de parametre olarak verilen JSON nesnesinin değerleri sırası ile okunup value değerine aktarılıyor. Fonksiyonun birinci değeri index’i, ikinci value’yu verir. Bize value lazım.

jQuery.each(sonuc.rows, function(index, value) {
})

value içinde de . operatörü ile her bir sütun değerimizi çekiyoruz. Çektiğimiz değerlerle de String şeklinde tablonun satırını oluşturuyoruz. En kritik nokta ‘ ve “ işaretlerinin dikkatli kullanılması. Burada her bir satırdaki nesnelerin id’lerini ve data-index’lerini dinamik olarak oluşturduk. Mesela;

<td><button type='button' data-index='"+value.id+"' class='guncelle btn btn-light'>Güncelle</button>&nbsp&nbsp<button type='button' data-index='"+value.id+"' class='sil btn btn-light' >Sil</button></td>

Bunun sonucu aşağıdaki gibi bir şey olacaktır. Değişen tek şey index numarası.

<td><button type='button' data-index='1' class='guncelle btn btn-light'>Güncelle</button>&nbsp&nbsp<button type='button' data-index='1' class='sil btn btn-light' >Sil</button></td>"

Oluşturduğumuz bu data-index değerleri her bir satırda artacaktır. Index değerimiz de tablodaki id sütun değerlerinden geliyor, fonksiyondaki index parametresi ile karıştırmayın.

Şimdi biz dinamik olarak satır ve bu satırlar içinde butonlar oluşturdukya, jQuery artık bunu direkt olarak .click() metodu ile yakalayamıyor. Bu nedenle aşağıdaki gibi bir metot kullandık.

$(document).on('click','.guncelle',function(){
});

Burada, doküman içinde (on metodu ile) yer alan .guncelle class isimli nesneye click event’i atanmış. Yani bu metot ile ilk olarak dokümana bakılıyor ve onun içinde nesne aranıyor. Şimdi bizim birden çok .guncelle class isminde butonumuz olduğu için hangi satırdakine tıklandığını anlamamız lazım. Bunun için de aşağıdaki metot ile kaçıncı id numaralı değerlere ait buton tıklanmış öğrenebiliriz.

var index = $(this).attr('data-index');

Bu metot index değişkenine, o tıklanmış olan butondaki data-index değerini atıyor. Her bir satırdaki tüm input’ların ve butonların index değerleri aynıdır. Bu da demek oluyor ki güncellenecek olan başlık ve metin değerlerini bu index numaralarına göre okumak lazım. Zaten o input ve textarea’da bu şekilde id atamalarını yapmıştık.

var index = $(this).attr('data-index');
var baslik = $("#baslik"+index).val();
var metin = $("#metin"+index).val();

Evet, aldığımız index numarasına göre dinamik bir şekilde target’lenecek olan nesneyi de belirtmiş olduk.

“#baslik” + index aslında “#baslik1” anlamına gelir. Yani id’si #baslik1 olan nesneyi hedefle dedik. Aynı şekilde silme butonu için de aynı teknikle yapılması gereken işlemleri uyguladık.

Son olarak bir de tabloyu silecek olan komutumuz vardı, onda da SQL’in DROP TABLE sorgusunu kullandık, yani herhangi bir Web SQL API metodu bulunmamaktadır.

Bu örneği daha iyi anlamak için tek tek butonlara tıklayıp test edin ve konsoldaki sonuçları izleyin.

İçeriği paylaş:
  • facebook
  • twitter

Comments(3)

  1. Ayşe
    275 days ago

    Çok faydalı bir içerik olmuş. Elinize sağlık.

  2. Arda
    281 days ago

    Hocam selamlar, Web Sql sizce nerelerde kullanılmalı?

    • ugurgelisken
      281 days ago

      Merhaba. Genellikle HTML5 ile mobil uygulama geliştirildiğinde lokal verileri saklamak için kullanılıyor.

Leave a comment

Yorum