teamdefinex.com

Web Components???

DefineX
TeamDefineX
Published in
5 min readDec 24, 2021

--

Uygulamalarımızı geliştirirken her zaman kod tekrarından kaçınmaya çalışır ve buna dikkat ederiz. Yeni uygulamalar gelecek ve bunu sürdürmeye çalışacağız. Önyüz uygulamaları için, component bazlı çözümler kabul görmüş bir gerçeklik ve farklı arayüz kütüphanelerinin farklı çözümleri mevcut. Peki WebComponent olunca ne oluyor? Webcomponent bizim custom HTML tagleri (<div> <span> gibi) oluşturmamızı sağlıyor. Bunu nasıl yaptığımıza bir göz atalım.

1) Shadowroot

Component özelinde encapsulated bir alan oluşturur. DOM Tree de yer alan style, event gibi yapılardan etkilenmez, bu sayede modüler yapılar oluşturmanın önünü açar.

2) Custom Element

Custom HTML tag leri tanımamızı sağlar. Tanımlama esnasında verdiğimiz tag adı ve ile class ilişkisini kurar, her kullanımda yeni bir instance oluşturur. Örnek kullanım: customElements.define(‘your-class-name”,ClassReference)

3) HTML Template

Componentimiz browser tarafından oluşturulurken ekranda ne göstereceğini, çıktısının ne olacağını belirttiğimiz kısımdır. Daha fazla ayrıntı burada.

Temel olarak webcomponent bu. Yorumlarınız düşünceleriniz olursa da bekliyorum. İlk örneğimizde iki butonu olan ve içinde bir sayısal değer tutan sayaç yapacağız.

Componentimizin son halinde neler var, nasıl görünüyor bir bakalım:

teamdefinex.com

Başlangıçta componentimiz için <template> oluşturuyoruz . Kullanacağımız HTML elementlerini tanımlıyoruz. Ardından classımızı tanımlamaya başlıyoruz. Classımız için “CounterComponent” ismini seçtim. Extend ettiğimiz yer “HTMLElement” olduğunu görüyorsunuz. Böylece standart HTML elementinin üstünde ilerleme kaydedebiliriz.

LifeCycle:

1) Constructor

Başlatıcı adımımız, “Constructor” fonksiyonumuzdur. İlk değer atamalarımız, tanımlarımız burada yer alır. Componentimiz için gerekli ilk tanımlamalar burada yer alır. This.myValue = “First Value” gibi. Burada unutulmaması gereken diğer konu Mixin ve extend edilen classlardan tüm mirası aldığından emin olmaktır. Bunu da “super()” çağrımını ekleyerek tamamlamış oluyoruz. Burada componentimizin shadowDom kullanasına karar verebilir ve HTML parçamızın eklenmesini yapabiliriz.

teamdefinex.com

2) connectedCallback

Componentimizin içinde bir öğe eklenmesi durumunda çağrılan adımdır. Bu adımda document.queryselector ile dom üzerinde elementleri bulabiliriz. Bu sayede eventlistener ekleme işlemleri için uygun bir adım oluyor.

teamdefinex.com

3) disconnectedCallback

Componentimiz dom üzerinden silindiğinde ve ekranda artık çalışmadığında, bu lifecycle adımımız tetiklenir. Bu adımda ekli eventlistener bulunuyorsa bunlardan kurtulmamız gerekir. Temizleme ve kaynakları sıfırlama adına, en uygun yerimiz burasıdır.

teamdefinex.com

4) attributeChangedCallback

Adının hakkını veren lifecycle adımına geldik. Componentimizin üzerine eklenen(örneğimizde observedAttributes) parametrelerde bir değişiklik olması durumunda bu adım tetiklenmekte. 3 parametre ile çağrılır. Bunlar;

1- Parameter adı,

2- Parametrenin eski değeri,

3-Parametrenin yeni değeri.

teamdefinex.com

5) adoptedCallback

Componentimiz içinde bunla ilgili bir örnek yok ama güzel bir şekilde açıklayacağım. Uygulamamız içinde bir kaç iframe olması durumunu düşünelim. Her iframe kendi içinde bir “document” a sahip olacak. Eğer componentimiz sayfa içinde bir yerde ekliyken iframe içine taşınırsa. Bu fonksiyonumuz tetiklenir. Burada şöyle bir önemli nokta var. iframe içine taşınan elementimizin “constructor” adımı çağrılmaz. Bu yüzden yapılması gereken işlemleri burada tekrar etmemiz yada yeri değiştiği ile ilgili işlemleri burada yürütebiliriz.

Componentimiz ilk sayfaya eklenmesi durumundaki adımların sırası:

constructor > connectedCallback

Componentimiz farklı bir iframe altına taşınma işleminin yapılması.( nasıl yapılacağı ile ilgili örnek)

disconnectedCallback > adoptedCallback > connectedCallback

Bu adımlardan sonra componentimizi oluşturmaya başlayabiliriz. Componentimiz bir sayaç componenti olacak “-“,”+” buttonları bulunacak bir de ortalarında değeri gösterdiğimiz alan olacak.

Geliştireceğimiz component burada.

İlk adım olarak component’imizin HTML kısmını oluşturuyoruz.

const template = document.createElement(‘template’);

