10 Nisan 2021 Cumartesi

NwJs ve VueJs ile Masaüstü Uygulama Geliştirmek

 Merhaba, bu yazıda VueJs yardımıyla NwJs platformunda masaüstü uygulama geliştirmek için araştırma yapıyorum. Nw.js , Google Chrome ve NodeJs temelleri üzerinde kurulmuş ve masaüstü uygulamaları web teknolojileri kullanarak üretmeyi amaçlayan bir platform. İlgimi çekti ve bir deneyelim bakalım neler yapabiliriz diyerek başladım. Bu yazıda VueJs versiyon 2 kullandım.


NwJs Kurulumu


Ben NwJs sitesindeki yazıya göre kurdum. Bu sayfadan SDK versiyonu zip dosyasını indirelim ve bilgisayarımızda seçtiğimiz bir klasöre içindekileri açalım. Daha sonra bu klasörü sistemimizin PATH ortam değişkenine ekleyelim. Nasıl yaparız bu PATH işini? Sistem -> Gelişmiş Sistem Ayarları -> Ortam değikenleri ile Windows işletim sisteminde yapılıyor. 




Merhaba Dünya


Bir test uygulaması ile kurulumun çalıştığından emin olalım. Uygulamamız için bir yeni klasör üretelim ve içine NodeJs uygulamalarının olmazsa olmazı package.json dosyasını ekleyelim.

package.json
{
    "name": "MerhabaDünya",
    "main": "index.html"
}

Burada verilen bilgiye göre index.html dosyası uygulamamızın ana giriş dosyası olacak. Bu dosya için de basit bir html oluşturalım.

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Merhaba Dünya!</title>
</head>
<body>
    <h1>Merhaba Dünya!</h1>
</body>
</html>


Evet şimdi bu uygulama test edilmeye hazır. Uygulama klasöründe bir terminal açıp şu komutu girelim.

nw .

Şöyle bir pencere açılacaktır.



Ne ka güzel, ne ka güzeeel. Artık html , css ve javascript kullanarak masa üstü uygulama geliştirebiliriz. Terminal ile uğraşmak istemiyorum diyorsanız ya bir bat dosya ya da bir kısayol oluşturun. 





VueJs Kullanan Masa Üstü Uygulama


Şimdi yeni bir klasör üretelim ve bu sefer VueJs kullanan bir uygulama üretelim. 

package.json
{
    "name": "Merhaba Vue",
    "main": "index.html"
}
ve görsel

index.html
<!DOCTYPE html>
<html>
<head>
    <title>Merhaba Vue!</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app"><h1>{{ mesaj }}</h1></div>

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

O az önceki kısayolu her yere kopyalayın bence, gayet rahat oluyor. Uygulama çalışınca şöyle görünecek.


Bu test Vue sayfasını daha önceki bir yazımda da anlatmıştım. İsteyen ayrıntılara oradan bakabilir. 

Ama uygulamamız internetten dosya indiriyor derseniz internetten çağırdığımız vue.js dosyasını indirerek klasörümüze koyup direk yerelden de kullanabiliriz. Sadece script ekleyen satırı değiştirmek yetecektir. 

index.html
...
<head>
    <title>Merhaba Vue!</title>
    <script src="vue.js"></script>
</head>
...

Böylece uygulama internete gerek duymadan da çalışır. Stil dosyası olarak da standart bir stil için primitive.css indirip klasörümüze primitive.css adıyla kaydedersek. 

index.html
...
<head>
    <title>Merhaba Vue!</title>
    <script src="vue.js"></script>
    <link rel="stylesheet" href="primitive.css" />
</head>
...

Uygulama standart bir css stili kullanmaya başlayacaktır. 





Geliştirmeye Devam


Temelleri bitirdik şimdi daha önce VueJs için yazdığım şu yazıdakileri masaüstü olarak gerçekleştirelim. ilk adında bir klasör oluşturalım içine az önce indirdiğimiz vue.js dosyasını kopyalayalım ve NwJs uygulamamızı oluşturmaya başlayalım. 

ilk/index.html
<!DOCTYPE html>
<html>
<head>
    <title>VueJs Öğreniyorum</title>
    <script src="vue.js"></script>
</head>
<body>
    <div id="app">
        <h3>{{ mesaj }}</h3>
    </div>

    <script src="main.js"></script>
</body>
</html>
Gördüğümüz gibi JavaScript kodu main.js adında bir dosyaya aldık. İçinde şunlar var.

ilk/main.js
new Vue({
	el: "#app",
	data: {
		mesaj: "VueJs Öğreniyorum"
	}
});

Böyle daha yapısal davrandık. Son olarak package.json dosyamızı da oluşturalım.

ilk/package.json
{
    "name": "İlk",
    "main": "index.html"
}

Uygulamayı çalıştıralım



Uygulamayı Geliştirelim


Sayfaya bir input elemanı ekleyerek kullanıcının mesajı buradan değiştirmesini sağlayalım. 

ilk/index.html
...
    <div id="app">
        <input type="text" @input="mesajDeğiştir($event)">
        <h3>{{ mesaj }}</h3>
    </div>
...

@input kelimesi bildiğimiz gibi v-on:input kelimesinin kısaltılmışı. mesajDeğiştir metodunu da JavaScript kodumuza ekleyelim.

ilk/main.js
new Vue({
    el: "#app",
    data: {
        mesaj: "VueJs Öğreniyorum"
    },
    methods: {
        mesajDeğiştir: function(olay){
            this.mesaj = olay.target.value;
        }
    }
});

Artık input içine yazı yazdıkça anında mesaja kopyalanacaktır.


Süper. VueJs ile daha önce web sayfası olarak yaptığımız uygulamaları şimdi masaüstü olarak yapıyoruz. index.html dosyamız içinden main.js içinde tanımlı bir fonksiyonu da çağırabiliriz. 

ilk/index.html
...
    <div id="app">
        <h3>{{ merhabaDe() }}</h3>
    </div>
...

ilk/main.js
    methods: {
        merhabaDe: function(){
            return "Merhaba, naber?";
        }
    }
});
...


Bu arada Görev Yöneticisinde uygulamamız ile ilgili görevlere bakınca aynı tarayıcı programlarında olduğu gibi (özellikle Google Chrome) birçok yardımcı programdan oluştuğunu görürüz. 

Fonksiyon içinden data bölümündeki değişkenlere erişmek için başına this ekliyorduk. Mesajımızı fonksiyon yoluyla yayınlamayı düşünürsek.

ilk/main.js
....
    methods: {
        merhabaDe: function(){
            return this.mesaj;
        }
    }
});

Orjinale geri döndük. Ama bu sefer mesajı direk değişkenden değil de metodla alıyoruz. Sayfamıza bir de link ekleyelim ama referans adresi Vue değişkeninden gelsin,

ilk/index.html
....
    <div id="app">
        <h3>{{ merhabaDe() }} - <a href="{{ link }}">Google</a></h3>
    </div>
...

ilk/main.js
new Vue({
    el: "#app",
    data: {
        mesaj: "VueJs Öğreniyorum",
        link: "http://google.com/"
    },
...


Televizyon tamircisi terimiyle "görüntü va ses yok". Link tıklanınca hata mesajı alıyoruz. Uygulamayı tekrar çalıştıralım ve sayfada boş bir yere sağ tıklayıp İncele seçerek ya da F12 tuşlayarak Geliştirici Araçları penceresini açalım. 



Diyor ki eleman özelliğine bu şekilde değişken bağlamak artık yok. v-bind ile değişken bağlaması yapınız. Görsel dosyamızı şöyle düzeltelim.

ilk/index.html
...
    <div id="app">
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
    </div>
...

Artık link çalışmaya başladı


Aha da yaşasın tarayıcı yaptık!. Hayır yapmadık, zaten uygulamamız tarayıcıda çalışıyor da farkında değiliz. Sonuçta Google Chrome yardımıyla platform oluşturulmuş.  :href deyiminin uzun şekli v-bind:href Bir şeyi değişken değerine bağlamaya bind deniyor. Burada da href özelliğini link değişkenine bağlamış oluyoruz. link değişkeni değişince otomatikman tıklanınca gidilecek adres değişir. Hadi deneyelim, sayfaya bir buton elemanı ekleyelim.

ilk/index.html
...
    <div id="app">
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
        <button @click="link = 'http://yandex.com'">Link Değiştir</button>
    </div>
...

Şimdi uygulamayı yenileyip linke tıklamadan önce butona tıklarsak Google yerine Yandex açılacaktır. Aynısını metod ile de yapabiliriz. 

ilk/index.html
...
    <div id="app">
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
        <button @click="linkDeğiştir()">Link Değiştir</button>
    </div>
...

ilk/main.js
...
    methods: {
        merhabaDe: function(){
            return this.mesaj;
        },
        linkDeğiştir: function(){
            this.link = "http://yandex.com/";
        }
    }
});


