18 Nisan 2021 Pazar

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

 Merhaba ,


Önceki yazıda NwJs kuruluşunu göstermiştik. Bu sefer VueJs versiyon 3 kullanalım.  https://unpkg.com/vue@next adresinden VueJs versiyon 3 indirebiliriz. Başlamak için helloWorld adında yeni bir klasör oluşturup içine indirdiğimiz vue.global.js dosyasını koyalım. Sonra da package.json , index.html , main.js isminde yeni dosyalar ekleyelim.

helloWorld/package.json
{
    "name": "helloWorld",
    "main": "index.html"
}
helloWorld/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Merhaba Dünya!</title>
    <script src="vue.global.js"></script>
</head>
<body>
    <h1>Merhaba Dünya!</h1>
    <script src="main.js"></script>
</body>
</html>

Şimdilik main.js dosyamızı boş bırakıp bir çalışmasını deneyelim. Klasör içinde nw . komut satırı çalıştıracak bir kısa yol ekleyelim.


Şimdi kısayolu tıklayıp uygulamayı çalıştıralım.


Şimdi aynı mesajı VueJs yardımıyla verecek şekil düzenleyip ona da start verelim.

helloWorld/index.html
<!DOCTYPE html>
<html>
<head>
    <title>Merhaba Dünya!</title>
    <script src="vue.global.js"></script>
</head>
<body>
    <div id="app">
        <h1>{{ mesaj }}</h1>
    </div>
    <script src="main.js"></script>
</body>
</html>
helloWorld/main.js
const App = {
    data() {
        return {
            mesaj: "Merhaba Dünya!"
        }
    }
}

Vue.createApp(App).mount("#app");

Aynı sonucu aldıysak herşey hazır demektir. Şimdi klasörümüzün bir kopyasını oluşturup adına da test diyelim ve her iki JavaScript platformun da özelliklerini öğrenmeye devam edelim. 

test/index.html
<!DOCTYPE html>
<html>
<head>
    <title>VueJs-NwJs Öğreniyorum</title>
    <script src="vue.global.js"></script>
</head>
<body>
    <div id="app">
        <h1>{{ sayaç }}</h1>
    </div>
    <script src="main.js"></script>
</body>
</html>

test/main.js
const App = {
    data() {
        return {
            sayaç: 0
        }
    },
    mounted() {
    	setInterval(() => {
    		this.sayaç++;
    	}, 1000);
    }
}

Vue.createApp(App).mount("#app");

Önce yaptığımız uygulamada sadece bir değişken değerini gösterdik. Ama VueJs bu kadar değil tabi değişken değeri kodumuz içinde değiştikçe gösterilen değer de reaktif olarak değişir. Bu örneğimizde setInterval fonksiyonu kullanılarak her 1000 ms'de bir sayaç değeri bir arttırılıyor. Uygulamayı çalıştırırsak sayfa yenilemesi yapmadan değerin otomatik olarak değiştiğini görürüz. mounted bloğunda verilen script Vue uygulaması sayfada monte edilince çalışmaya başlar. 







İçerik Menüsü


Uygulamamızda bir elemana sağ tıklayınca popup şeklinde bir menü açmak için contextmenu olayı ve nw.Menu nesnesi kullanabiliriz. Önce görsele bir paragraf elemanı ekleyerek başlayalım. 

test/index.html
...
    <div id="app">
        <h1>{{ sayaç }}</h1>
        <p @contextmenu="popup($event)">Bu yazıya sağ tıklayarak içerik menüsü açabilirsiniz</p>
    </div>
...

contextmenu olayına yani bu elemana sağ tıklama olayına popup adında bir fonksiyon bağladık. Açılacak menüyü oluşturmak için JavaScript kodumuzun en sonuna şunları ekleyelim

test/main.js
...
Vue.createApp(App).mount("#app");

var menu = new nw.Menu();
menu.append(new nw.MenuItem({
	label: "Eleman A",
	click: () => {
		alert("'Eleman A menüsüne' tıkladın");
	}
}));
menu.append(new nw.MenuItem({ label: "Eleman B" }));
menu.append(new nw.MenuItem({ type: "separator" }));

