29 Mayıs 2020 Cuma

Vue.js TEMELLERİ

Pandemi dolu evdekal günlerimde sıkıldıkça yeni bir şeye sarıyorum. JavaScript iskeletler bugünlerde çok popüler. Node Angular React deneyip dururken Vue.js'nin benim programlama mantığıma çok yakın geldiğini gördüm.

Bu yazımda da Vue.js öğrenmeye çalışacağım. Ben böyle yazmadan öğrenemiyorum, gençliğimden beri böyle. Vue.js açık kaynaklı bir JavaScript front-end iskeleti. Eğlenceli bir yapı. Vue , bir MVC uygulamasının görsel tarafını oluşturmak içindir. (Model View Controller - Rails gibi üzerine bir kitap yazılır, ayrı konu). Vue şu sıralar en hızlı yükselen ve en popüler JavaScript kütüphanesi. Benim gönlimde jQuery kütüphanesini salladı. Vue'nin arkasında React'ın arkasındaki Facebook ya da Angular'ın arkasındaki Google gibi koca bir firma yok. Evan You tarafından yazılmış ve arkasında geniş bir açık kaynak topluluğu var.



Kurulum



Vue.js iki şekilde kullanılabiliyor, bir Node projesi içinde ya da statik bir HTML dosyada enjekte olarak. Önce basit yolundan başlayalım HTML dosya içinde script olarak kullanmak. jQuery benzeri JavaScript kütüphaneleri kullananlar bunu nasıl olduğunu bilir. Bir HTML dosya oluşturup içinde Vue.js internet reposundan dosyasını dahil ederek kullanacağız. Sonra uygulama için sayfaya id değeri olan bir <div> elemanı ekledik mi orede Vue.js güzelliklerini görebiliriz.



Statik HTML dosyası


index.html
<!DOCTYPE html>
<html lang="tr">
    <head>
        <meta charset="utf-8" />
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <title>Vue App</title>
    </head>

    <body>
        <div id="app"></div>
    </body>
</html>

Basit bir "Merhaba Dünya" çıktısını Vue.js ile alabilmek için mesaj adında bir değişken içine mesajımızı yazalım ve Vue.js ile sayfada gösterelim. Değişkeni sayfada göstermek için uygulamaya ait id'ye sahip elemanın içinde çift süslü parantez ile çevrili olarak yazmak gerekir. Daha sonra <script> tag içinde data ile DOM arasında bağlantı kurarız. Değişkene verdiğimiz değer çıktıya yazılır.

Şöyle yapılır:

index.html
<!DOCTYPE html>
<html lang="tr">
    <head>
        <meta charset="utf-8" />
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <title>Vue App</title>
    </head>

    <body>
        <div id="app">{{ mesaj }}</div>

        <script>
            const App = new Vue({
                el: "#app",
                data: {
                    mesaj: "Merhaba Vue"
                }
            });
        </script>
    </body>
</html>

Sayfayı tarayıcıda açtığımızda,


Pek de matah bir şey olmadı. Ancak temel uygulama parçalarını gördük, bir de Vue.js sadece JavaScript dosyası. Node, Babel , Webpack falan olmadan da çalışıyor.



Vue CLI


Ama yine de Node avantajlarından yararlanmak isteyebilirsiniz. Bunu yapmanın en kolya yolu Vue CLI kullanmak. Açık hali Vue Command Line Interface (Vue komut satırı arabirimi). Bu yazıyı okuyorsanız demekki Node falan hepsini biliyorsunuzdur. Ben bilmiyordum önce gidip 5-10 dakika okudum geldim. ilk önce Vue CLI kurarak başlayacağız. Konsolda şunları girelim.
# npm ile kurulum
npm i -g @vue/cli @vue/cli-service-global

# yarn ile kurulum
yarn global add @vue/cli @vue/cli-service-global

Şimdi Vue CLI kurmayı bitirdik artık vue komutu konsolda çalışacaktır. Şimdi deneme yapacağımız bir klasör oluşturalım ve içinde konsol ya da powershell açıp vue create yazarak bir Vue uygulaması üretelim.
vue create vue-app

Default ya da manual yolla devam etmek isteğinizi soracaktır. Default seçerek devam edelim. Bir sürü bir şeyler yükleyi falan hatasız işlemi bitirirse içinde uygulamamız olan bir vue-app klasörümüz olacaktır. Uygulama klasörüne geçelim ve çalıştıralım.
cd vue-app
npm run serve
# veya
yarn serve

Server çalışmaya başladıktan sonrahttp://localhost:8080/ adresini tarayıcıda açarsak,



Buraya kadar sağ sağlim geldiyseniz Vue geliştirme ortamınız hazır demektir. Favori kod editörünüze Vue.cs için eklentisi varsa yükleyin.



Vue Geliştirici Araçları


Araç çantanızda bulunması gereken bir şey daha var, Vue geliştirici araçları. Bu Vue geliştirirken kullanabileceğimiz bir tarayıcı eklentisi. 

Ben Opera kullanıyorum ve Install Chrome Extensions diye bir eklenti sayesinde Chrome eklentilerini yükleyebiliyorum. 





Başlayalım