Bir kerelik bağlama


Sayfada değişken ile de mesajı gösterelim.

ilk/index.html
...
    <div id="app">
        <h1>{{ mesaj }}</h1>
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
    </div>
...

ilk/main.js
...
    methods: {
        merhabaDe: function(){
            this.mesaj = "Sadece Merhaba";
            return this.mesaj;
        }
    }
...

2 mesaj da aynı olacaktır.


Aslında yukarıya önce VueJs Öğreniyorum yazar ama değiken başlanmış olduğu için alt satırda çağırılan metod mesaj değişkeni değerini değiştirince yukarıdaki yazı da değişir. Eğer metodla yukarının da değişmesini istemiyorsak v-once kelimesi ile mesaj değişkenini sadece bir kere okunmasını sağlayabiliriz. 

ilk/index.html
...
    <div id="app">
        <h1 v-once>{{ mesaj }}</h1>
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
    </div>
...




HTML kodu yayınlamak


Diyelim yukarıda sadece href değerini bağladığımız link elemanını komple VueJs değişkeni yardımıyla görselde yayınlamak istiyoruz. Görsel ve JavaScript şöyle olacaktır.

ilk/index.html
...
    <div id="app">
        <h1 v-once>{{ mesaj }}</h1>
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
        <hr>
        <h3>{{ linkEleman }}</h3>
    </div>
...

ilk/main.js
...
    data: {
        mesaj: "VueJs Öğreniyorum",
        link: "http://google.com/",
        linkEleman: "<a href='http://google.com/'>Google</a>"
    },
...



Web uygulamalarında güvenlik amaçlı olan bir önleme takıldık. HTML kodunu değişkene verince yayınlarken düz yazıya dönüştürüyor. Değişkendeki HTML kodu yayınlamak için v-html yönergesi kullanılır

ilk/index.html
...
    <div id="app">
        <h1 v-once>{{ mesaj }}</h1>
        <h3>{{ merhabaDe() }} - <a :href="link">Google</a></h3>
        <hr>
        <h3 v-html="linkEleman"></h3>
    </div>
...


Aslında masaüstü uygulamada Cross Site Scripting'den korkmamıza gerek yok ama web teknolojisi ile yazılınca program bu kısıtlama karşımıza çıktı.


Olayları İzlemek


VueJs kullanıcının yaptığı etkileşimlerden doğan olayları takip etmek için v-on yönlendiricisini kullanır. En başta hatırlarsak bir input elemanına değer girilmesini izleyen bir kod yazmıştık. Şimdi de bir buton tıklanmasını izleyen örnek verelim.

ilk/index.html
...
    <div id="app">
        <button v-on:click="arttır()">Tıkla</button>
        <h3>{{ sayaç }}</h3>
    </div>
...

ilk/main.js
new Vue({
    el: "#app",
    data: {
        sayaç: 0
    },
    methods: {
        arttır: function(){
            this.sayaç++;
        }
    }
});




Olay nesnesinden olay bilgisini almak


Yukarıdaki koda ilave olarak mouse koordinatları bildirmek için bir görsel ilavesi yapalım.

ilk/index.html
...
    <div id="app">
        <button v-on:click="arttır()">Tıkla</button>
        <h3>{{ sayaç }}</h3>
        <p v-on:mousemove="koordinatYenile()">{{ x }} / {{ y }}</p>
    </div>
...