Önce menu adında bir nesne oluşturup bunu nw.Menu nesnesi olarak tanımlıyoruz. Sadece bu satırla bıraksak sağ tıkladığımızda içinde boş yazan bir popup açılır. Sonra sırayla menü elemanlarını nw.MenuItem nesneleri olarak ekliyoruz. İlk satır için label yani yazı Eleman A olacak. Ayrıca bu menü elemanına tıklanırsa çalışmasını istediğimiz fonksiyonu da hemen oracıkta click özelliğinde tanıtıyoruz. İkinci menü elemanı yazısı Eleman B ve şimdilik tıklanınca hiç bir şey olmuyor. Son olarak da type değeri "separator" olan bir eleman ekliyoruz ki bu sadece yatay ayırıcı bir çizgi olacak. Şimdi de bu menünün açılabilmesi için Vue nesnesinin methods bölümüne popup metodunu ekleyelim.

test/main.js
...
    mounted() {
    	setInterval(() => {
    		this.sayaç++;
    	}, 1000);
    },
    methods: {
        popup(olay) {
            olay.preventDefault();
            menu.popup(olay.x, olay.y);
            return false;
        }
    }
...

Uygulamanın meşhur "Uygulamayı Yeniden Yükle" ile başlayan menüsünü bu paragraf için devre dışı bırakmak amacıyla preventDefault kullanıyoruz. Daha sonra menu nesnesini tıklanan yerin x ve y koordinatlarında popup metodunu çağırarak gösteriyoruz. 






Dışarıdaki Kod ile Vue Değişkenine Ulaşmak


Diyelim menüde ikinci satırı değiştirip sayaç değişkenini sıfırlamak istiyoruz. JavaScript kodumuzu şöyle değiştiririz.

test/main.js
...
const app = Vue.createApp(App).mount("#app");

var menu = new nw.Menu();
menu.append(new nw.MenuItem({
    label: "Eleman A",
    click: () => {
        alert("'Eleman A menüsüne' tıkladın");
    }
}));
menu.append(new nw.MenuItem({ 
    label: "Şu sayacı sıfırla",
    click: () => {
        app.$data.sayaç = 0;
    }
}));
menu.append(new nw.MenuItem({ type: "separator" }));

Önce monte ettiğimiz Vue nesnesini app adında bir değişkene bağlıyoruz ki içinde dışarıdan erişebilelim. Sonra da menünün tıklama fonksiyonunda bu değişkenin $data özelliği yardımı ile Vue nesnesi içindeki sayaç değerine müdahale edebiliyoruz. 



NodeJs Dahili Modüllerini Kullanmak


NodeJs modüllerini direk JavaScript kodumuz içinde kullanabiliriz. npm kullanarak kurulan modüller içinse daha özel bir yöntemle modülü kurarak kullanabiliriz. Burada mevcut modüllerden olan os modülünü deneyelim. 

test/index.html
...
    <div id="app">
        <h1>{{ sayaç }}</h1>
        <p @contextmenu="popup($event)">Bu yazıya sağ tıklayarak içerik menüsü açabilirsiniz</p>
        <h4>{{ platform }} işletim platformunda çalışıyoruz</h4>
    </div>
...

test/main.js
...
    data() {
        return {
            sayaç: 0,
            platform: "bilinmiyor"
        }
    },
    mounted() {
        setInterval(() => {
            this.sayaç++;
        }, 1000);
        var os = require('os');
        this.platform = os.platform();
    },
...






Menü Bar Oluşturmak


Uygulamamıza standart menü bar oluşturmak için içerik menüsü ile aynı yapıyı kullanıyoruz. JavaScript kodumuzun sonuna şunları ekleyelim.

test/main.js
...
var menubar = new nw.Menu({ type: "menubar" });
var fileMenu = new nw.Menu();
fileMenu.append(new nw.MenuItem({ label: "Yeni" }));
fileMenu.append(new nw.MenuItem({ label: "Çıkış" }));

menubar.append(new nw.MenuItem({
    label: "Dosya",
    submenu: fileMenu
}));

nw.Window.get().menu = menubar;

fileMenu adında bir menü başlığı oluşturup altına Yeni ve Çıkış etiketlerine sahip 2 menü elemanı ekliyoruz. Sonra bu fileMenu başlığı altında topladığımız grubu Dosya etiket yazısıyla menubar'ımıza ekliyoruz. Son olarak da elde ettiğimiz menubar'ı uygulama penceremize menu olarak tanıtıyoruz.