Tebrikler her şey hazır. Fırından yeni çıkmış bir Vue uygulamamız var. Proje dosyaları içinde public adında bir klasör var. İçinde index.html dosyası var. src klasörünün altındaki main.js dosyası her şeyin başladığı yer. İki tane .vue dosyası var, HelloWorld.vue ve App.vue .Bunla Vue komponentlerini temsil ediyorlar. 


Giriş Noktası


main.js içinde Vue.js'yi getiriyoruz ve App komponentimizi index.html içindeki app div'inde yayınlıyoruz. 

src/main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
 


Vue Dosyasının Anatomisi


Projemize ekleyeceğimiz her .vue dosyası şu 3 yapıyı içermeli. 
  • <template>
  • <script>
  • <style>
ve şöyle görünür

örnek.vue
<template></template>

<script>
  export default {
    name: 'komponent-adı',
  }
</script>

<style scoped></style>

Bu ilginç gelebilir. Herkes HTML , CSS ve JavaScript'i birbirinden ayırmak gerektiğini söylerken, Vue hepsini bir arada tavsiye ediyor. Ama bir de şöyle düşünelim , bu bir Vue komponent dosyası ve görselin bir kısmını temsil edecek. Kendine ait scripti ve sadece kendi içinde geçerli stili olsa daha iyi. Uygulama geneli kullanılacak stil ve scriptleri hala ayrı tutabiliriz. Burada komponent odaklı olarak düşünmek lazım. Bu sayede kodumuza çok daha iyi hakim olabiliriz.

Komponente ait data ve lojik script tag içinde yer alır. Ama burada sadece name değeri zorunludur. style tag'i sadece CSS içeriyor ve sadece bu komponent için geçerli olacağı scoped ile belirleniyor.

Şimdi bu uygulamayı gerçekten yapmaya başlayalım.

Bu yazının amacı işlevsellik. Stiller üzerinde pek durmayacağız. Bu yüzden Primitive UI kullanıp ortalama bir stil elde edeceğiz. index.html dosyasın stil bağlantısını ekleyelim.
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css" />




Bir Komponent Oluşturmak


EmployeeTable.vue adında bir dosyada çalışanların bilgilerini göstermek amaçlı bir Vue komponenti tanımlayalım. Komponent olduğu için dosyayı src/components klasörüne koymalıyız. Bu komponentde bir tablo görselinde çalışanların listesini vereceğiz.

src/components/EmployeeTable.vue
<template>
    <div id="employee-table">

        <table>
            <thead>
                <tr>
                    <th>Çalışanın Adı</th>
                    <th>Çalışanın Emaili</th>
                </tr>
            </thead>
            
            <tbody>
                <tr>
                    <td>Ümit Kayacık</td>
                    <td>umit@xmail.com</td>
                </tr>
                <tr>
                    <td>Duygu Kumsal</td>
                    <td>kumsal@xmail.com</td>
                </tr>
                <tr>
                    <td>Zeynep Bıçak</td>
                    <td>zbicak@xmail.com</td>
                </tr>
            </tbody>
        </table>

    </div>
</template>

<script>
    export default {
        name: "employee-table"
    };
</script>

<style scooped></style>

Vue teamüllerinde komponent dosyalarına isim verirken ve onları başka dosyalar içine çağırırken verilen isimlerde CamelCase yazım şekli kullanılır. Fakat template olarak kullanırken kebab-case yazım şekli kullanılır. Yukarıdaki örneğimize bakarsak dosya ismi EmployeeTable.vue CamelCase yazılmış ama template id'si employee-table kebab-case yazılmış. Yoğurdu bu arkadaşlar yapmış, onlar gibi yemek daha iyi olur.

EmployeeTable kalıbını employee-table adı ile export ettik. Şimdi onu App.vue dosyası içinde import edeceğiz (bu işlemlere sırasıyla ihraç ve ithal de diyebiliriz, size kalmış). src klasörünü ifade etmek için @ sembolünü kullanabiliriz. App.vue dosyası komponentlerini components özelliği içinde tanır. Tüm import edilen komponentler buraya eklenmelidir. Dosyaya bir-iki tane de stil ekledik.

src/App.vue
<template>
  <div id="app" class="small-container">
    <h1>Çalışanlar</h1>

    <employee-table />
  </div>
</template>

<script>
import EmployeeTable from '@/components/EmployeeTable.vue'

export default {
  name: 'app',
  components: {
    EmployeeTable
  }
};
</script>

<style>
  button {
    background: #009435;
    border: 1px solid #009435;
  }

  .small-container {
    max-width: 680px;
  }
</style>

Sayfayı yenilersek şunu görürüz.



Bu yakışıklı tablo stili o yukarıda sayfaya dahil ettiğimiz primitive-ui CSS dosyasından geliyor.

Tabloyu sabit bilgilerle doldurup nasıl göründüğünü test ettik, sıra geldi bilgileri veri olarak saklayıp dinamik bir şekilde gösterilmesini sağlamaya. App.vue dosyamız da aslında bir komponent ve main.js dosyası içinde bu komponent kullanılarak index.html dosyasına şekil veriliyor. Komponent içinde başka komponentler şeklinde büyüte büyüte koca bir uygulama oluyor zamanla.