ilk/main.js
...
    data: {
        sayaç: 0,
        x: 0,
        y: 0
    },
    methods: {
        arttır: function(){
            this.sayaç++;
        },
        koordinatYenile: function(olay){
            this.x = olay.clientX;
            this.y = olay.clientY;
        }
...

Paragraf üzerinde mouse gezdirdikçe koordinatlar değişecektir. Neden sadece paragraf üzerindeyken? Çünkü olay izlemesini paragraf elemanına tanımladık. Metod tanımında parametre olarak bir isim verirsek o isimdeki değişkende olayın bilgisi olacaktır. clientX ve clientY ise VueJs ile alakasız. Bunlar JavaScript MouseMove olayının standart bilgileri.

v-on: yerine kısaltması olan @ işaretini de kullanabiliriz,

<p @mousemove="koordinatYenile($event)">{{ x }} / {{ y }}</p>


Kendi verimizi fonksiyona göndermek


Ya görev fonksiyonuna kendi istediğimiz bir parametreyi göndermek istersek ne yapacağız? Sayaç meselesine geri dönelim. Şu anda kodumuz butona her tıklandığında değeri bir arttırıyor. Değeri her seferinde arttırılacak miktarı da buton tıklamasında fonksiyonu çağırırken belirtmek istersek şöyle yaparız.

ilk/index.html
...
    <div id="app">
        <button @click="arttır(2)">Tıkla</button>
        <h3>{{ sayaç }}</h3>
        <p @mousemove="koordinatYenile($event)">{{ x }} / {{ y }}</p>
    </div>
...

ilk/main.js
...
    methods: {
        arttır: function(miktar){
            this.sayaç += miktar;
        },
...

Artık her buton tıklanınca sayı 2 artacaktır. Peki hem kendi verilerimizi hem de olay bilgisini nasıl alacağız?

ilk/index.html
...
    <div id="app">
        <button @click="arttır(2, $event)">Tıkla</button>
        <h3>{{ sayaç }}</h3>
        <p @mousemove="koordinatYenile($event)">{{ x }} / {{ y }}</p>
    </div>
...

$event yazarak olay bilgisini parametre olarak verebiliriz. $event değişkeni ön tanımlıdır ve içinde gerçekleşmiş lan olay bilgisini barındıran bir nesnedir. Fonksiyon içinde de 2. parametre olarak standart parametre gibi kullanabiliriz. Mesela,

ilk/main.js
...
        arttır: function(miktar, olay){
            console.log(olay);
            this.sayaç += miktar;
        },
...



Olayı değiştirmek


Bazen tanımladığımız olayı modifiye etmemiz gerekebilir. Bunu görmek için de koordinatlar göstergemize örnek yapalım. Diyelin paragraf içinde bir bölgede mouse koordinatlarının takip edilmemesini istiyoruz. Görselimizi şöyle değiştirelim,


ilk/index.html
...
        <p @mousemove="koordinatYenile($event)">
            Koordinatlar : {{ x }} / {{ y }} 
            - <span>ÖLÜ BÖLGE</span>
        </p>
...

Eklediğimiz span elemanı hala bizim paragrafımız içinde olduğundan onun üzerinde mouse gezerken de koordinatlar yenilenir. Bunu durdurmak için span elemanına da mouse takip eden bir olay izlemesi yazıp onun fonksiyonu içinde paragraftaki izleyicinin harekete geçmesini engelleyebiliriz.

ilk/index.html
...
        <p @mousemove="koordinatYenile($event)">
            Koordinatlar : {{ x }} / {{ y }} 
            - <span @mousemove="durdur($event)">ÖLÜ BÖLGE</span>
        </p>
...

Metod tanımı da şöyle,

ilk/main.js
...
        durdur: function(olay){
            olay.stopPropagation();
        }
...


stopPropagation yazımını kaç defa kontrol ettim. Bu fonksiyon olayın burada bitirilmesi ve diğer izleyenlere bildirilmemesini sağlıyor.

Fakat VueJs'de bunu daha kolay bir şekilde yapabiliriz.

ilk/index.html
...
        <p @mousemove="koordinatYenile($event)">
            Koordinatlar : {{ x }} / {{ y }} 
            - <span @mousemove.stop="">ÖLÜ BÖLGE</span>
        </p>
...

.stop eki olayın yayınlanmasını durdurur. Bu sayede bir yeni fonksiyon tanımlamaya gerek kalmaz. durdur metodumuza artık gerek kalmadı silebiliriz. VueJs'de bu eklere olay modifiye edici denir (event modifier). 



Keyboard olayları


Görsele bir input elemanı ekleyelim ve içinde herhangi bir tuşa basılıp bırakılınca bir alarm versin.

ilk/index.html
...
            <input type="text" @keyup="alarmVer()">
...

Ve karşılık metodumuz,

ilk/main.js
...
         alarmVer: function(){
            alert("ALARM!");
        }
...

Hangi tuşa basarsak basalım bıraktığımızda bir alarm penceresi açılacaktır. Peki sadece enter tuşu basıp bırakılınca alarm vermek istersek ne yapacağız?. VueJs'de bunun için bir olay modifiye edici var ,

ilk/index.html
...
        <input type="text" @keyup.enter="alarmVer()">
...

Böylece input elemanına kullanıcı birşeyler girip enter basınca çalışan bir metod tanımlarız. .enter ile keyup olayını modifiye edince sadece enter tuşuna cevap veriyor. Tuş ismini yazarak böyle filtrelemeler yapabiliriz. Mesela .z desek z tuşu basılınca çalışacaktı. Hem enter hem de boşluk çubuğu basılmasında çalışsın istersek.

ilk/index.html
...
        <input type="text" @keyup.enter.space="alarmVer()">
...

Böyle sürüp gider.



Görsel içinde JavaScript yazmak


Butonumuz tıklanınca fonksiyon çağırmak yerine direk JavaScript kod da çalıştırabiliriz. Örnek olarak butonumuzun bir kopyasını alalım ve fonksiyon çağırmak yerine kod yazalım.

ilk/index.html
...
        <button @click="arttır(2, $event)">Tıkla</button>
        <button @click="sayaç++;">Tıkla</button>
...

Aynı şekilde değeri görterirken de işlem yapabiliriz.

ilk/index.html
...
        <h3>{{ sayaç * 2 }}</h3>
...

Bu da tamamen geçerli bir yöntem olacaktır. İstersek daha büyütelim sayı 10'dan büyük mü kontrol edelim.

ilk/index.html
...
        <h3>{{ sayaç * 2 > 10 ? "Sayı 10'dan büyük" : "Sayı 10'dan küçük" }}</h3>
...

Gördüğümüz gibi kodumuz karışık yada sade, uzun ya da kısa olsun çalışacaktır. Tabi bu şekilde görsel içine kod yazmak yapısal düzen bakımından bozucu bir şey. Lojik ile görseli birbirine karıştırıyoruz. Ancak gerekli olduğu durumlarda böyle bir imkan olduğunu da bilelim.



İki yönlü bağlama


Daha önce görsel eleman değeri ile bir verinin bağlanmasını bind işlemi olarak görmüştük. Kafa karıştıranları silelim , görselimizi ve JavaScript kodumuzu basit bir hale getirip bu konuya başlayalım.

ilk/index.html
...
    <div id="app">
        <input type="text">
        <h3>{{ isim }}</h3>
    </div>
...

ilk/main.js
new Vue({
    el: "#app",
    data: {
        isim: "Ümit"
    }
});

İki yönlü bir bağlama yapmak istiyoruz. Hem input içine girilen değer isim değişkenine konsun ve sayfadaki yazı değişsin. Hem de kodumuz içinde bir yerde isim değişkeni değişince bu da input içine yazılsın.

ilk/index.html
...
    <div id="app">
        <input type="text" v-model="isim">
        <h3>{{ isim }}</h3>
    </div>
...

v-model deyimi input eleman değeri ve isim değişken değerini direk birbirine çift yönlü bağlıyor. Daha önceki gibi yok değer girişine olay izleme yaz , yok fonksiyon içinde olay.target.value deyip değere ulaşmaya çalış gibi şeylerin hepsini bu deyim otomatik olarak bizim adımıza yapacaktır. input içinde değiklik yaptıkça hiç kod yazmadan direk olarak yazı da değişecektir.

Kod içinde değer değişiminin etkisini görmek için de bir buton ekleyelim.

ilk/index.html
...
    <div id="app">
        <input type="text" v-model="isim">
        <button @click="isim = 'Hasan';">Değiştir</button>
        <h3>{{ isim }}</h3>
    </div>
...





Hesaplanmış özellikler ile değişimlere tepki vermek


Şöyle de bir kodumuz olsun

ilk/index.html
...
    <div id="app">
        <button @click="arttır()">Arttır</button>
        <h3>{{ sayaç }}</h3>
        <h3>{{ sonuç }}</h3>
    </div>
...

ilk/main.js
...
    data: {
        sayaç: 0,
        sonuç: ""
    },
    methods: {
        arttır: function(){
            this.sayaç++;
            this.sonuç = this.sayaç > 5 ? "5'ten büyük" : "5'ten küçük";
        }
    }
...

Butona her tıklandığında sayaç değeri bir artacak, değer 5'ten büyükse sonuç değeri 5'ten büyük değilse sonuç değeri 5'ten küçük olacak. Basit (aslında eşit ya da küçük olmalı ya, neyse amaç öğrenmek).

Küçük kod parçalarında bu kolay yönetilebilir ve anlaşılabilir bir yöntem. Ama ilk olarak şuna dikkat edelim sayfa ilk geldiğinde butona tıklanmadan önce sonuç değerini göremiyoruz. Ya da diyelim bir tane de Azalt butonu ekledik, buna da bir metod yazdık

ilk/index.html
...
        <button @click="arttır()">Arttır</button>
        <button @click="azalt()">Azalt</button>
...

ilk/main.js
...
    methods: {
        arttır: function(){
            this.sayaç++;
            this.sonuç = this.sayaç > 5 ? "5'ten büyük" : "5'ten küçük";
        },
        azalt: function(){
            this.sayaç--;
            this.sonuç = this.sayaç > 5 ? "5'ten büyük" : "5'ten küçük";
        }
    }
...

Biz ne yapacağız? Sayfa yüklenince karşılaştırma yap, değer artırılınca karşılaştırma yap, azaltılınca karşılaştırma yap. Diyelim değeri 10'ar arttıran kod yazdık orada da sonuç değerini bulmak için karşılaştırma yap. Sonra da Allah kahretsin küçük ya da eşit yazmalıydık de tüm karşılaştırmaları tek tek bul değiştir. Bunu kökten çözmek için hesaplanmış özellikler vardır.

ilk/main.js
new Vue({
    el: "#app",
    data: {
        sayaç: 0
    },
    computed: {
        sonuç: function(){
            return (this.sayaç > 5 ? "5'ten büyük" : "5'ten küçük");
        }
    }
});

Artık butonlardan metod çağırmak yerine direk sayaç değerini değiştirebiliriz.

ilk/index.html
...
        <button @click="sayaç++;">Arttır</button>
        <button @click="sayaç--;">Azalt</button>
...

computed bölümünde tanımlanan işlemler bağlı oldukları değişken değerlerinde bir değişim oldu mu otomatikman devreye girip hesap yaparlar. Bu örnekte sayaç değeri değiştikçe otomatik olarak sonuç değeri de değişir. Ancak computed bölümünde yer alan değerler aynı data bölümündekiler gibi görselimizde kullanılabilir. Aynen kullanılabilir derken bir küçük ayrıntı var, computed bölümündeki değerler default olarak sadece okunan değerlerdir. Sayfaya {{ }} içinde yazarken sorun yok, v-bind ile bir elemanın değerini bağlarken de sorun yok. Ama v-model kullanınca bildiğimiz gibi iki yönlü bağlama oluyor, yani dışarıdan da hesaplanmış değer üzerine yazılabilmeli. Bu amaçla tanımlama yapılırken get ve set fonksiyonları ayrı ayrı tanımlanır. Bunu da ileride görürüz inşalllah..



Bir alternatif olarak watch


watch bölümünde değer değişimini izlemek istediğimiz data bölümü değişkenlerine ait görev fonksiyonları tanımlayabiliriz. Bu aslında yukarıda anlatılan ve computed kullanılan yapıya bir alternatif olarak kullanılabilir. Yani sayaç değişkeni izlemeye alınır ve değişim olunca sonuç değişkeni değeri belirlenir.

ilk/main.js
...
    data: {
        sayaç: 0,
        sonuç: ""
    },
    watch: {
        sayaç: function(değer){
            this.sonuç = değer > 5 ? "5'ten büyük" : "5'ten küçük";
        }
    }
...

Bu da sayfa ilk yüklendiği an hariç aynı etkiyi yapacaktır. Ama örneği başka bir şeyle geliştirelim. Diyelim değer değiştirildikten 3 saniye sonra sayaç değerini sıfır yapalım.

ilk/main.js
...
    watch: {
        sayaç: function(değer){
            this.sonuç = değer > 5 ? "5'ten büyük" : "5'ten küçük";
            var vm = this;
            setTimeout(function(){
                vm.sayaç = 0;
            }, 3000);
        }
    }
...

setTimeout bloğu içinden sayaç değerine ulaşabilmek için bloğa girmeden evvel this değişkenini vm değişkenine kopyaladık. Değer değişimi ile buraya gelindiği için eklediğimiz timeout , ilk değer değişiminden 3000 mili saniye sonra sayaç değerini sıfırlar.



CSS sınıflarıyla dinamik stil değiştirme


Bu konuyu işlemek için bir CSS stil dosyasına ihtiyacımız olacak. Uygulama klasörümüzde styles.css adında yeni bir text dosya tanımlayalım ve bunu index.html dosyamızdan çağıralım.

ilk/index.html
...
<head>
    <title>VueJs Öğreniyorum</title>
    <script src="vue.js"></script>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
...

Görselimizin ve JavaScript dosyamızın başlangıç hali de şunlar olsun.

ilk/index.html
...
    <div id="app">
        <div class="demo"></div>
        <div class="demo"></div>
        <div class="demo"></div>
    </div>
...

ilk/main.js
new Vue({
    el: "#app"
});

Bu div elemanlarını kare şeklinde göstermek için stil dosyamızda şunları yazalım.

ilk/styles.css
.demo {
	width: 100px;
	height: 100px;
	background-color: gray;
	display: inline-block;
	margin: 10px;
}

Sayfa şöyle görünecektir.


Diyelim bu kareleri başka başka renkler yapmak isiyoruz. Bunu class değerlerine başka değerler ekleyerek yapabiliriz. Stil dosyamıza şu stillleri ekleyelim.

ilk/styles.css
...
.red {
	background-color: red;
}

.green {
	background-color: green;
}

.blue {
	background-color: blue;
}

Şimdi birinci kareyi kırmızı yapmak için class değerine ekleme yaparız.

ilk/index.html
...
    <div id="app">
        <div class="demo red"></div>
        <div class="demo"></div>
        <div class="demo"></div>
    </div>
...



Peki karenin rengini üzerine her tıklandığında değiştirmek istersek?

VueJs ile bu işi yapmak için ilk önce bir değişkende kırmızı olup olmadığını saklamak sonra da her tıklandığında bu değişken değerini ters çevirmek gerekiyor.

ilk/main.js
new Vue({
    el: "#app",
    data: {
        kırmızıEkle: false
    }
});

ilk/index.html
...
    <div id="app">
        <div 
            class="demo" 
            @click="kırmızıEkle = !kırmızıEkle;">
        </div>
        <div class="demo"></div>
        <div class="demo"></div>
    </div>
...
Sonra da class değerine dinamik olarak ilave yapmak için v-bind yapısını kullanırız.

ilk/index.html
...
        <div 
            class="demo" 
            @click="kırmızıEkle = !kırmızıEkle;" 
            :class="{ red: kırmızıEkle }">
        </div>
...

Önce de söylediğimiz gibi :class ve v-bind:class aynı şeyi ifade eder. Burada ilave class olarak kırmızıEkle değişken değerine bağlı olarak red değeri girilmesini bir JavaScript kod ile sağlıyoruz. Burada v-bind:class deyimine bir JavaScript nesne gönderince değer true ise key ismi class olarak eklenir. Örnekle anlatsak daha iyi,
...
:class="{ red: kırmızıEkle, blue: maviEkle }"
...

Bunun da farkı yok aynı anlamda.
...
:class="{ 'red': kırmızıEkle, blue: maviEkle }"
...

Burada maviEkle değeri true ise class değerine blue eklenecektir, her iki değer de true ise hem red hem blue eklenecektir. Ayrıca eklenecek değeri tırnak içinde yazmak sadece görsel olarak bir anlam katar, isterseniz öyle kullanın.

Bir kırmızı bir mavi olsun istersek
...
:class="{ red: kırmızıEkle, blue: !kırmızıEkle }"
...

Görsel içinden bu kodu uzaklaştırmak için hesaplanmış değer de kullanabiliriz. Şöyle ki,

ilk/index.html
...
        <div 
            class="demo" 
            @click="kırmızıEkle = !kırmızıEkle;" 
            :class="divClasses">
        </div>
...

ilk/main.js
...
    computed: {
        divClasses: function(){
            return {
                red: this.kırmızıEkle,
                blue: !this.kırmızıEkle
            }
        }
    }
...




İsim kullanarak dinamik stil değişimi


Data kısmında tanımlı bir değişken ismini de direk olarak :class deyimine değer olarak verebiliriz. Bir input ekleyelim ve orada hangi class değeri yazıyorsa o 3. kareye eklensin.

ilk/index.html
...
    <div id="app">
        <div 
            class="demo" 
            @click="kırmızıEkle = !kırmızıEkle;" 
            :class="divClasses">
        </div>
        <div class="demo"></div>
        <div class="demo" :class="renk"></div>
        <hr>
        <input type="text" v-model="renk">
    </div>
...

ilk/main.js
...
    data: {
        kırmızıEkle: false,
        renk: "green"
    },
...

Şimdi input'a ne yazarsak o değer 3. kareye class olarak eklenecektir.


class değeri için array içinde kombinasyon da yapabiliriz. Mesela
<div class="demo" :class="[renk, { red: kırmızıEkle }]"></div>

Tabi bunu yaptık da burada bir CSS çakışması olacak. Stil dosyamızda green ve blue seçicileri daha sonra geldiği için red class değeri eklesek bile kırmızı olmayacaktır. Önce input içindeki değeri silip sonra 1. kareye tıklarsak 3. karenin de onunla beraber kırmızı yada gri olduğunu görürüz.




class olmadan stili dinamik olarak değiştirmek


Başa dönelim ve class olmadan stile müdahale etmek için bu sefer :style kelimesini kullanalım.

ilk/index.html
...
    <div id="app">
        <div class="demo"></div>
        <div class="demo"></div>
        <div class="demo"></div>
        <hr>
        <input type="text" v-model="renk">
    </div>
...

ilk/main.js
new Vue({
    el: "#app",
    data: {
        renk: "green"
    }
});

:style kelimesi kullanarak direk olarak arkaplan rengine şöyle müdahale edebiliriz.

ilk/index.html
...
,        <div class="demo" :style="{ 'background-color': renk }"></div>
        <div class="demo"></div>
        <div class="demo"></div>
...

background-color yazısını tırnak içine aldık çünkü arasında tire işareti olan bir isim JavaScript'te kabul edilmez. Ama tırnak olmadan yazmak için bir kolaylık olarak Camel Case denen teknikle şöyle yazarsak da VueJs anlayacaktır.
...
        <div class="demo" :style="{ backgroundColor: renk }"></div>
...


input kutusuna yazdığımız renk kareye arkaplan rengi olarak gelecektir. #0ff gibi sayı ile de renk verebiliriz, kutuya yazıp deneyin.

Stilimizde çok fazla değer olacaksa hesaplanmış değer kullanarak stilleri JavaScript kodumuz içinde daha düzenli görünecek şekilde verebiliriz.

ilk/index.html
...
    <div id="app">
        <div class="demo" :style="{ backgroundColor: renk }"></div>
        <div class="demo" :style="stilim"></div>
        <div class="demo"></div>
        <hr>
        <input type="text" v-model="renk">
        <input type="text" v-model="width">
    </div>
...

ilk/main.js
...
    data: {
        renk: "green",
        width: 100
    },
    computed: {
        stilim: function(){
            return {
                backgroundColor: this.renk,
                width: this.width + "px"
            };
        }
    }
...





Array ile stil vermek


:style değerlerini de bir array içinde kombinasyon yapabiliriz. 3. kutuya da bunu yapalım.
...
        <div class="demo" :style="[stilim, { height: width + 'px' }]"></div>
...


DOM etkileşimleri bölümü burada bitiyor. Çok şey gördük masallah. Görsellerin baya tozunu attık. Şimdi yeni bir bölüme geçiyoruz.







Koşullar ve Listeler


Bu bölümde görsellerin yayınlanmasında koşulları ve listeleri kullanarak yapabileceklerimizi göreceğiz.


v-if ile koşula göre yayınlama


Artık yeni bir klasöre önceki dosyaları kopyalayıp baştan bu bölüme başlayalım. Daha önce ilk adında bir klasörde yaptıklarımızı toplamıştık, bir kopyasını oluşturup bu klasöre de koşullar adını verelim de bir değişiklik gibi olsun. Hep aynı klasörde silip silip yazdıkça sanki bir şey yapmıyormuşuz gibi geliyor. Başlangıç olarak görselimize scriptimize şunları yazalım.

koşullar/index.html
<!DOCTYPE html>
<html>
<head>
    <title>VueJs Koşullar</title>
    <script src="vue.js"></script>
</head>
<body>
    <div id="app">
        <h3>Beni görebilirsin!</h3>
        <h3>Beni de görebilir misin?</h3>
    </div>

    <script src="main.js"></script>
</body>
</html>

koşullar/main.js
new Vue({
    el: "#app",
    data: {
        göster: true
    }
});

Uygulamayı çalıştırdığımızda iki satır yazı görünecektir. Bazen bir kısım görseli belli koşullara bağlı olarak göstermek ya da gizlemek isteyebiliriz. Örneğin bir hata mesajı, bir değeri değiştirmek için yanında input açmak, belli koşullara göre değişik işler yapan butonları göstermek ya da yok etmek gibi.

Örnek olarak burada ilk satırın göster değişkeni değerine göre görünmesini istiyorsak,

koşullar/index.html
...
    <div id="app">
        <h3 v-if="göster">Beni görebilirsin!</h3>
        <h3>Beni de görebilir misin?</h3>
        <button @click="göster = !göster;">Değiştir</button>
    </div>
...

v-if VueJs kelimesine verilen göster değeri true ise ilk satır görünür. En alta da bir buton ekleyerek göster değişkeni değerini değiştirip test edebiliriz.


Örnek resim göster değişken değeri false iken olan sayfa görünümünü veriyor. Dikkatinizi çekmek istediğim bir nokta var. Geliştirici araçları penceresinde elemanları incelersek her iki satır da görünürken bir div ve içinde 2 tane h3 elemanı olacaktır. Ama butona tıkladığımızda elemanları şöyle görürüz.


<!----> şeklinde boş bir HTML yorum elemanı olarak görünen şey aslında VueJs'in bize burada bir şeyler vardı ama ben onları bir sebepten dolayı uçurdum mesajıdır.

Önemli bir konu da v-if elemanı görünür veya görünmez yapmıyor. Komple sayfaya ekliyor ya da DOM listesinden elemanı yok ediyor. Hani bazen elemanı stilinde display: none yaparak görünmez yaparız ama hala orada duruyordur. Bu öyle değil tamamen eleman DOM'dan çıkarılıyor.

v-if olur da tamamlayıcısı v-else olmaz mı? Görsele bir satır daha ekleyelim.

koşullar/index.html
...
    <div id="app">
        <h3 v-if="göster">Beni görebilirsin!</h3>
        <h3 v-else>Şimdi de beni görebilirsin!</h3>
        <h3>Beni de görebilir misin?</h3>
        <button @click="göster = !göster;">Değiştir</button>
    </div>
...

v-else kendinden önce yazılan en son v-if ile verilen koşul geçersiz ise çalışır.



Alternatif v-if satırı kullanmak


Yeri gelmişken bahsedelim. eğer HTML kod içinde template tag kullanırsak bu elemanlar listesinde görünmeyecektir. Örneğin,

koşullar/index.html
...
    <div id="app">
        <h3 v-if="göster">Beni görebilirsin!</h3>
        <h3 v-else>Şimdi de beni görebilirsin!</h3>
        <template>
            <h3>Template içi yazı</h3>
        </template>
...


template kullanarak bir çok elemanı başka bir görsel eleman içine koymadan bir çok elemanı tek bir v-if ile görünmez yapabiliriz. Mesela
...
        <template v-if="göster">
            <h1>Template içi başlık</h1>
            <h3>Template içi yazı</h3>
        </template>
...



v-show kullanarak gizlemek


v-show kullanarak v-if gibi elemanı tamamen DOM'dan çıkarmadan sadece stilini değiştirerek görünmez yapabiliriz. Ama eleman hala orada olacaktır.

koşullar/index.html
...
    <div id="app">
        <h3 v-if="göster">Beni görebilirsin!</h3>
        <h3 v-else>Şimdi de beni görebilirsin!</h3>
        <h3 v-show="göster">Beni de görebilir misin?</h3>
        <button @click="göster = !göster;">Değiştir</button>
    </div>
...


style="display: none;" ile eleman görünmez yapılmış. Ama hala DOM içinde duruyor. Programımızda bir yerlerde eleman görünmez olsa da hala DOM elemanı olarak etkileşimde bulunmamız gerekiyorsa v-if yerine v-show kullanmak gerekir.



v-for kullanarak listeleri göstermek


Sayfamızı boşaltarak başlayalım.

koşullar/index.html
...
    <div id="app">

    </div>
...

koşullar/main.js
new Vue({
    el: "#app",
    data: {
        yiyecekler: ["Et", "Meyve", "Kurabiye"],
        kişiler: [
            {isim: "Ümit", yaş: 55, renk: "red"},
            {isim: "Alp", yaş: "bilinmiyor", renk: "blue"}
        ]
    }
});

Bu sefer görselimiz boş ama bir sürü verimiz var. Bir yiyecekler listesi, bir kişiler listesi ki her bir elemanı bir obje oluyor. Niye böyle başladık? Çünkü listelerle görsel oluşturmayı göreceğiz.

v-for kullanarak yiyecekler listesini yayınlayalım,

koşullar/index.html
...
    <div id="app">
        <ul>
            <li v-for="yiyecek in yiyecekler">{{ yiyecek }}</li>
        </ul>
    </div>
...

v-for yapısı bir array içindeki her eleman için birer kez çalışır. Burada yiyecek in yiyecekler şekli ile yiyecekler listesindeki her değer için bir li elemanı üretilir. Her turda o andaki değer yiyecek adında geçici bir değişkene yüklenir.


Liste değerleri yanında index değerlerini de alıp kullanabiliriz.

koşullar/index.html
...
            <li v-for="(yiyecek, index) in yiyecekler">
                {{ yiyecek }} ({{ index }})
            </li>
...


Tabi bu index değeri sadece böyle rakam olarak göstermek için alınmıyor. İleride daha gelişmiş kodlar yazarken bize çok faydalı olabilecek bir özellik. Parantez içinde ilk yazılan, elemanın değerinin konacağı değişken adı, ikinci ise index değerinin konacağı değişken adı olmalıdır.




Alternatif v-for deyimi


Daha önce v-if deyiminde gördüğümüz gibi burada da template elemanı kullanarak birden fazla elemandan oluşan bir grup yayınlaması yapabiliriz.

koşullar/index.html
...
        <template v-for="(yiyecek, index) in yiyecekler">
            <h2>{{ yiyecek }}</h2>
            <p>{{ index }}</p>
        </template>
...




Nesneler üzerinden döngü yapmak


Nesneler üzerinden de v-for döngüsü yapılabilir.

koşullar/index.html
...
    <div id="app">
        <ul>
            <li v-for="kişi in kişiler">{{ kişi.isim }}</li>
        </ul>
    </div>
...


Bunda pek matah bir şey göremedik. Ama bildiğimiz gibi burada kişi değişkeni bir nesne ve v-for kullanarak onun da elemanları üzerinden döngü yapabiliriz.

koşullar/index.html
...
        <ul>
            <li v-for="kişi in kişiler">
                <span v-for="değer in kişi">{{ değer }} </span>
            </li>
        </ul>
...


array içinde döngü yaparken index değerini aldığmız gibi nesne üzerinde döngü yaparken bu sefer key değerini alabiliriz.

koşullar/index.html
...
        <ul>
            <li v-for="kişi in kişiler">
                <div v-for="(değer, key) in kişi">{{ key }}: {{ değer }}</div>
            </li>
        </ul>
...


Yetmedi nesne içinde de index değerini alabiliriz.
...
                <div v-for="(değer, key, index) in kişi">{{ key }}: {{ değer }} ({{ index }})</div>
...




Sayı dizisi üzerinde çevrim


Tamamen değişkenlerden bağımsız olarak sayılar üzerinden de çevrim yapabiliriz.
        <span v-for="n in 10">{{ n-1 }}</span>





Bir Oyun Yazalım


İlk önce app adında bir yeni klasör yapalım ve içine stiller için css adında bir alt klasör ekleyelim. CSS dosyası olarak 2 dosya var bu klasörde biri kendi yazdığımız stilller için app.css dosyası, biri de yerleşimle uğraşmamak adına hazır kullanılan foundation.css kütüphanesi. Bu kütüphaneyi buradan benim drive dosyalarımdan indirebilirsiniz.

Uygulama klasörümüzde index.html dosyamıza şunları yazalım.

app/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Yaratık Katili</title>
    <script src="vue.js"></script>
    <link rel="stylesheet" href="css/foundation.min.css">
    <link rel="stylesheet" href="css/app.css">
</head>
<body>
    <div id="app">
        <section class="row">
            <div class="small-6 columns">
                <h1 class="yazı-ortada">SEN</h1>
                <div class="sağlıkbar">
                    <div class="sağlıkbar yazı-ortada" style="background-color: green; margin: 0; color: white;">
                        
                    </div>
                </div>
            </div>
            <div class="small-6 columns">
                <h1 class="yazı-ortada">YARATIK</h1>
                <div class="sağlıkbar">
                    <div class="sağlıkbar yazı-ortada" style="background-color: green; margin: 0; color: white;">
                        
                    </div>
                </div>
            </div>
        </section>
        <section class="row butonlar">
            <div class="small-12 columns">
                <button id="oyun-başlat">YENİ OYUN BAŞLAT</button>
            </div>
        </section>
        <section class="row butonlar">
            <div class="small-12 columns">
                <button id="saldır">SALDIR</button>
                <button id="özel-saldırı">ÖZEL SALDIRI</button>
                <button id="iyileş">İYİLEŞ</button>
                <button id="vazgeç">VAZGEÇ</button>
            </div>
        </section>
        <section class="row kayıtlar">
            <div class="small-12 columns">
                <ul>
                    <li>
                        
                    </li>
                </ul>
            </div>
        </section>
    </div>

    <script src="main.js"></script>
</body>
</html>

css klasörü içindeki app.css dosyamıza da şu stillleri yazalım.

app/css/app.css
.yazı-ortada {
	text-align: center;
}

.sağlıkbar {
	width: 80%;
	height: 40px;
	background-color: #eee;
	margin: auto;
	transition: width 500ms;
}

.butonlar, .kayıtlar {
	margin-top: 30px;
	text-align: center;
	padding: 10px;
	border: 1px solid #ccc;
	box-shadow: 0px 3px 6px #ccc;
}

.turn {
	margin-top: 20px;
	margin-bottom: 20px;
	font-weight: bold;
	font-size: 22px;
}

.kayıtlar ul {
	list-style: none;
	font-weight: bold;
	text-transform: uppercase;
}

.kayıtlar ul .player-turn {
	color: blue;
	background-color: #e4e8ff;
}

.kayıtlar ul .monster-turn {
	color: red;
	background-color: #ffc0c1;
}

button {
	font-size: 20px;
	background-color: #eee;
	padding: 12px;
	margin: 10px;
	box-shadow: 0 1px 1px black;
}

#oyun-başlat {
	background-color: #aaffb0;
}

#oyun-başlat:hover {
	background-color: #76ff7e;
}

