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.

AA

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 Directives

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.

[resimage jpg=v-text-directives" uri="vue-js-direktifler-directives" alt="Vue v-text Directives" class="mb-3"]

İ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().

VueJS app.$options

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.