Küçük Bir ToDo Uygulaması


VueJs sitesinde verilen örneklerden ToDoMVC uygulamasını masaüstü olarak gerçekleştirelim. Veri depolama olarak da harddiskte bir text dosyası kullanalım. todo adında yeni bir klasör oluşturalım, içine temel dosyalarımızı oluşturalım ya da direk test klasöründekileri kopyalayalım. Ucundan görseli oluşturalım

todo/index.html
<!DOCTYPE html>
<html>
<head>
    <title>İşler Güçler</title>
    <script src="vue.global.js"></script>
    <link rel="stylesheet" type="text/css" href="bootstrap.min.css">
</head>
<body>
    <div id="todo-app">
    	<div class="container">
	        <h1 class="text-center text-danger">İşler Güçler</h1>
	        <input class="form-control" 
	        	style="font-size: 22px;" 
	        	autofocus 
	        	autocomplete="off" 
	        	placeholder="Ne yapman gerekiyor" 
	        	v-model="newTodo" 
	        	@keyup.enter="addTodo()">
	    </div>
    </div>
    <script src="main.js"></script>
</body>
</html>
Hazır stiller kullanmak için Bootstrap.css indirdim ve kullandım. https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css adresinden indirilen bootstrap.min.css dosyasını buraya kopyaladım. 

Görsele şimdilik bir başlık yazısı ve bir input elemanı ekliyoruz. Bir yerden başlamak lazım. Ben adım adım gitmeyi severim. input elemanını newTodo adında bir değişkene bağlıyoruz ve içine bir şeyler girilip enter basılınca addTodo adında bir metod çağırılmasını belirtiyoruz. JavaScript dosyamızda da temeli bir atalım.

todo/main.js
const App = {
    data() {
        return {
            newTodo: ""
        }
    },
    mounted() {
    },
    methods: {
        addTodo() {
            console.log("Yeni iş: " + this.newTodo);
            return false;
        }
    }
}

const app = Vue.createApp(App).mount("#todo-app");

var win = nw.Window.get();
win.resizeTo(400, 600);

newTodo metodunda şimdilik sadece konsola metoda gelindiğini gösterir bir kod yazdık. Yeni bir komut da uygulama penceresinin boyutunu değiştiren resizeTo fonksiyonunu kullanmamız oldu. 





Verileri Dosyadan Okumak


Yapılacaklar listesini saklamak için uygulama klasöründe bulunan todos.json adında bir dosyayı kullanalım. Bu dosya içine örnek 1-2 değer girerek uygulama başlarken okumasını yapalım.

todo/todos.json
[
    {
        "id": 0,
        "title": "Test Todo",
        "completed": false 
    },
    {
        "id": 1,
        "title": "Test Todo 2",
        "completed": true 
    }    
]

todo/main.js
...
    data() {
        return {
            newTodo: "",
            todos: this.readTodos()
        }
    },
    methods: {
        addTodo() {
            console.log("Yeni iş: " + this.newTodo);
            return false;
        },
        readTodos() {
            'use strict';
            this.todos = require("todos.json");
            console.log(this.todos);
        }
    }
...

Vue nesnemize todos adında bir değişken ekleyip dosyadaki değerleri bu değişken içine okuyoruz. Okuma işlemi için dosya sistemine ait kod kullanmak yerine require kullanarak kestirme yoldan dosyanın tüm içeriğini todos değişkeni içine aldık. 'use strict' kodu ise JavaScript'e koddaki ufak tefek hataları düzeltmeye çalışmayıp direk hata vermesi için bir yönlendirme. Bu satırı koymasanız da okuma gerçekleşir, öneren kişi böyle yapmış diye koydum oraya. Aslında hata yapacak bir şey yok dosyamızın yeri garanti, herşey kontrol altında.

İkinci bir konu, sadece uygulama başlarken bir kez okuma yapacağım ve daha sonra sadece yazma işlemi yapılacak. Bu yüzden require ile alabiliriz. Yoksa require tekrar tekrar okuma yapmak için bir komut değildir, sadece bir kez okuma yapar ve getirdiği bilgiyi hafızasında saklar. 

Tabi burada okuma işlemini görmek için konsola yazdırdık. Ama JavaScript kodumuzda dikkat edersek biraz hata var. readTodos metodunun geriye bir değer dönmesi gerekiyor ki todos değişkeni içine bu dönen değer konularak uygulama başlasın. 