#saldır {
	background-color: #ff7367;
}

#saldır:hover {
	background-color: #ff3f43;
}

#özel-saldırı {
	background-color: #ffaf4f;
}

#özel-saldırı:hover {
	background-color: #ff9a2b;
}

#iyileş {
	background-color: #aaffb0;
}

#iyileş:hover {
	background-color: #76ff7e;
}

#vazgeç {
	background-color: #ffffff;
}

#vazgeç:hover {
	background-color: #c7c7c7;
}

body {
	padding: 5px;
}




Script dosyamızda önce temel Vue oluşum nesnesi ile başlayalım.

app/main.js
new Vue({
    el: "#app",
});

Data bölümünde neler olacak? Bir bakışta gördüğümüz oyuncunun ve yaratığın sağlık seviyeleri.

app/main.js
new Vue({
    el: "#app",
    data: {
    	oyuncuSağlık: 100,
    	yaratıkSağlık: 100
    }
});

Hem butonların gösterimi hem de oyun kontrolü için oyunun çalışmakta olduğuna dair bir veri de olsa iyi olur.

app/main.js
...
    data: {
    	oyuncuSağlık: 100,
    	yaratıkSağlık: 100,
    	çalışıyor: false
    }
...

Sağlık göstergelerinden başlayalım. Genişliğin orantılı olarak değişmesi ve üzerinde sağlık değerinin yazması için. HTML görseli şöyle yaparız.