App.vue komponentimizin şu anda name ve components özelliklerini gördük. Veri saklamak için kullanılan da data() metodu. data() metodunu kullanarak employees adında bir array tanımlayalım ve içine çalışan bilgilerimizi yerleştirelim. Çalışan bilgilerini veri haline getirirken her bir kaydın tek olmasını sağlamak amacıyla aynı veri tabanı tablolarındaki gibi bir "id" alanı da ekliyoruz.

src/App.vue
...
export default {
  name: 'app',
  components: {
    EmployeeTable
  },
  data() {
    return {
      employees: [
        {
          id: 1,
          name: "Ümit Kayacık",
          email: "umit@xmail.com"
        },
        {
          id: 2,
          name: "Duygu Kumsal",
          email: "kumsal@xmail.com"
        },
        {
          id: 3,
          name: "Zeynep Bıçak",
          email: "zbicak@xmail.com"
        }
      ]
    }
  }
};
...

Genel olarak yapıya bakarsak JSON nesnesi tanımlar gibi gidiyor herşey. Şimdi App.vue içinde bu veriye sahibiz. Ama bu veriyi EmployeeTable.vue kalıbına aktaracağız ki içinde gösterebilelim. Burada data değerini kalıba bir adlandırılmış özellik olarak aktarırız. Bunu Vue bağlaması ile yaparken v-bind:employees şeklinde ya da kısaltılmış hali :employees şeklinde özelliğe bir isim veririz. Bu özelliğe employees array'i mizi atayarak alt kalıp dosyasına gönderiyoruz.

Şöyle açıklayalım App.vue dosyasında yukarıda <employee-table /> elemanını şu hale getiriyoruz

<employee-table :employees="employees" />

<!-- bu da aynı şey -->
<employee-table v-bind:employees="employees" />

Kafaya takılacak bir soru, neden aşağı kalıba gönderirken array ile aynı isimde bir adlandırılmış özellik kullanıyoruz? Aynı şeyi anlatan bir sürü değişik isim olmasın diye. Sonra komponenti eklerken başka isim, komponentin içinde başka isim, hangisi hangisinde kullanılıyordu vs. olacak. Ama bu şekilde aynı isimlendirmeler için bir kısa yol olsa iyi olur (belki de var, bilmiyorum).

Yukarıdan veriyi kalıba gönderdik. Kalıbın içinde veriyi kullanabilmek için de props özelliği içinde employees isminde bir array geliyor diye anlatırız.

src/components/EmployeeTable.vue
    export default {
        name: "employee-table",
        props: {
            employees: Array
        }
    };




Döngüler


Şimdi, veriyi aşağıya aldık. Array içindeki elemanlar üzerinden bir döngü yaparak görselde yayınlayacağız. Bu amaçla v-for özelliği kullanılır. Görseldeki tablonun her satırı için verideki çalışan bilgilerinden birini kullanacağız.

src/components/EmployeeTable.vue
<template>
    <div id="employee-table">

        <table>
            <!-- thead kısmı aynen kalacak -->
            
            <tbody>
                <tr v-for="employee in employees" :key="employee.id">
                    <td>{{ employee.name }}</td>
                    <td>{{ employee.email }}</td>
                </tr>
            </tbody>
        </table>

    </div>
</template>

Vue bir array içindeki her elemanı tanımlamak için id'ye ihtiyaç duyar bunu :key="employee.id" özelliği ile verdik. Koda bakarsak v-for özelliğinin kullanıldığı tr bloğu array içindeki her eleman için işletiliyor. 3 tane kaydımız olduğuna göre 3 tane tr bloğu üretilecek. employees array'inin her bir elemanı employee değişkenine atanarak döngü yapılıyor. Böylece employee.id ya da employee.name dediğimizde o anda işlenmekte olan array elemanına ait bilgileri alıyoruz.

Sayfayı yenilediğimizde görsel değişmedi ancak artık elimizde değiştirilebilir bir veri var. Artık listeye yeni bir çalışan ekleyebilir ya da bir çalışanın bilgilerini değiştirebiliriz.



Formlarla Çalışmak


Şu ana kadar bir CRUD uygulamasının Read kısmını tamamladık. Şimdi yeni çalışan ekleme kısmını (yani Create) çalışalım. Bir form yaparak yeni çalışan girilmesini sağlayacağız. 

EmployeeForm.vue adında yeni bir komponent tanımlayalım. İsimi ve emaili girmek için birer input elemanı ve değerleri göndermek için bir button elemanı olsun. Girilen bilgileri de employee adında bir data elemanında toplayalım. 

src/components/EmployeeForm.vue
<template>
  <div id="employee-form">
    <form>
      <label>Çalışanın Adı</label>
      <input type="text" />
      <label>Çalışanın Emaili</label>
      <input type="text" />
      <button>Çalışan Ekle</button>
    </form>
  </div>
</template>

<script>
  export default {
    name: "employee-form",
    data() {
      return {
        employee: {
          name: "",
          email: ""
        }
      }
    }
  };
</script>

<style scooped>
  form {
    margin-bottom: 2rem;
  }
</style>