todo/main.js
...
        readTodos() {
            'use strict';
            var todos = require("todos.json");
            return todos;
        }
...

Artık Geliştirici Araçları konsolda app.todos yazıp enter basarsak dosyadan okuna değerleri göreceğiz.


Artık elimizde bir iş listesi var. Bu listeyi görselimize ekleyelim de konsola sorup durmayalım. 

todo/index.html
<!DOCTYPE html>
<html>
<head>
    <title>İşler Güçler</title>
    <script src="vue.global.js"></script>
    <link rel="stylesheet" type="text/css" href="bootstrap.min.css">
    <style>
        [v-cloak] {
            display: none;
        }
    </style>
</head>
<body>
    <div id="todo-app">
        <div class="container">
            <h1 class="text-center text-danger display-3">İşler Güçler</h1>
            <input class="form-control" 
                style="font-size: 22px;" 
                autofocus 
                autocomplete="off" 
                placeholder="Ne yapman gerekiyor" 
                v-model="newTodo" 
                @keyup.enter="addTodo()">
        </div>
        <br>
        <footer class="container bg-dark" v-show="todos.length" v-cloak>
            <ul class="list-group">
                <li
                    v-for="todo in todos" 
                    class="list-group-item" 
                    :key="todo.id" 
                >
                    <label class="h4">{{ todo.title }}</label>
                </li>
             </ul>
        </footer>
    </div>
    <script src="main.js"></script>
</body>
</html>

Nereden başlayalım? v-cloak yönlendiricisi Vue nesnesi oluşana kadar çok kısa bir süre {{ todo.title }} gibi işlenmemiş kod görünüyor ya, işte onun görünmesini engelliyor. Tek başına işe yaramaz bir de header bölümünde kendisine atanan stil ile birlikte kullanılıyor dikkat edersek. v-for kullanarak todos değişkeninde bulunan listeyi ekrana yazıyoruz. :key daha sonra listeden elemanı silmek amaçlı kullanılabilir. 




Yeni Girilen İşi Listeye Eklemek


Yeni girilen işi sadece konsola yazıp orada bırakmıştık. Şimdi onu todos değişkenine ekleyelim. 

todo/main.js
...
        addTodo() {
            var iş = this.newTodo && this.newTodo.trim();
            if(!iş){
                return;
            }
            var maxId = 0;
            for(x in this.todos){
                if(this.todos[x].id > maxId)
                    maxId = this.todos[x].id;
            }
            this.todos.push({
                id: (maxId+1),
                title: iş,
                completed: false
            });
            this.newTodo = "";
        },
...

Önce girilen iş var mı, yok mu kontrolü ve boşluklarından temizlenmesi yapılıyor. Sonra listedeki maximum id değeri bulunuyor. Sonra da bir sonraki id değeri ile girilen iş listeye ekleniyor. completed özelliği false yapılarak işin daha bitmediği belirtiliyor.

Artık input elemanına yeni bir iş girip enter basınca girilen iş listede görünmeye başladı. Ancak bu yeterli değil, iş listesinin son halini todos.json dosyasına da kaydetmeliyiz ki uygulamayı kapatıp açınca eski liste geri gelsin. 

Kayıt için yazacağımız kod uygulamamızda başka yerlerde de kullanılacak, mesela işin bittiğini işaretlerken, işi listeden sildiğimizde vb. Bu yüzden biz bir ortak rutin yazalım, todos listesi her değiştiğinde onu diskteki dosyaya kaydetsin.


todo/main.js
...
    data() {
        return {
            newTodo: "",
            todos: this.readTodos()
        }
    },
    watch: {
        todos: {
            handler: (todos) => {
                'use strict';
                const fs = require('fs');
                var liste = JSON.stringify(todos, null, 2);
                fs.writeFileSync("todos.json", liste);
            },
            deep: true
        }
    },
...



watch özelliği sayesinde VueJs nesnesinin bir değişken değerinde oynama olunca bir fonksiyon çalıştırmasını belirtiyoruz. Bu fonksiyon handler özelliğinde yazılıyor ya da çağırılıyor. deep: true özelliği ile de array içindeki elemanlara da değişim için izleme yapılması isteniyor. 