app/index.html
...
        <section class="row">
            <div class="small-6 columns">
                <h1 class="yazı-ortada">SEN</h1>
                <div class="sağlıkbar">
                    <div class="sağlıkbar yazı-ortada" 
                        style="background-color: green; margin: 0; color: white;" 
                        :style="{ width: oyuncuSağlık + '%' }">
                        {{ oyuncuSağlık }}
                    </div>
                </div>
            </div>
            <div class="small-6 columns">
                <h1 class="yazı-ortada">YARATIK</h1>
                <div class="sağlıkbar">
                    <div class="sağlıkbar yazı-ortada" 
                        style="background-color: green; margin: 0; color: white;" 
                        :style="{ width: yaratıkSağlık + '%' }">
                        {{ yaratıkSağlık }}
                    </div>
                </div>
            </div>
        </section>
...

Artık sağlık değerleri değiştikçe yeşil barların üzerindeki yazı ve genişlikleri otomatik olarak değişecek.




Koşullara göre butonları göstermek


Buton gruplarımızı section tag'leri içine koyduk. Bunların hangisinin görüneceğini v-if yönlendiricisini kullanarak belirleriz. çalışıyor değeri false iken YENİ OYUN BAŞLAT butonunun olduğu section görünür olacak, çalışıyor değeri true iken diğer oyun oynamasına ait butonların bulunduğu section görünür olacak.