Bu komponenti App.vue içinde kullanalım,

src/App.vue
<template>
  <div id="app" class="small-container">
    <h1>Çalışanlar</h1>

    <employee-form />
    <employee-table :employees="employees" />
  </div>
</template>

<script>
import EmployeeTable from '@/components/EmployeeTable.vue'
import EmployeeForm from '@/components/EmployeeForm.vue'

export default {
  name: 'app',
  components: {
    EmployeeTable,
    EmployeeForm
  },
  data() {
   ...
  }
};
</script>

Aha, geldi bile sayfaya



Şimdi bu forma yazılan bilgileri Vue nasıl alacak sorusu var. Bunu yapmak için v-model özelliği var. v-model ile bir inputu bir veriye bağladığımızda inputtaki değer değiştikçe değişkenin değeri de değişir.

src/components/EmployeeForm.vue
<template>
  <div id="employee-form">
    <form>
      <label>Çalışanın Adı</label>
      <input v-model="employee.name" type="text" />
      <label>Çalışanın Emaili</label>
      <input v-model="employee.email" type="text" />
      <button>Çalışan Ekle</button>
    </form>
  </div>
</template>

İsterseniz Vue geliştirici araçlarında inputlara veri girdikçe değişkenlerin nasıl değiştiğini kontrol edebilirsiniz.



Verileri inputlardan elde ettikten sonra şimdi butonun basılmasıyla yukarıya App komponentine gönderirsek listemize yeni çalışan olarak eklenecektir.


Olay İzleyiciler


Form gönderilince onsubmit olayı oluşur. Bu Vue ile takip etmek için forma v-on:submit adlandırılmış özelliği ekleriz. Kısaca @submit de yazılabilir. Benzer şekilde elemanların tıklandığını izlemek için v-on:click ya da @click gibi olay izlemeleri sağlanır. 

