Vue.js Direktifleri (html, text, once, for)
VueJS ve Vue.js Interpolations (Ara Değerler) başlıklı yazıların adından direktifler (directives) ile devam ediyoruz.
Direktifler, v-
ön ekine sahip özel niteliklerdir (attributes) ve görevi DOM’a özelleştirilmiş davranışlar uygulatmaktır. Hemen bir örnek ile direktifin işleyişini netleştirelim. Bir önceki yazıda, ara değerler ile ilgili anlatımda data
içeriğinden edindiğimiz key
ve value
için başta mustache olmak üzere bazı template sistemlerinin yazım kurallarından da bildiğimiz süslü parantezler {{}}
kullanmıştık; {{ message }}
gibi. Hatta v-text
ve v-html
ile de değer değişiminden, hatta v-for
ile döngü konusundan özet bir şekilde bahsetmiştik. Burada bahsi geçen v-text
, v-html
ve v-for
Vue.js tarafından sunulan direktiflerden sadece birkaçı. Evet, yazının konusu da tam olarak bu; Vue.js 2.x bağlamında sunulan direktifler.
Vue.js Direktifleri (Directives) ve Kullanımları
Önceki yazılarda ve bu yazının giriş paragrafında da bahsi geçtiği üzere, direktifler, Vue.js şablonlarında (template) HTML attribute (nitelik/özellik) olarak kullanılan ve DOM‘a bind, update ve unbind gibi reaktif (reactive) davranışlar (behavior) atayabilmekteyiz.
Direktifler, ayrıca çeşitli argümanlar (argument) ve düzenleyiciler (modifier) alabilirler. İlgili direktif başlığı altında detaylarına değineceğim. Angular’a aşina iseniz direktiflerin işleyişini de muhtemelen biliyorsunuzdur. Bu arada, etiket dediğime bakmayın, kullanımı bu şekilde olabilir ancak arka planda her direktif global ve/veya local olarak çeşitli işlemler yürütmektedir. Evet, istediğimiz taktirde biz de kendi direktiflerimizi oluşturabilmekteyiz, lakin bu bir başka yazının konusu. Öncelikle, Vue.js 2.x itibariyle (önceki sürümlerde kullanılan bazı direktifler artık desteklenmemekte) ön tanımlı olarak sunulan direktifleri bir listeleyelim. İlerleyen süreçte, Vue.js güncellemeleriyle birlikte eklenen / değişen direktifler olursa yine bu tablo üzerinden aktarmaya çalışacağım.
Vue.js Directives
v-text | v-html | v-once | v-for |
v-show | v-if / v-else-if / v-else | v-on | v-bind |
v-model | v-pre | v-cloak | v-slot |
Önceki örneklerden de aşina olduğumuz üzere v-text
ile başlayalım. Örnekler için kullanacağımız Vue instance şöyle:
// Vue v2.x
var app = new Vue({
el: '#app',
data: {
message: 'Hello World!',
private: false,
number: 10,
user: {
firstName: 'Julius',
lastName: 'Rodman'
}
},
filters: {
uppercase(value) {
// veya uppercase: function(value) {
return value.toUpperCase()
}
},
methods: {
uppercase(value) {
return value.toUpperCase()
},
uppercase2(value) {
return this.$options.filters.uppercase(value);
}
}
});
// Vue v3.x
// v3.x ile birlikte filtreler kullanımdan kaldırıldı.
const app = Vue.createApp({
data() {
return {
message: 'Hello World!',
private: false,
number: 10,
user: {
firstName: 'Julius',
lastName: 'Rodman'
}
}
},
methods: {
uppercase(value) {
return value.toUpperCase()
},
uppercase2(value) {
return this.$options.filters.uppercase(value);
}
}
}).mount('#app');
Evet, ilk bakışta karışık görünüyor, ancak panik yok, satırları-işlemleri aşama aşama anlatacağım.
v-text, v-html, v-once ve v-for
Bu yazıda örnekleri geniş tutabilmek adına v-text
(declarative binding), v-html
(declarative binding), v-once
(one-time binding) ve v-for
direktiflerine değineceğim. v-show
, v-if / v-else-if / v-else
, v-on
, v-bind
için Vue.js Direktifleri – 2 ve v-model
, v-pre
, v-cloak
, v-slot
için ise Vue.js Direktifleri – 3 başlıklı yazılarla devam edebilirsiniz.
v-text
v-text
direktifi içine aldığı ifadeyi (data içerisinde tanımladığımız key ya da bir method olabilir) attribute (nitelik/özellik) olarak dahil olduğu HTML etiketi arasına yazar. Özetle, document.getElementById("element").textContent;
karşılığına sahiptir. {{...}}
ifadeleri gibi değerlendirilebilir. Ancak, içeriğinde doğrudan işlem barındıramaz. Bu tür gereksinimler için mustaches ve methods gibi özellikleri kullanırız. Aşağıda birkaç sıralı örnek ve bu örneklerin çıktılarını görsel olarak iletiyorum.
İlgili koda GitLab > Vue-Examples > Example-4 üzerinden de ulaşabilirsiniz. Gelelim açıklamalara.
Hello {{ user.firstName + ' ' + user.lastName }}
Yukarıdaki örnek basit bir mustaches kullanımı ile user
içeriğinde tanımlı firstName
ve lastName
değerlerini bize bir araya getirerek (user.firstName + ' ' + user.lastName
) sunmakta. Herhangi bir başka özelliği yok. Kapsayıcısı olan div etiketiyle bir ilişkisi yok.
Hello {{ user.firstName }} {{ user.lastName | uppercase }}
Bu kullanımda firstName
ve lastName
ayrı mustahces içerisinde kullanılmakta. {{ user.firstName }} {{ user.lastName }}
şeklinde kullansaydık bir önceki örnekten hiçbir farkı olmayacaktı. Ancak, lastName
pipe ile bir filtreyle ilişkilendirilmiş. Vue instance içerisinde göreceğiniz üzere filters
altında bir uppercase
filtresi oluşturulmuş. Filtrenin görevi kendisine iletilen değeri (value) toUpperCase()
ile büyük harflere dönüştürmek. uppercase
adında bir de method tanımlı. Aklınız karışmasın, filter ile ikisi ayrı tanımlamalar. İsim benzerliğini kasıtlı olarak tercih ettim. Birazdan konuyu açıklayacağım.
Hello {{ user.firstName + ' ' + user.lastName | uppercase }}
İlk 3 örneğin bir birleşimi olarak pipe ile değerin tamamı filtreye atanmakta ve bunun sonucu olarak da tüm değerler büyük harfli olarak iletilmekte.
{{ 'Hello ' + user.firstName + ' ' + user.lastName }}
Yine basit bir örnek olarak, önceki örneklerde dışarıda tuttuğumuz Hello
ifadesini de mustaches içerisine ekledik. Sonuçta bir değişiklik olmadı. Peki, bir ödev sorusu. Bu mustaches içerisinde bir filter kullansaydık ({{ 'Hello ' + user.firstName + ' ' + user.lastName | uppercase }}
) nasıl bir sonuç alırdık?
<div v-text="'Hello ' + user.firstName + ' ' + user.lastName"></div>
Mustaches yerine v-text
ile örneğimizi yineleyelim. Görüldüğü üzere Hello
ifadesi user.firstName + ' ' + user.lastName
ile birlikte v-text
içeriğinde yer almakta ve çıktı olarak sunulmakta.
Hello <span v-text="user.firstName"></span> <span v-text="user.lastName"></span>
firstName
ve lastName
yine mustaches örneğinde olduğu gibi 2 parça halinde span
etiketleri içerisinde yer almakta ve bize bir önceki örnek ile aynı sonucu getirmekte.
Hello <span v-for="who in user" v-text="who"></span>
Bu örnekte v-text
bir sonraki yazıda detaylıca değineceğim v-for
direktifi ile birlikte kullanılmakta. Vue instance‘da da görüldüğü üzere user
adında bir nesnemiz var. v-for
ile user içeriği (user.firstName ve user.lastName) döngü halinde who
değişkenine aktarılmakta. Ardından, v-text
who
değerini span
etiketi içerisine yerleştirmekte.
Hello <span v-for="who in user" :text-content.prop="who | uppercase"></span>
Bu örneğe pek sık rastlamayabilirsiniz, pek tavsiye edilen bir yöntem de olmayabilir. Vue.js
dokümantasyonunda filtre kullanımı mustaches ile birlikte önerilmekte. Neden olmasın? sorusunun cevabı olarak paylaştığım bu örnekte yine v-for
ile edindiğimiz değer :text-content.prop
ile (prop konusuna ayrı bir yazıda değineceğim) yine HTML etiketi içerisine metin olarak (text-content) aktarılmakta. :text-content
esasında v-bind:text-content
olarak da yazılabilir. İşlevi v-text
direktifini açıklarken de bahsettiğim document.getElementById("element").textContent;
müdahalesini gerçekleştirmektir. Bu sayede pipe ile filtrelemeyi bir arada kullanmış olmaktayız. Benzer bir sonuca v-bind:inner-html.prop
ile de ulaşabiliriz.
Hello <span v-for="who in user" v-text="uppercase(who)"></span>
Yukarıdaki ilk filtre kullanım örneğinde de bahsi geçtiği üzere uppercase adında bir filtremiz bir de metotumuz var. v-text
direktifi içerisinde filtre kullanılmadığı için (az önceki örnek bir istisna yöntem barındırmakta) Vue.js uppercase
‘i filtrelerde aramayacaktır. Bu nedenle de bir karışıklık ortaya çıkmamakta. Örnek, işleme alındığı anda uppercase()
metotuna ulaşmakta ve içerdiği değere toUpperCase()
uygulayıp döndürmekte.
Hello <span v-for="who in user" v-text="uppercase2(who)"></span>
uppercase2()
yine bir önceki örnekteki gibi benzer bir sürece sahip. Ancak, içeriği uppercase()
metotundan biraz farklı. Çünkü, this.$options.filters.uppercase
ile aslında uppercase
filtresine ulaşmaktayız. Evet, v-text
içerisinde normal şartlarda filtre kullanamıyor olabiliriz. Ancak, metotlarla filtrelere erişerek işlemler yapabiliriz.
Hello <span v-for="who in user" v-text="$options.filters.uppercase(who)"></span>
Son örnekte metot içerisinde gerçekleştirdiğimiz filtre erişimini doğrudan v-text
içeriğinde yapmaktayız. $options
hem bu hem de bir önceki örnekte kritik öneme sahip. Console üzerinden Vue instance içeriğine verdiğimiz variable üzerinden erişebileceğine Vue Nedir? başlıklı yazıda değinmiştim; var app = new Vue()
.
Bu sayede app.$options
ile instance
içeriklerine (components, directives, filters, methods…) ulaşabilmekteyiz. Console alanına app.$options
yazarak nelere erişebileceğimize bir göz atmanızı öneririm.
v-html
document.getElementById('element').innerHTML
v-text
yeteneklerine ek olarak kod içeriğinde HTML etiketleri kullanmamızı mümkün kılmakta. Dolayısıyla yukarıdaki örnekleri v-text
yerine v-html
yazarak ve value olarak HTML etiketleri ekleyecek yineleyebilirsiniz. Unutmayın, v-html
kullanıcı tarafından sağlanan içerikler sebebiyle Cross-Site Scripting (XSS) saldırısına için zemin oluşturabilir.
v-once
v-text
, v-html
ya da mustaches, değeri nasıl HTML etiketleri arasına uyguladığınız önemli değil. Önemli olan v-once
kullanımında, belirtilen direktifin reaktif özelliğini kaybedecek olması. Sayfa açık iken v-once
ile işaretlenen değişkene yeni değer atanmış olması önemli değil, içeriği değişmeyecektir. v-once
sayesinde değer bir defa gösterilir ve bir daha güncellen(e)mez.
Yukarıdaki örnekleri şu şekilde güncelleyelim.
<div v-once="user">Hello {{ user.firstName + ' ' + user.lastName | uppercase }}</div>
<div>Hello <span v-for="who in user" v-text="who" v-once="who"></span></div>
<div>Hello <span v-for="who in user" v-text="uppercase(who)" v-once="user"></span></div>
Console üzerinden veya Vue instance içerisinde user.firstName
ve/veya user.lastName
için yeni değerler ilettiğinizde ilgili alanların güncellenmediğini görebilirsiniz.
v-for
v-for
bir listeleme direktifi (repeater) olarak kullanılmaktadır. Obje/dizi içeriklerini sırayla belirtilen alana atar ve x in y
şeklinde ifade edilir. Örneğin, v-for="msg in message
. Yukarıdaki örneklerde görüldüğü üzere user
içeriği sırasıyla who
‘ya aktarılmaktadır. Aşağıdaki örnekte ise message
içeriğinde farklı dillerde Selam ifadeleri yer alır ve yine sırasıyla msg
‘ye aktarılarak uygulanır.
<!DOCTYPE html>
<html>
<head>
<title>Vue Example #1</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="msg in message" v-bind:title="msg">{{ msg }}</li>
</ul>
</div>
<script src="https://vuejs.org/js/vue.js"></script>
<script>
// Vue v2.x
var app = new Vue({
el: '#app',
data: {
message: ['Selam', 'Hello', 'Tungjatjeta', 'Salam', 'Вiтаю', 'Hola', 'Hallo', 'Tere']
}
});
// Vue v3.x
const app = Vue.createApp({
data() {
return {
message: ['Selam', 'Hello', 'Tungjatjeta', 'Salam', 'Вiтаю', 'Hola', 'Hallo', 'Tere']
}
}
}).mount('#app');
</script>
</body>
</html>
Örneği biraz daha geliştirmek adına, v-for
dışında v-bind
direktifine de yer verdim. {{ msg }}
kullanımını v-text
ile de gerçekleştirebilirdik. Bu durumda ilgili satırı şu şekilde güncellememiz yeterli olacaktır.
<li v-for="msg in message" v-bind:title="msg" v-text="msg"></li>
Yapılan değişiklikle, li
etiketine v-text
direktifini ekledik ve içeriğine msg
ile edindiğimiz değerin yazılmasını istedik. Bu direktifle birlikte Vue.js li
textContent‘ini Vue instance‘deki msg
property ile değiştirdi. Aynı işlemi v-html
direktifi ile de gerçekleştirebiliriz.
<li v-for="msg in message" v-bind:title="msg" v-html="msg"></li>
Bu arada, v-for="msg in message"
kullanımında değerleri msg
‘ye atamak yerine, doğrudan value
ve key
de çağırabiliriz; v-for="(value, key) in message"
. Aşağıdaki örneklere göz atabilirsiniz.
<!-- #1 -->
<ul>
<li v-for="(value, index) in message" v-bind:title="value">{{ index }}: {{ value }}</li>
</ul>
<!-- #2 -->
<ul>
<li v-for="(value, index) in user" v-bind:title="value">{{ index }}: {{ value }}</li>
</ul>
<!-- #3 -->
<ul>
<li v-for="(value, index) in users" v-bind:title="value">{{ index }}: {{ value }}</li>
</ul>
<!-- #4 -->
<ul>
<li v-for="(value, index) in users" v-bind:title="value.firstName" :key="index">{{ index }}: {{ value.firstName }} {{ value.lastName }}</li>
</ul>
<!-- #5 -->
<ul>
<li v-for="(value, index) in users" v-bind:title="value" :key="index">{{ index }}: <span v-for="val in value" v-bind:title="val">{{ val }} / </span></li>
</ul>
<!-- #6 -->
<ul>
<li v-for="(value, index) in users" v-bind:title="value">{{ index }}: <span v-for="val in value.firstName">{{ val }} / </span></li>
</ul>
<!-- #7 -->
<ul>
<li v-for="(value, index) in users" v-bind:title="value.firstName" :key="index">{{ index }}: {{ value.firstName }} {{ value.lastName }}</li>
</ul>
Sonuç Olarak
v-text
, v-html
direktifleri çok komplike olmayan özellikleri oldukça pratik bir şekilde icra etmektedirler. Bu direktifler yerine genelde mustaches kullanımı tercih edilmektedir. v-once
komponent kullanımında yer bulan ve reaktif işlemlerin dışarıda tutulması istenen durumlarda sıklıkla kullanılabilmektedir. v-for
ise en çok kullanılan direktiflerden biri ve v-if
, v-show
, v-on
v-bind
gibi direktiflerle özel ilişkiler oluşturabilmekte. İlerleyen yazılarda bu özelliklerden detaylıca söz edeceğim.