app/index.html
...
        <section class="row butonlar" v-if="!çalışıyor">
            <div class="small-12 columns">
                <button id="oyun-başlat">YENİ OYUN BAŞLAT</button>
            </div>
        </section>
        <section class="row butonlar" v-if="çalışıyor">
            <div class="small-12 columns">
                <button id="saldır">SALDIR</button>
                <button id="özel-saldırı">ÖZEL SALDIRI</button>
                <button id="iyileş">İYİLEŞ</button>
                <button id="vazgeç">VAZGEÇ</button>
            </div>
        </section>
...

İkinci v-if yerine v-else kullanarak kestirmeden gidebilirdik ama böyle daha açıklayıcı oldu bence. Artık uygulamayı yenileyince sadece YENİ OYUN BAŞLAT butonu görünecektir.


Tabii ki buton henüz hiç bir iş yapmıyor.




Oyunu Başlat metodu


Önce buton tıklama olayına metodu bağlayarak başlayalım.

app/index.html
...
        <section class="row butonlar" v-if="!çalışıyor">
            <div class="small-12 columns">
                <button id="oyun-başlat" @click="oyunuBaşlat()">YENİ OYUN BAŞLAT</button>
            </div>
        </section>
...

oyunuBaşlat metodu içinde neler olacak? İlk akla gelen çalışıyor değeri true olacak ve oyun oynama butonları görünür olacak. Ayrıca yeni bir oyuna başlarken oyuncu ve yaratık sağlık değerlerini %100'den başlatmalıyız. Bu doğrultuda JavaScript kodumuza şunları ekleriz.