submit olayı ayrıca HTML de default olan form gönderme işlemini iptal eden prevent özelliğine de sahiptir (JavaScript'de butonu işleyen fonksiyon içinde event.preventDefault() komutunun yaptığı iş).  Bunlarra göre form gönderilince bir metod çağırmak için şu ilaveyi yapalım.

    <form @submit.prevent="handleSubmit">

Bu özellik ile form butona basılarak gönderildiğinde default işlevini değil handleSubmit adındaki metodu çağırır.



Metodlar


Komponent özelliklerinden biri de metodlar. Metodlar o komponente ait iş yapan fonksiyonlar aslında ve methods: özelliği altında tanımlanırlar. Şimdi oraya handleSubmit metodunu ekleyelim. 

src/components/EmployeeForm.vue
<script>
  export default {
    name: "employee-form",
    data() {
      return {
        employee: {
          name: "",
          email: ""
        }
      }
    },
    methods: {
      handleSubmit() {
        console.log("handleSubmit çalışıyor..")
      }
    }
  };
</script>

Şimdi kenarda geliştirici araçlarında Console sekmesi açıkken butona tılarsak "handleSubmit çalışıyor.." mesajının konsola yazıldığını görürüz.



Emit - Verileri yukarı gönderme yolu


Butona tıklayınca metodun çalıştığını gördük. Şimdi App komponentine verileri gönderebilmek için komponentin $emit fonksiyonunu kullanacağız. $emit , komponentin export edildiği yere bir olay ve bir veriden oluşan ikili bir yapı yayınlar. Böylece, yukarıdaki komponent o olayı takip ederek ve gerçekleştiğinde de veriyi işleyerek bu yayını değerlendirir. 

Komut yapısı şöyledir,

this.$emit('yayınlanan-olayın-adı', yukarıGönderilenData)

Bizim olayımızda add:employee adında bir olay oluşturalım ve data olarak da komponentimizin employee datasını verelim.

src/components/EmployeeForm.vue
      handleSubmit() {
        this.$emit("add:employee", this.employee)
      }

Not: Buradaki add:employee şeklindeki olay isimlendirmesi de Vue geliştiricilerinin bir tavsiyesidir.

Şimdi geliştirici araçlarında "Events" sekmesindeyken butona tıkladığımızda olayın yayınlandığı bilgisi düşecektir.


Gördüğümüz gibi <employee-form> kaynağından adı add:employee olan bir olay oldu, bilgilerinde de bir array var, onuniçinde de email ve name etiketli değerler var diyor. Şimdi bu bilgiler App komponentine kadar geldi. Peki App komponentinde bunları nasıl işleyeceğiz?




Alt komponentten gönderilen verileri almak


employee-form komponentinin yayınladığı olayı yakalamamız gerekiyor. Bunu komponentin template içine eklendiği yerde yaparız. Şöyleki

<komponent @emit-edilen-olay="olayıİşleyecekMetod"></komponent>

Bizim olayımıza uygularsak

src/App.vue
    <employee-form @add:employee="addEmployee" />

Şimdi App komponenti içinde bir addEmployee metodu tanımlamak gerekiyor. Aşağıdan gelen olay içinde employee datasını da taşıyor olduğuna göre fonksiyonumuz şöyle olacaktır,

  methods: {
    addEmployee(employee) {
      this.employees = [...this.employees, employee]
    }

Bu kod employees array'ine yeni bir elemanı name ve email özellikleri ile ekler. Ama biz sorun çıkmaması için bir de id özelliği eklemiştik çalışanların veri array'ine. Önce bu id değerini array uzunluğundan çekerek hesaplayabiliriz.

Kısa bir hatırlatma eğer veriler bir veritabanı tablosunda saklanıyor olsaydı id değeri otomatik üretilecek sadece name ve email değerleri verilerek yeni kayıt eklenebilecekti. Burada yapılan sadece basit şekilde çalışma mantığını göstermek amacıyla geçici bir işlem. Yoksa bu şeklilde yapınca ilerde bir kayıt silme işlemi sıkıntı yaratacaktır.

id değerini de hesaplayan fonksiyonumuz şöyle,

src/App.vue
    addEmployee(employee) {
      const lastId = 
          this.employees.length > 0 
          ? this.employees[this.employees.length - 1].id 
          : 0;
      const id = lastId + 1;
      const newEmployee = {...employee, id};

      this.employees = [...this.employees, newEmployee];
    }

Artık yeni çalışanlar listeye eklenebilir. Ama malesef uygulama bir veritabanına bağlanmadığı için sayfa yenileyince liste yine eskisine dönecektir.






Form doğrulama temeli


Bu teknik olarak çalışıyor ama kodumuzu biraz daha düzenleyebiliriz.
  • Eğer her şey iyi giderse bir başarı mesajı yayınlayalım
  • Eğer eksik bilgi varsa bir hata mesajı yayınlayalım
  • Yanlış veri içeren input kutularını renklendirelim
  • Form sağlıklı gönderildiğinde içini boşlatalım
  • Sağlıklı bir gönderme sonrasında yine formun ilk kutusuna odaklanalım




Hesaplanmış özellikler


Vue'de bir şeylerin değeri değiştiğinde otomatik olarak değerleri değişen hesaplanmış özellikler kullanabiliriz (computed properties). Böylece Vue kodları içine karmaşık işlemler koymaktan kurtuluruz. Şimdilik sadece forma girilen 2 değerin boş olmamasını kontrol edeceğiz. 

src/components/EmployeeForm.vue
    methods: {
      handleSubmit() {
        this.$emit("add:employee", this.employee)
      }
    },
    computed: {
      invalidName() {
        return this.employee.name === ""
      },
      invalidEmail() {
        return this.employee.email === ""
      }
    }

Sayfayı yenileyip Vue geliştirici araçlarını açarsak employee-form computed bölümünde bu iki değeri görürüz. input elemanlarında değer girdiğimiz anda biz hiç bir şey yapmadan bu değerler otomatik değişir. İşte computed bölümünün ana özelliği bu. İşlemlerde kullanılan herhangi bir değer değiştiğinde hesaplanan değer de otomatik değişiyor.

Devamında data kısmına 3 yeni değer ilave ediyoruz. submitting ile şu anda form gönderimi yapıldığını, error ile bir şeylerin ters gittiğini success ile de işlemin sağlıklı sonlandığını ifade edeceğiz.

src/components/EmployeeForm.vue
    data() {
      return {
        submitting: false,
        error: false,
        success: false,
        employee: {
          name: "",
          email: ""
        }
      }
    },

Gönderme fonksiyonu ilk önce ne olup olmadığına bakmazsızın error ve succes'ı resetler. Sonra submiiting setleyerek gönderimin başladığını kaydeder. Sonra hesaplanmış değerlere bakar her hangi biri true değerdeyse error'u true yaparak metoddan çıkar. Eğer herşey normalse gönderme işini yapar ve success'i true yapıp diğerlerini false yapar.

src/components/EmployeeForm.vue
    methods: {
      handleSubmit() {
        this.submitting = true
        this.clearStatus()

        if (this.invalidName || this.invalidEmail) {
          this.error = true
          return
        }

        this.$emit("add:employee", this.employee)
        this.employee = {
          name: "",
          email: ""
        }
        this.error = false
        this.success = true
        this.submitting = false
      },

      clearStatus() {
        this.success = false
        this.error = false
      }
    },

Bir hata ve bir de başarı mesajı istediğimize göre önce CSSde hazırlık yapıyoruz,

src/components/EmployeeForm.vue
<style scooped>
  form {
    margin-bottom: 2rem;
  }

  [class*="-message"] {
    font-weight: 500;
  }

  .error-message {
    color: #d33c40;
  }

  .success-message {
    color: #32a95d;
  }
</style>

Son olarak formu ayarlıyoruz. Eğer form geçersiz bir bilgi gönderiyorsa o input class değerine has-error gelecek. :class yazarak class değerinin JavaScript işlem olacağı anlatılıyor, normal string değil. Bir input değeri değiştiğinde yada ona focuslanıldığında durum değerlerini sıfırlamak için clearStatus metodunu çağırıyoruz. Son olarak da error ya da success değerine bağlı aşağıda mesaj gösteriyoruz.

src/components/EmployeeForm.vue
<template>
  <div id="employee-form">
    <form @submit.prevent="handleSubmit">
      <label>Çalışanın Adı</label>
      <input 
        v-model="employee.name" 
        type="text" 
        :class="{ 'has-error': submitting && invalidName }" 
        @focus="clearStatus" 
        @keypress="clearStatus" 
      />
      <label>Çalışanın Emaili</label>
      <input 
        v-model="employee.email" 
        type="text" 
        :class="{ 'has-error': submitting && invalidEmail }" 
        @focus="clearStatus" 
      />
      <p v-if="error && submitting" class="error-message">
        ❗ Lütfen tüm alanları doldurun
      </p>
      <p v-if="success" class="success-message">
        ✅ Çalışan başarıyla eklendi
      </p>
      <button>Çalışan Ekle</button>
    </form>
  </div>
</template>

Mesajların başındaki o acayip karakterler nereden geldi bilmiyorum. Ben de internetten kopyaladım




Koşullu davranışlar


v-if kelimesi dikkatinizi çekmiştir. Bu Vue için buradaki örnekte eğer koşul değeri true çıkarsa <p> tag'i görünecek yoksa görünmeyecek. v-else-if ve v-else kelimeleri de programlama dillerindeki else if ve else gibi davranırlar. 

Şimdi eksik bilgi ile butona basarsak şöyle bir şey olacak.


Başarılı olunca da şöyle bir mesaj görünür.






Referans eklemek


Yapacak küçük bir iş daha kaldı. Yeni kayıt gönderdikten sonra ilk input elemanına kursörü göndererek arka arkaya veri girişlerinde kullanıcıya yardımcı olmak. Bir elemanı Vue programımız içinde kolayca bulmak istiyorsak ona ref özelliği ile bir referans ismi veririz. İlk input elemanına bir referans ekleyelim. 

src/components/EmployeeForm.vue
      <label>Çalışanın Adı</label>
      <input 
        ref="first" 
... />

Bunu kaydedip sayfada Vue geliştirme araçlarına bakarsak $refs grubu altında first adında bir input elemanı olduğunu görürüz. Şimdi gönderim tamamlandıktan sonra bu elemana fokuslanmak için handleSubmit metoduna ekleme yapalım.

...
        this.$emit("add:employee", this.employee)
        this.$refs.first.focus()
...

Artık yeni bir çalışan bilgisini eklediğimizde kursör otomatik olarak ilk input elemanına gidecektir.

Bu noktada formumuz bitti gibi. Ama ilginç gelebilecek bir şeyden bahsedeyim. Sayfanın kaynağına tarayıcıda bakarsak ekranda gördüğümüz hiç bir elemanı göremeyiz. Sadece geliştirici araçlarını açarsak o anda görünmekte olan elemanların gösterildikleri şekille bilgilerini görebiliriz. Tüm sayfayı Vue.js ve bizim yazdığımız programlardan üretilen JavaScriptler oluşturuyor.




Listeden birini silmek


Yeni çalışan ekleme formumuz tamam. Kayıtlarda yapılacak 2 işlem daha var kaydı düzenlemek ve kaydı silmek. Kolay olanından başlayalım, bir kaydı tablodan silmek. 

EmployeeTable.vue komponent dosyamızda tablo satırlarına yukarıda bahsettiğimiz aksiyonlar için butonlar ekleyelim.

src/components/EmployeeTable.vue
<template>
    <div id="employee-table">

        <table>
            <thead>
                <tr>
                    <th>Çalışanın Adı</th>
                    <th>Çalışanın Emaili</th>
                    <th>İşlemler</th>
                </tr>
            </thead>
            
            <tbody>
                <tr v-for="employee in employees" :key="employee.id">
                    <td>{{ employee.name }}</td>
                    <td>{{ employee.email }}</td>
                    <td>
                        <button>Düzenle</button>
                        <button>Sil</button>
                    </td>
                </tr>
            </tbody>
        </table>

    </div>
</template>

<script>
    export default {
        name: "employee-table",
        props: {
            employees: Array
        }
    };
</script>

<style scooped>
    button {
        margin: 0 0.5rem 0 0;
    }
</style>

Üzerinde Sil yazan butondan üst komponente deleteEmployee adında bir olay emit edelim, data olarak da employee.id değerini gönderelim.

src/components/EmployeeTable.vue
<button @click="$emit('delete:employee', employee.id)">Sil</button>

App.vue komponentine dönelim ve bu emit'in karşılığını yazalım.

src/App.vue
<employee-table :employees="employees" @delete:employee="deleteEmployee" />

Şimdi de metodların içine ilgili kaydı listeden kaldıracak bir fonksiyon yazalım. Kaydı array içinden silmek için filter fonksiyonu kullanıyoruz.

src/App.vue
  methods: {
    addEmployee(employee) {...},
    deleteEmployee(id) {
      this.employees = this.employees.filter(
        employee => employee.id !== id
      )
    }
  }

Oh ne güzel soru yok, emin misin yok, bas düğmeye silinsin kayıt. Neyse, hiç kayıt kalmayınca boş tablo yerine Keyıt bılunamadı falan bir mesaj çıkaralım.

src/components/EmployeeTable.vue
    <div id="employee-table">
        <p v-if="employees.length < 1" class="empty-table">
            Çalışan kaydı bulunamadı
        </p>
        <table v-else>
          ...
        </table>
    </div>

Kayıt silme işlemi bu kadar.



Kaydı düzenlemek


Düzenlemek silmekten biraz daha karışık. App.vue komponentinden başlamak daha kolay gibi. deit:employye olayını uygulamay ekleyerek başlayalım. 

src/App.vue
    <employee-table :employees="employees" 
              @delete:employee="deleteEmployee" 
              @edit:employee="editEmployee" 
    />

Sonra editEmployee metodunu tanımlayalım. id ve updatedEmployee parametreleri olsun. employees array üzerinde çalışarak ilgili kaydı güncelleyeceğiz.

src/App.vue
    editEmployee(id, updatedEmployee) {
      this.employees = this.employees.map(employee => 
        employee.id === id ? updatedEmployee : employee
      )
    }

Bu kadar basit. Ama bu updatedEmployee nasıl elde edilecek esas püf noktası burada. EmployeeTable.vue dosyasına dönelim. Düzenleme butonuna tıklanması ile birlikte bir düzenleme moduna geçilmesini istiyoruz.

src/components/EmployeeTable.vue
<button @click="editMode(employee.id)">Düzenle</button>

Adı editing olan bir data tanımlayacağız ve onda düzenlenmekte olan kaydın id bilgisini saklayacağız. Sonra da lokal tanımlanmış editEmployee metoduyla üst komponente olay bildirimi yapacağız. Bu da App.vue içinde edit:employee olayını tetikler.

src/components/EmployeeTable.vue
<script>
    export default {
        name: "employee-table",
        props: {
            employees: Array
        },
        data() {
            return {
                editing: null
            }
        },
        methods: {
            editMode(id) {
                this.editing = id
            },

            editEmployee(employee) {
                if (employee.name === "" || employee.email === "") return
                this.$emit("edit:employee", employee.id, employee)
                this.editing = null
            }
        }
    };
</script>

Şu anda tablomuzun kodu şöyle

<tr v-for="employee in employees" :key="employee.id">
    <td>{{ employee.name }}</td>
    <td>{{ employee.email }}</td>
    <td>
        <button @click="editMode(employee.id)">Düzenle</button>
        <button @click="$emit('delete:employee', employee.id)">Sil</button>
    </td>
</tr>

Kaydı düzenlenebilir hale getirmek için editing === employee.id karşılaştırması true değerde olmalı. Düzenleme için EmployeeTable.vue içerisinde bulunulan satırda direk işlem yapacağız. koşul doğruysa o satır düzenleme yapılıyordur, ve text yerine inputlar içinde değerleri gösteririz. Kaydetmek ve olayı iptal etmek için de butonlar değişmeli.

Bunları düşününce tablo satırı kodumuzu şöyle yaparız.

src/components/EmployeeTable.vue
<tr v-for="employee in employees" :key="employee.id">
    <td v-if="editing === employee.id">
        <input type="text" v-model="employee.name">
    </td>
    <td v-else>{{ employee.name }}</td>

    <td v-if="editing === employee.id">
        <input type="text" v-model="employee.email">
    </td>
    <td v-else>{{ employee.email }}</td>

    <td v-if="editing === employee.id">
        <button @click="editEmployee(employee)">Keydet</button>
        <button class="muted-button" @click="editing = null">İptal</button>
    </td>
    <td v-else>
        <button @click="editMode(employee.id)">Düzenle</button>
        <button @click="$emit('delete:employee', employee.id)">Sil</button>
    </td>
</tr>

Şimdi düzenleme yapabiliriz.


Not: bu input elemanları tablo satırını genişlettiği için butonlar düzgün görünmedi ve App.vue stil değerlerindeki small-container sınıfı değerini max-width: 720px; yaptım.

Düzenleme çalışıyor ancak değer değiştiridikten sonra iptal etsek de geri dönemiyoruz. İptal butonunda işlem yapmak yerine cancelEdit metodunu çağıracağız.

<button class="muted-button" @click="cancelEdit(employee)">İptal</button>

Değişiklik yapılmadan önceki hale geri dönebilmek için düzenleme işlemine girerken çalışan kaydının bir kopyasını cachedEmployee değişkenine kaydedeceğiz. İptal butonuna basılınca da bu kaydettiğimiz değeri geri kopyalayacağız. editMode metoduna id değerini değil tüm employee nesnesini parametre olarak göndereceğiz ki kopyasını alabilelim. Bu amaçla düzenle düğmesi kodunu şöyle değiştirelim.

<button @click="editMode(employee)">Düzenle</button>

editMode ve cancelEdit metodlarımızın son hali de şöyle olacak.

editMode(employee) {
    this.cachedEmployee = Object.assign({}, employee)
    this.editing = employee.id
},
cancelEdit(employee) {
    Object.assign(employee, this.cachedEmployee)
    this.editing = null
},

Uygulamamız şu anda teknik olarak tamamlanmış bulunuyor. Ama ürün olarak sunabilmemiz için verileri bir veritabanında barındırmak gerekiyor. Bu amaçla deneme olarak arka taraftaki bir veritabanına API çağrıları ekleyeceğiz.



Asenkron REST API çağrıları yapmak


TEst için API çağrıları yapabileceğimiz ve bize gerçek veri cevapları verebilen JSON Placeholder sitesini kullanacağız. Değerleri GET ile alabiliriz (örnek olarak https://jsonplaceholder.typicode.com/users syafasına bakabiliriz buradaki users JSON bilgisini kullanacağız). Ayrıca POST , PUT ve DELETE çağrıları da yapabiliriz, fakat bunlar gerçek veritabanında değişiklik yapmazlar sadece test amaçlıdırlar. 

async/await ile yapılmış ve try/catch yapısı içeren bir asenkron fonksiyon yapısı şöyledir. 

async asynchronousMethod() {
  try {
    const response = await fetch('url')
    const data = await response.json()

    // `data`kullanarak yapılacak işlemler
  } catch (error) {
    // `hata` durumunda yapılacak işlemler
  }
}

Burada Fetch API kullanılmış.Sebebi Node ve tarayıcılarda zaten yüklüdür.

Şimdi kayıtlar üzerinde işlem yaptığımız tüm CRUD metodlarını async metodlara dönüştüreceğiz ve Front-end'de yaptığımız tüm veri işlemlerini API ile Back-end veritabanında yapacağız.


Lifecycle Metodları


Dışarıdan veri alıp verirken öyle hafızadaki gibi sürekli değer okuma yapıp, her değişiklikte yazma yapılmaz. GET işlemi ile tüm employee tablosu verisini alacağız ve önceden üretitiğimiz geçici verilerle komple değiştireceğiz. Bu GET işlemini mounted lifecycle metodu içinde yapacağız. 

mounted komponentimizin DOM'a yerleştirilmesi sonrası çalışır. Bu bir API'den veri alırken kullanılan standart andır (bazıları created metodunu da kullanır). 

src/App.vue
export default {
  name: 'app',
  components: {
    EmployeeTable,
    EmployeeForm
  },

  data() {
    return {
      employees: []
    }
  },
  mounted() {
    this.getEmployees()
  },

Şimdi tüm CRUD metodlarımızı asenkron eşlenikleri ile değiştirebiliriz.



GET


Kaynaktan veri tablosunu almak için metodların içine getEmployees metodunu ekleyelim. 

src/App.vue
    async getEmployees() {
      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users")
        const data = await response.json()
        this.employees = data
      } catch (error) {
        console.error(error)
      }
    }

Sayfayı yenilediğimizde API'den çekilen verileri görürüz.






POST


Yeni kayıt eklemek.

    async addEmployee(employee) {
      try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users", {
          method: "POST",
          body: JSON.stringify(employee),
          headers: { "Content-type": "application/json; charset=UTF-8" }
        })
        const data = await response.json()
        this.employees = [...this.employees, data]
      } catch (error) {
        console.error(error)
      }
    },
 