Kayıt işleri için Node FileSystem modülünü kullanıyoruz. JSON.stringify fonksiyonu ile todos değişkeni dosyaya kayıt edilecek şekilde string yapıya dönüştürüyor. Diğer 2 parametre olmazsa boşluksuz şekilde bir JSON stringi oluşturur, ama bu parametreler ile todos.json dosyasını editörde açtığımızda anlayabileceğimiz bir görüntü olur. writeFileSync fonksiyonu ise FileSystem modülünün dosyaya yazmak için kullandığı metod. 




Bir İşi Listeden Silmek


OK. Yeni iş ekledik ve iş listesini dosyaya kaydettik. CRUD işinden Create tamam Read tamam, sırada Delete var (Kolaydan başlayıp gidiyorum). Görselde listemize bir silme butonu ekleyerek başlayalım.

todo/index.html
...
                <li
                    v-for="todo in todos" 
                    class="list-group-item" 
                    :key="todo.id" 
                >
                    <div class="clearfix">
                        <label class="h4">{{ todo.title }}</label>
                        <button 
                            class="btn-small btn-danger float-right" 
                            @click="removeTodo(todo)"
                            >X</button>
                    </div>
                </li>
...


removeTodo fonksiyonunu da VueJs nesnemizin metodlarına ekleyelim.

todo/main.js
...
        removeTodo(todo) {
            this.todos.splice(this.todos.indexOf(todo), 1);
        }
...




Biten İşi İşaretlemek


İşi düzenlemeye girmeden önce biten işlerin completed özelliğine true değeri vererek bittiğini işaretleyelim ve bitenleri listede farklı gösterelim. Zaten todos.json dosyasına girdiğimiz örnek değerlerden birinin completed özelliği true değere sahipti. Listede gösterimden başlayalım. 

todo/index.html
...
    <style>
        [v-cloak] {
            display: none;
        }
        .list-group-item .completed {
            text-decoration: line-through;
            color: lightgray;
        }
    </style>
...

Önce completed adında bir class stili tanımlıyoruz. Yazının üstü çizili olacak ve açık renk olacak. Şimdi label elemanına completed değerine bağlı olarak bu class değerini ekleyelim.

...
<label class="h4" :class="{ completed: todo.completed }">{{ todo.title }}</label>
...


Biten işleri işaretlemek için de bir checkbox eleman ekleyelim görselimize. 

todo/index.html
...
            <div class="clearfix">
                <input type="checkbox" v-model="todo.completed">
                <label class="h4" :class="{ completed: todo.completed }">{{ todo.title }}</label>
                <button 
                    class="btn-small btn-danger float-right" 
                    @click="removeTodo(todo)"
                    >X</button>
            </div>
...

checkbox'ları daha yakışıklı göstermek için iki satır stil ekleyelim yukarıya.

todo/index.html
...
        input[type=checkbox]{
            transform: scale(2);
            margin-right: 20px;
        }
    </style>
...





İş Listesini Düzenlemek


Geldik CRUD yapısının en son işine Update yani kaydı değiştirmek. Daha önce aslında kaydın özelliklerinden bir tanesinde değişimi gerçekleştirdik. checkbox elemanını todo.completed değerine bağlayarak, tıklandığında completed özelliğinin değişmesini sağladık. Sırada todo.title değerinin düzenlenmesi var. Bu özellik text olduğuna göre düzenlemek için bir texbox kullanmak gerekiyor. 

todo/index.html
...
                <li
                    v-for="todo in todos" 
                    class="list-group-item" 
                    :key="todo.id" 
                    :class="{ editing: todo == editedTodo }"
                >
                    <div class="clearfix">
                        <input type="checkbox" v-model="todo.completed">
                        <label class="h4" 
                            :class="{ completed: todo.completed }" 
                            @dblclick="editTodo(todo)"
                        >{{ todo.title }}</label>
                        <button 
                            class="btn-small btn-danger float-right" 
                            @click="removeTodo(todo)"
                            >X</button>
                    </div>
                    <input type="text" 
                        class="edit" 
                        v-model="todo.title" 
                        v-todo-focus="todo == editedTodo" 
                        @blur="doneEdit(todo)" 
                        @keyup.enter="doneEdit(todo)" 
                        @keyup.esc="cancelEdit(todo)" 
                        >
                </li>
...