app/main.js
...
    },
    methods: {
    	oyunuBaşlat: function(){
    		this.çalışıyor = true;
    		this.oyuncuSağlık = 100;
    		this.yaratıkSağlık = 100;
    	}
    }
...

Sayfayı yenileyip butona tıkladığımızda artık oyun oynama butonları görünecektir.


Oyun artık çalışmaya başladı. Şimdi bu butonlara bir kodlar yazarak tarafların birbirini dövmelerini sağlamalıyız.



saldır metodunun yazılması


Dört oyun butonu için tıklama olaylarına metod isimleri tanımlayarak başlayalım.

app/index.html
...
                <button id="saldır" @click="saldır()">SALDIR</button>
                <button id="özel-saldırı" @click="özelSaldırı()">ÖZEL SALDIRI</button>
                <button id="iyileş" @click="iyileş()">İYİLEŞ</button>
                <button id="vazgeç" @click="vazgeç()">VAZGEÇ</button>
...

Bu 4 metodu içleri boş olarak JavaScript'imize ekleyelim.

app/main.js
...
    methods: {
    	oyunuBaşlat: function(){
    		this.çalışıyor = true;
    		this.oyuncuSağlık = 100;
    		this.yaratıkSağlık = 100;
    	},
    	saldır: function(){

    	},
    	özelSaldırı: function(){

    	},
    	iyileş: function(){

    	},
    	vazgeç: function(){
    		
    	}
    }
...

Biraz mantığı düşünelim. vazgeç metodu hariç diğerlerinde yaratık oyuncuya zarar verir. saldır metodunda oyuncu da yaratığa zarar verir. özelSaldırı metodunda oyuncu yaratığa daha fazla zarar verir. iyileş metodunda oyuncu sağlığı yükselirken yaratık zarar vermeye devam eder. vazgeç metodunda ise oyuncuya yeni oyun başlatma isteği sorulacak.

saldır metoduyla başlayalım. İlk yapılacak şey karşıya verilecek zararların hesaplanması. Bu zarar alt ve üst sınırlarını vereceğimiz bir rastgele değer olmalı.

app/main.js
...
    	saldır: function(){
    		var max = 10;
    		var min = 3;
    		var zarar = Math.max(Math.floor(Math.random() * max) + 1, min);
    	},
...

Math.random fonksiyonu 0 ile 0.99999.. arasında rastgele bir sayı üretir. Bunu max değeri olan 10 ile çarparsak 0 ile 9.99.. arası bir sayı elde ederiz. Math.floor fonksiyonu ise bu değerin sadece tamsayı kısmını bize verir. Böylece 0-9 arası bir tamsayı elde ederiz. Buna 1 ekleyerek 1-10 arası bir tamsayı elde ederiz. Math.max fonksiyonu ise parametresinde verilen 2 değerden hangisi büyükse onu geri döner. Bu durumda rastgele sayımız 1 ya da 2 çıkarsa min değeri olan 3 geri dönerek en az 3 değeri çıkması sağlanır.

Bu rastgele hesaplanan değerle yaratığa zarar verelim. Daha sonra benzer şekilde oyuncuya zarar veren bir rastgele miktar da hesaplayalım ama bu biraz daha fazla olsun, ne de olsa yaratık vuruyor.

app/main.js
...
    	saldır: function(){
    		var max = 10;
    		var min = 3;
    		var zarar = Math.max(Math.floor(Math.random() * max) + 1, min);
    		this.yaratıkSağlık -= zarar;

    		max = 12;
    		min = 5;
    		zarar = Math.max(Math.floor(Math.random() * max) + 1, min);
    		this.oyuncuSağlık -= zarar;
    	},
...