PUT


Mevcut kaydı güncellemek

    async editEmployee(id, updatedEmployee) {
      try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, 
        {
          method: "PUT",
          body: JSON.stringify(updatedEmployee),
          headers: { "Content-type": "application/json; charset=UTF-8" }
        })
        const data = await response.json()
        this.employees = this.employees.map(employee => 
          employee.id === id ? data : employee
        )
      } catch (error) {
        console.error(error)
      }
    },

fetch komutunda id değerini yazabilmeek için stringi " ` " ters turnak ile ifade ettik aman dikkat.




DELETE


Olan kaydı silmek.

    async deleteEmployee(id) {
      try {
        await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, {
          method: "DELETE"
        })
        this.employees = this.employees.filter(
          employee => employee.id !== id
        )
      } catch (error) {
        console.error(error)
      }
    },

Artık tamam. Verilerimizi statik data yerine jsonplaceholder üzerinden okumayı yazmayı da bitirdik. Yazdıklarınızda hata olup olmadığını test için her işlemi tek tek deneyip geliştirici araçlarında konsola hata mesajı gelecek mi diye bir süre bekleyin.




Derleyip Github sayfalarında yayınlamak


Yeni geliştirdiğimiz uygulamamızı Github sayfalarında statik site olarak yayınlamak istiyoruz. Github'da yeni bir repo oluşturduğunuzu ve aşağıdaki adımlar ile master dalına commit yaptığınızı kabul edelim.

git remote add origin https://github.com/username/vue-app
git add .
git commit -m "initial commit"
git push -u origin master

Derlemeyi aşağıdaki adımlarla gönderebiliriz.

gh-pages dallanması yapalım

git checkout -b gh-pages

dist klasörünü .gitignore içinden silelim

Ana klasörde vue.config.js adında bir dosya oluşturalım ve Github publicPath oluşturalım

vue.config.js

module.exports = {
    publicPath: "/vue-app/"
}

dist klasörünü oluşturacak olan derleme komutunu çalıştıralım,

npm run build
# veya
yarn build
 
Derleme klasörünü ekleyelim, commit edelim ve Github'a gönderelim.

username.github.io/vue-app adresinde sayfamız yayınlanmaya başlamıştır.

Bu yazı da bu kadar geliştirmeye devam edin. Kendinize iyi bakın. Kalın sağlıcakla..








Hiç yorum yok:

Yorum Gönder