li elemanının kodunu komple verdim. Dikkat edersek li elemanına da koşullu bir class eklemesi yaptık. Bu editing sınıfı o liste elemanında düzenleme yapıldığını belirtiyor. Kayıtlarda değişecek çok şey varsa ayrı bir form açılır ve kayıt orada düzenlenir. Ama biz burada sadece todo.title değerini düzenlemek istediğimiz için listede görünenin yerine bir input elemanı gösterelim ve değeri yerinde düzenleyelim. 

Düzenleme moduna geçmek için todo.title 'ı gösteren label elemanına çift tıklama yapılsın diye düşündük ve label elemanına @dblclick="editTodo(todo)" özelliğini ekleyerek çift tıklanınca editTodo metodunun çağırılmasını sağladık. 

Yeni eklediğimiz input elemanını direk todo.title değerine v-model ile bağladık. Bunun anlamı şu, bu input elemanı düzenleme modunda görünür olunca yaptığımız her değişiklik anında kayıtı değiştirecek. Bu yüzden düzenlemeyi iptal edebilmek amacıyla bu input elemanını görünür yapmadan önce eski değeri bir yere kaydetmek gerekecek unutmayalım. 

v-todo-focus ise eğer bu input elemanının ait olduğu iş düzenleme modundaysa otomatikman elemana fokuslanmayı sağlamak amacıyla bizim Vue nesnemize ekleyeceğimiz yeni bir yönlendirici. Kodunu az sonra göreceğiz. 

Kursörü input elemanı dışında bir yere tıklayınca ya da enter tuşu basılınca doneEdit metodunu çağırıyoruz. Aslında kayıt zaten değişti de, işte bu normal liste görünümüne dönüşü falan gerçekleştireceğiz. 

esc tuşu basılırsa cancelEdit metodunu çağıracağız ve burada daha önce dediğimiz gibi input elemanında yapılan değişiklikleri iptal edip düzenleme yapılmadan önceki todo.title değerine geri döneceğiz. 

JavaScript koda girmeden önce şu stillleri bir toparlayalım. index.html kodumuzun head kısmında yazdığımız stiller artık çoğaldı, onları oradan alalım ve styles.css adında bir dosya içine toplayalım.

todo/index.html
...
<head>
    <title>İşler Güçler</title>
    <script src="vue.global.js"></script>
    <link rel="stylesheet" type="text/css" href="bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
...

Stil dosyamızın içine daha önce burada olan stilleri taşıdıktan sonra şunları da ekleyelim.

todo/styles.css
...
.list-group li .edit {
    display: none;
}
.list-group li.editing .edit {
    display: block;
    width: calc(100% - 43px);
    padding: 12px 16px;
    margin: 0 0 0 43px;
    font-size: 22px;
}
.list-group li.editing div {
    display: none;
}
...

Önce input elemanımızı görünmez yapıyoruz. Ama içinde bulunduğu li elemanı editing sınıfına geçtiğinde görünür yapıp pencerede şeklini ve yazı boyutunu belirliyoruz. Aynı şekilde li elemanı editing sınıfı olduğunda daha önce hepsini bir div içine topladığımız default görünümü görünmez yapıyoruz. 

Gelelim JavaScript kodumuza yapacağımız ilavelere. 3 tane metod ve bir yönlendirici ekleyeceğiz. Metodların en altına şunları ekleyelim,

todo/main.js
...
    methods: {
...
        editTodo(todo) {
            this.editÖncesiDeğer = todo.title;
            this.editedTodo = todo;
        },
        doneEdit(todo) {
            if (!this.editedTodo) {
                return;
            }
            this.editedTodo = null;
            todo.title = todo.title.trim();
            if (!todo.title) {
                this.removeTodo(todo);
            }
        },
        cancelEdit(todo) {
            this.editedTodo = null;
            todo.title = this.editÖncesiDeğer;
        }
    },
    directives: {
        "todo-focus": (el, binding) => {
            if (binding.value) {
                el.focus();
            }
        }
    }
...

Sıradan gidelim. editTodo listede bir işin etiketine çift tıkladığımızda çağrılan metod. Bu metod önce todo.title değerini editÖncesiDeğer adında bir değişkene saklıyor ki düzenleme iptal edilirse geri dönebilelim. Sonra da editedTodo değişkenini çift tıkladığımız todo ile eşitliyor. Bu sayede görsel kodda olan şartlar gerçekleşecek ve input elemanı görünür olurken div elemanı görünmez olacaktır. 