Aynı kodları 2 defa tekrarladık DRY tekniğine (Don't Repeat Yourself) uymadı düzenlemek lazım. Ama önce bir çalıştıralım. Uygulamayı yenilediğimizde artık SALDIR butonuna tıkladıkça her iki tarafa da rastgele zararlar vermeye başladı. Ama işin sonu yok. Butona basmaya devam ettikçe sağlık değeri eksiye düşse bile devam ediyor. Bunun kontrol edilip sağlık değeri eksiye düşenin ölmesini sağlamalıyız.

app/main.js
...
saldır: function(){
    var max = 10;
    var min = 3;
    var zarar = Math.max(Math.floor(Math.random() * max) + 1, min);
    this.yaratıkSağlık -= zarar;

    if (this.yaratıkSağlık <= 0){
        alert("Sen Kazandın!");
        this.çalışıyor = false;
        return;
    }

    max = 12;
    min = 5;
    zarar = Math.max(Math.floor(Math.random() * max) + 1, min);
    this.oyuncuSağlık -= zarar;

    if (this.oyuncuSağlık <= 0){
        alert("Kaybettin!");
        this.çalışıyor = false;
        return;
    }
...

Yaratık sağlığını kontrol edip eğer sıfır ya da altında bir değerse bir mesaj ile oyuncunun kazandığını bildiriyoruz. Sonra çalışıyor değerini false yaparak YENİ OYUN BAŞLAT butonunun görünmesini sağlıyoruz. return ile fonksiyonu yarıda kesip çıkmasını sağlıyoruz. Çünkü kod aşağı devam edip oyuncuya da bir zarar verir ve oyuncu sağlığı da sıfır ya da altına düşerse iki tarafında kazandığı ya da kaybettiği bir durum ortaya çıkacak.


Ama olmaz ki yaratık çok zarar veriyor hep oyuncu kaybediyor. Oyuncunun kabiliyetlerini arttırmak lazım.

Şimdi Refactor zamanı kodumuzu biraz düzenleyelim, fazla dağılmadan.

Önce zarar hesabını bir fonksiyona atalım,

app/main.js
...
    methods: {
...
        saldır: function(){
            this.yaratıkSağlık -= this.zararHesapla(3, 10);

            if (this.yaratıkSağlık <= 0){
                alert("Sen Kazandın!");
                this.çalışıyor = false;
                return;
            }

            this.oyuncuSağlık -= this.zararHesapla(5, 12);

            if (this.oyuncuSağlık <= 0){
                alert("Kaybettin!");
                this.çalışıyor = false;
                return;
            }
        },
...
        zararHesapla: function(min, max){
            return Math.max(Math.floor(Math.random() * max) + 1, min);
        }
    }
...

Şimdi de oyunun bittip bitmediğini başka bir metod ile belirleyelim. Bu kontrol diğer butonlarda da lazım olacak, tekrar tekrar yazmayalım. Bir de ilave yapalım ve oyuncuya sonucu bildirirken yeni bir oyun isteyip istemediğini soralım.

app/main.js
...
        saldır: function(){
            this.yaratıkSağlık -= this.zararHesapla(3, 10);
            if (this.kazananKontrolü()){
                return;
            }

            this.oyuncuSağlık -= this.zararHesapla(5, 12);
            this.kazananKontrolü();
        },
 ...
       kazananKontrolü: function(){
            if (this.yaratıkSağlık <= 0){
                if (confirm("Sen Kazandın! Yeni Oyun?")){
                    this.oyunuBaşlat();
                } else {
                    this.çalışıyor = false;
                }
                return true;
            } else if (this.oyuncuSağlık <= 0){
                if (confirm("Kaybettin! Yeni Oyun?")){
                    this.oyunuBaşlat();
                } else {
                    this.çalışıyor = false;
                }
                return true;
            }
            return false;
        }
...


confirm ile bir soru penceresi açılır ve kullanıcı burada Tamam butonuna tıklarsa geriye true değeri döner. Kullanıcı Tamam butonunu tıklarsa zaten daha önce yazmış olduğumuz oyunuBaşlat metodunu çağırarak yeni bir oyun başlatıyoruz. İptal ederse çalışıyor bilgisini false yaparak YENİ OYUN BAŞLAT butonunun görünmesini sağlıyoruz.




Özel Saldırı metodunu yazalım


Bu metod içinde saldır metodunda yapılanları yapacağız ama oyuncunun yarattığı zararın daha büyük olmasını sağlamak için sınırlarını büyüteceğiz.

app/main.js
...
        özelSaldırı: function(){
            this.yaratıkSağlık -= this.zararHesapla(10, 20);
            if (this.kazananKontrolü()){
                return;
            }

            this.oyuncuSağlık -= this.zararHesapla(5, 12);
            this.kazananKontrolü();
        },
...

Yaratığın oyuncuya verdiği zararı aynı tuttuk. Burada da bir kod kopyalaması oldu. Yaratığın verdiği zarar kısmı aynı olduğu için bir metodda toplayalım.

app/main.js
...
        saldır: function(){
            this.yaratıkSağlık -= this.zararHesapla(3, 10);
            if (this.kazananKontrolü()){
                return;
            }

            this.yaratıkSaldırır();
        },
        özelSaldırı: function(){
            this.yaratıkSağlık -= this.zararHesapla(10, 20);
            if (this.kazananKontrolü()){
                return;
            }

            this.yaratıkSaldırır();
        },
...
        yaratıkSaldırır: function(){
            this.oyuncuSağlık -= this.zararHesapla(5, 12);
            this.kazananKontrolü();
        }
...

Oyuncu artık ÖZEL SALDIRI butonuna basarak yaratığa çok daha fazla zarar verebilir. Böyle olunca da hep oyuncu kazanıyor rahatça. Ben kendi versiyonumda bu butona 5 turda sadece 1 kez tıklama izni vermiştim mesela.




iyileş metodu yazılması


Zarar görmenin rastgele değerlerle olması aksine iyileşmede %10 bir sağlık artışı sağlayacağız. Bu arada yaratık da saldırmaya ve zarar vermeye devam ediyor olacak. 

app/main.js
...
        iyileş: function(){
            this.oyuncuSağlık += 10;
            this.yaratıkSaldırır();
        },
...
İyileşme işlemi de tamam gibi. Ancak bu butona istediğimiz zaman basabiliyoruz. Bu durumda sağlığımız tamken de bassak değer artmaya devam ediyor.


app/main.js
...
        iyileş: function(){
            if (this.oyuncuSağlık <= 90){
                this.oyuncuSağlık += 10;
            } else {
                this.oyuncuSağlık = 100;
            }
            this.yaratıkSaldırır();
        },
...

Oki doki, bunu da hallettik.

Dikkat ettiniz mi, VueJs nasıl da bize sağladığı imkanlar ile kodumuzu hem kolay hakim olunabilir hem de parça parça parça adımlarla çok daha kısa kodlarla yapmamızı sağlıyor. Bu kod salt JavaScript ile de yazılırdı ama DOM elemanları ve değerlerini güncellemeler ile o kadar uğraşırdık ki kodumuz çorbaya dönerdi. Halbuki VueJs'in bize sağladığı imkanlar ile biz sadece değişken değerleri ile ilgileniyoruz DOM elemanları ile etkileşimi ise sadece başlangıçta bir tarif ettik gerisini VueJs yapıyor.



Aksiyon butonlarımızı bitirelim


Aslında vazgeç metodu içinde yapılacak iş sadece çalışıyor değerinin false yapılması ama butona kazara basılırsa diye bir confirm ile oyuncudan emin olmasını isteyebiliriz. 

app/main.js
...
        vazgeç: function(){
            if (confirm("Oyundan Çıkmak İstiyor musun?"))
                this.çalışıyor = false;
        },
...


Bu projenin son bulması için yapılacak tek şey kaldı, aşağıdaki bölüme mesajlar yazarak oyuncuyu bilgilendirmek.




Aksiyon log yazılımı


Bu amaçla ilk önce mesajları içeren bir array tanımını data bölümüne ekleriz ve buna yapacağımız ilaveler ile mesajlar gösterilir. 

app/main.js
...
    data: {
        oyuncuSağlık: 100,
        yaratıkSağlık: 100,
        çalışıyor: false,
        mesajlar: []
    },
...

Oyuncu saldırdığında onun saldırdığını ve verdiği zararın miktarını mesajlar içine eklememiz gerekiyor. saldır metodunu şöyle değiştirelim,

app/main.js
...
        saldır: function(){
        	var zarar = this.zararHesapla(3, 10);
            this.yaratıkSağlık -= zarar;
            this.mesajlar.unshift({
            	oyuncuMu: true,
            	yazı: "Oyuncu yaratığa " + zarar + " zarar verdi"
            });
            if (this.kazananKontrolü()){
                return;
            }

            this.yaratıkSaldırır();
        },
...

yaratıkSaldırır metodu içinde de yaratığın oyuncuya verdiği zararı kaydetmeliyiz.

app/main.js
...
        yaratıkSaldırır: function(){
        	var zarar = this.zararHesapla(5, 12);
            this.oyuncuSağlık -= zarar;
            this.kazananKontrolü();
            this.mesajlar.unshift({
            	oyuncuMu: false,
            	yazı: "Yaratık oyuncuya " + zarar + " zarar verdi"
            });
        }
...

oyuncuMu değeri burada false olarak bu mesajın oyuncunun değil yaratığın verdiği zarara ait olduğunu gösteriyor. Bu bilgiyi görselde renklendirmek için kullanacağız. unshift ile array'in en başına eleman eklenir böylece gösterimde en başa en son mesaj gelecek.

Mesajları topladık, sıra geldi görselde bunları göstermeye.




v-for ile görsele mesajları listelemek


index.html dosyamız içinde bu yazıları koymak için bir unordered list koyduk. Basit bir şekilde mesajları v-for döngüsü ile şöyle yayınlayabiliriz. 

app/index.html
...
                <ul>
                    <li v-for="mesaj in mesajlar">
                        {{ mesaj.yazı }}
                    </li>
                </ul>
...



Mesajlar gelmeye başladı. Yalnız özel saldırı için mesaj eklemeyi unutmuşuz. Şimdi buna biraz stil katalım. Önce mesaj yoksa bu section elemanını göstermeyerek başlayalım.

app/index.html
...
        <section class="row kayıtlar" v-if="mesajlar.length">
            <div class="small-12 columns">
                <ul>
                    <li v-for="mesaj in mesajlar">
                        {{ mesaj.yazı }}
                    </li>
                </ul>
            </div>
        </section>
...

mesajlar array'i uzunluğu sıfırsa yani mesaj yoksa bu section görünmeyecektir.




Mesaj kayıtlarını bitirelim


Önce özelSaldırı metoduna mesaj ekleyerek başlayalım,

app/main.js
...
        özelSaldırı: function(){
        	var zarar = this.zararHesapla(10, 20);
            this.yaratıkSağlık -= zarar;
            this.mesajlar.unshift({
            	oyuncuMu: true,
            	yazı: "Oyuncu yaratığa " + zarar + " büyük zarar verdi"
            });
            if (this.kazananKontrolü()){
                return;
            }

            this.yaratıkSaldırır();
        },
...

iyileş metodu içine de iyileşme miktarını belirten mesaj eklemeliyiz.

app/main.js
...
        iyileş: function(){
            if (this.oyuncuSağlık <= 90){
                this.oyuncuSağlık += 10;
            } else {
                this.oyuncuSağlık = 100;
            }
            this.mesajlar.unshift({
            	oyuncuMu: true,
            	yazı: "Oyuncu 10 birim iyileşti"
            });
            this.yaratıkSaldırır();
        },
...

ayrıca yeni oyun başlarken mesajları da boşaltmamız gerekir.

app/main.js
...
        oyunuBaşlat: function(){
            this.çalışıyor = true;
            this.oyuncuSağlık = 100;
            this.yaratıkSağlık = 100;
            this.mesajlar = [];
        },
...

Sıra geldi mesajları renklendirmeye.




Mesajları koşula göre renklendirme


CSS dosyamızda player-turn ve monster-turn adında 2 stil mevcut. Bunları kullanacağız. 

app/index.html
...
        <li v-for="mesaj in mesajlar" 
            :class="{ 'player-turn': mesaj.oyuncuMu, 'monster-turn': !mesaj.oyuncuMu }">
            {{ mesaj.yazı }}
        </li>
...



Evet önceki yazımız burada bitiyor. Gördüğümüz gibi daha önce web uygulaması olarak yaptıklarımızı şimdi masaüstü olarak yaptık, her iki şekilde de çalışıyor. Masaüstü uygulama geliştirme için alternatif olarak kullanabiliriz. Bir dahaki yazıda biraz daha masaüstü uygulamaya benzer şeyler yapmak ve VueJs versiyon 3 kullanmak istiyorum.

Hadi kalın sağlıcakla..












Hiç yorum yok:

Yorum Gönder