template.innerHT = `

<style>

button {

width: 50px;

height: 50px;

border: 1px solid orange;

border-radius: 20%;

background: red;

color: white;

font-weight: bold;

cursor: pointer;

}

button:active {

background-color: #D9391C;

border: 1px solid blue;

border-radius: 50%;

}

button:focus {

outline: none;

}

span {

display: inline-block;

margin: 0 5px;

min-width: 25px;

text-align: center;

}

</style>

<slot></slot>

<button id=”increaseBtn”>+</button>

<span id=”label”>0</span>

<button id=”decreaseBtn”>-</button>

`;

Buttonların üst kısmında stiller verilmiş durumda.

Arkasından class tanımımızı ekliyoruz.

export class CounterComponent extends HTMLElement {

Class ismimiz “CounterComponent” ve extend edilen yer gördüğünüz gibi basic “HTMLElement”.

İlk önce class içinde kullanacağım parametreleri tanımlıyorum. Burada takip edeceğim tek parametrem olacak.

static get observedAttributes() {

return [‘value’];

}

Value üzerinde değişiklikler için set-get metotlarını ekliyorum.

get value() {

return this.getAttribute(‘value’);

}

set value(val) {

this.setAttribute(‘value’, val);

}

Böylece value ataması olduğunda yada buttonlar tıklandığında attribute de güncellemesini yapmış olacağım.

Sonrasında lifecycle adımlarımla devam ediyorum.

constructor() {

// prototip veya extend olunan yerlerdeki işlemleri miras alabilmek için eklenir.

super();

// İsteğe bağlı olarak componentinizi shadow dom içine alabilirsiniz

this.attachShadow({ mode: ‘open’ });

this.shadowRoot.appendChild(template.content.cloneNode(true));

// Dom elemenlerini oluşturdugumuz değişkenlere atıyoruz

this.increaseButton = this.shadowRoot.querySelector(‘#increaseBtn’);

this.decreaseButton = this.shadowRoot.querySelector(‘#decreaseBtn’);

this.label = this.shadowRoot.querySelector(‘#label’);

this.value = 0;

}

Burada componentim shadow içinde oluşması için

this.attachShadow({ mode: ‘open’ });

ekledim.

this.shadowRoot.appendChild(template.content.cloneNode(true));

shadowDom içine en başta oluşturduğum HTML kodunu buraya ekledim.

this.increaseButton = this.shadowRoot.querySelector(‘#increaseBtn’);

this.decreaseButton = this.shadowRoot.querySelector(‘#decreaseBtn’);

this.label = this.shadowRoot.querySelector(‘#label’);

this.value = 0;

HTML üzerinde olan button ve label için değişkenlerime atamalarımı yaptım aynı zamanda sayacımın “0” dan başlaması için ilk değer atamamı yaptım.

Diğer lifecycle adımıma geçtiğimde:

connectedCallback() {

// Buttonların click eventlerine dinleyici ekliyoruz

// addEventListener callback fonksiyonuna kendi componentimizi gönderiyoruz bu context üstünde işlem yapabilmesi için

this.increaseButton.addEventListener(‘click’, this._increase.bind(this));

this.decreaseButton.addEventListener(‘click’, this._decrease.bind(this));

}

Dom üzerinde bulunan elementlerime eventlistener ekledim. Button tıklandığında sayacımı azaltma ve arttırma fonksiyonlarımı çağırıyorum.

Ardından;

disconnectedCallback() {

// dinleyicileri siliyoruz yine kendi contextimizi göndererek

this.increaseButton.removeEventListener(‘click’, this._increase.bind(this));

this.decreaseButton.removeEventListener(‘click’, this._decrease.bind(this));

}

Lifcycle da bahsettiğimiz gibi bu adımda HTMLelementimizin ekrandan kaldırıldığı durumlarda eventlistenerlarımızı siliyoruz.

Devamında:

attributeChangedCallback(name, oldValue, newValue) {

this.label.innerHTML = newValue;

}

Value üzerinde bir değişiklik olduğunda dom üzerinde bulunan sayacımızı güncelliyoruz.

Geriye kalan kısımda arttırma ve azaltma fonksiyonlarımızı ekliyoruz.

_increase() {

this.value = parseInt(this.value) + 1;

}

_decrease() {

this.value = this.value — 1;

}

Ve en sonunda class’ımızı kapatıp component tanımımızı ekliyoruz.

}

customElements.define(‘my-parent’, CounterComponent);

Şu anda gördüğünüz gibi componentimizi bir file içinde oluşturduk.

Bunun birde HTML içinde çalıştığını görürsek ilk adım için her şeyi tamamlamış olacağız.

<body>

<my-parent> </my-parent>

<script type=”module” src=”./my-element.js”></script>

</body>

Elementimizin adını ve oluşturduğumuz dosyanın importunu eklediğimize göre artık önümüzde bir engel kalmamış olmalı.

Ekrana gelen ilk hali
“+” tıklamaları sonrası
“-” tıklamaları sonrası

Umarım faydalı bir yazı olmuştur. Deneyimlerinizi benimle paylaşırsanız çok sevinirim.

Emre Çakır, Senior Developer @TeamDefineX

www.teamdefinex.com

emre.cakir@teamdefinex.com

--

--

DefineX
TeamDefineX

We provide insights for leaders of digital world to accelerate digital transformation and liberate global markets with technology. Visit us at teamdefinex.com.