editedTodo değişkeni görselde de kullanılıyor. Bu değişkeni data bölümüne eklemeliyiz. 

todo/main.js
...
    data() {
        return {
            newTodo: "",
            todos: this.readTodos(),
            editedTodo: null
        }
    },
...

doneEdit metodu editedTodo değeri yoksa hiç bir şey yapmadan geri dönüyor. Ama varsa önce editedTodo değerine null vererek boşaltıyor, sonra todo.title değerini sağıda solundaki boşluklardan temizliyor ve eğer todo.title değerinde bir şey yoksa o işi listeden silip atıyor. 

cancelEdit metodu editedTodo değerine null verip düzenleme modunu iptal ediyor ve todo.title değişkenine işe başlarken yedeğe aldığımız editÖncesiDeğer değişkenindeki değeri atıyor.

directives bölümü kullanıcı tanımlı yönlendiricilerin bulunduğu yer. görselde v-todo-focus diyerek verdiğimiz yönlendirici burada todo-focus adıyla ifade ediliyor. Bu yönlendiriciyi görselde input elemanına todo == editedTodo şartı ile bağlamıştık. Yani o input elemanı düzenleme modundaki li elemanı içindeyse bu yönlendirme çalışacak ve bağlı olduğu input elemanına kursörü fokuslayacaktır. 


Yep, artık program çalışıyor. Birkaç özellik ekleyelim uygulamamıza.




Verileri Filtrelemek


İşleri bitenler ve bitmeyenler olarak filtrelemek istersek, önce gereken filtreleme fonksiyonlarını bir grup olarak tanımlayarak başlayalım. JavaScript dosyamızın başına şu tanımları ekleyelim. 

todo/main.js
var filtreler = {
    tümü: (todos) => {
        return todos;
    },
    aktif: (todos) => {
        return todos.filter((todo) => {
            return !todo.completed;
        });
    },
    biten: (todos) => {
        return todos.filter((todo) => {
            return todo.completed;
        });
    }
}
...

Bu tanımlamaları yaptıktan sonra konsolda mesela filtreler.biten(app.todos) veya filtreler["aktif"](app.todos) yazdığımızda ilgili filtrelemeye göre sonuçların geldiğini görürüz. tümü niye var, todos yazsak aynı değil mi? çünkü biz bir değişken yapacağız, içine tümü yazarsak tümü, aktif yazarsak aktif olanlar vs gelecek. Önce değişkeni ekleyelim.

todo/main.js
...
    data() {
        return {
            newTodo: "",
            todos: this.readTodos(),
            editedTodo: null,
            görünenler: "tümü"
        }
    },
...

Filtrelenmiş sonuçları saklamak için computed bölümünde bir değişken ekleyelim.

todo/main.js
...
    computed: {
        filtrelenmişler() {
            return filtreler[this.görünenler](this.todos);
        }
    },
    watch: {
...

Şimdi de listede bu filtrelenmişleri görüntüleyecek şekilde görseli değiştirelim.

todo/index.html
...
                <li
                    v-for="todo in filtrelenmişler" 
                    class="list-group-item" 
                    :key="todo.id" 
                    :class="{ editing: todo == editedTodo }"
                >
...

Şimdi konsolda mesela app.görünenler = "aktif" girersek listede sadece aktif olanlar görünecektir. Şimdi görselimize bu seçimleri yapmak için butonlar ekleyelim.

todo/index.html
...
        </footer>
        <div class="d-flex justify-content-around p-2">
            <button class="btn btn-sm" @click="görünenler='tümü'" 
                :class="{ 'btn-dark': görünenler == 'tümü' }">Tümü</li>
            <button class="btn btn-sm" @click="görünenler='aktif'" 
                :class="{ 'btn-dark': görünenler == 'aktif' }">Aktif</li>
            <button class="btn btn-sm" @click="görünenler='biten'" 
                :class="{ 'btn-dark': görünenler == 'biten' }">Biten</li>
        </div>
...


Bu kadar. Her ihtimale karşı dosyaları drive'ıma yükledim. Buradan indirebilirsiniz. 









Hiç yorum yok:

Yorum Gönder