Yazılımda oturmamış bir kavram, Modal Yönetimi
Özellikle web uygulamalarda olmazsa olmaz bir component olan Modal’ın kod tarafında nasıl yönetileceği konusunda developerlar hala tam olarak uzlaşabilmiş değil. Gelin ideal Modal component’i nasıl olmalı birlikte inceleyelim.
Modal nedir?
Tüm uygulamalarda kullanıcının zaman zaman karşısına çıkan, derinlik bakımından sayfaların üzerinde duran bölüme Modal veya Pop-up ismi verilir. Çoğunlukla bir onay kutucuğu olarak karşımıza çıkan bu bölümler designer’lar tarafından sıklıkla üretilir. Modal alışılagelmiş, gözlerin aradığı bir UI (User Interface) elementidir.
Bu kadar varlığını kanıtlamış bir elementin kodlanma süreci de çoktan standartlaşmalıydı yazılımın birçok alanında olduğu gibi bu konuda da standartlaşmamanın getirdiği kronik sorunlar developer’ların başını ağrıtıyor.
Hadi standartlaştıralım
Konuyu VueJS ile anlatacağım ama diğer framework’lerde nasıl olduğunu tahmin edebilirsiniz.
Modalın ne zaman görüneceğine kim karar verir?
Bir Modal’ın ekranda ne zaman görüneceğine yalnızca Parent Page karar verebilmeli. Ne zaman gizleneceğine ise görünmesini sağlayan Parent Page ve modal’ın kendisi karar verebilmeli.
Sık yapılan yanlışlardan biri Modal’ın görünmesini sağlayan butonun ve kendisinin aynı component dosyası içerisinde kodlanması. Bu yönetimi zor bir Modal ortaya koyar. Modal’ın görünmesini sağlayan state de Parent Page’den gelmelidir.
// App.vue
<script setup>
import { reactive } from 'vue'
import Modal from './components/Modal.vue'
const state = reactive({ isVisibleModal: false })
const onOpenModal = () => state.isVisibleModal = true
const onCloseModal = () => state.isVisibleModal = false
</script>
<template>
<button @click="onOpenModal">Open Modal</button>
<Modal v-model="state.isVisibleModal">
<template #header>
Modal Title
</template>
<template #content>
<p>
Modal content Lorem ipsum dolor sit amet, consectetur adipisicing elit. Odit, amet iste laboriosam iure
voluptates sunt sint, possimus doloribus impedit nulla et? Consequuntur accusamus quia ex minima dolores animi
omnis cupiditate.
</p>
</template>
<template #footer>
<button @click="onCloseModal">Close</button>
</template>
</Modal>
</template>
Import ettiğimiz Modal adlı component’in görünürlüğünü tetikleyen state isVisibleModal
bu state true
iken Modal görünecek, false
iken Modal görünmeyecek. Modal’ın ekranda görünmediği durumlarda DOM olarak da yüklenmiyor olması profesyonelce bir davranış olacaktır.
Garip bir şekilde HTML’in kendi Modal elementi olan <dialog>
open olmadığı durumlarda da DOM yığını içerisinde varlığını sürdürüyor.
Görseldeki v-if
ifadesinin bulunduğu kısım Modal’ın olduğu kısım, Modal’ın görünmesini sağlayan şartlar yerine gelinceye kadar orada bu v-if ifadesi yer alacak. Modal kullanıcı tarafından ekranda görünmüyorken, DOM olarak da browser’a yüklenmemesi için Modal component’inin en dış kapsayıcı elementi görünürlük şartına bağlanmalı.
// Modal.vue
<script setup>
const isVisibleModal = defineModel() // define two way binding
const props = defineProps({
width: {
type: String,
default: '40rem'
}
})
// close modal when click on backdrop
const onCloseModal = (e) => {
if (e.srcElement.classList.contains('backdrop')) {
isVisibleModal.value = false
}
}
</script>
<template>
<div v-if="isVisibleModal" class="backdrop" @click="onCloseModal">
<div class="modal">
<section class="header">
<slot name="header" />
</section>
<section class="main">
<slot name="content" />
</section>
<section class="footer">
<slot name="footer" />
</section>
</div>
</div>
</template>
<style scoped>
.backdrop {
width: 100vw;
height: 100vh;
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal {
background-color: rgba(0, 0, 0);
padding: 2rem;
border-radius: .5rem;
width: v-bind(width);
display: flex;
flex-direction: column;
gap: 1em;
}
.header {
font-weight: bold;
}
.footer {
margin-left: auto;
}
</style>
Tüm bunlara rağmen Modal component’inin script
kısmı Parent Page ekrana mount olduğunda run olur. Dolayısıyla Modal’ın içerisinde mount olur olmaz çalışacak bir fonksiyon bulundurmamak gerekir. Bu mimari açıdan da sakıncalıdır.
Modal içerisinde API Request yapılmaz
Modal’ın jenerik bir component olmasını isteriz, böylece projedeki tüm Modal’ların tek bir tipte görünmesini sağlayabiliriz. Bir gün değişiklik yapmak istediğimizde tek bir yerden (senaryomuz gereği Modal.vue dosyasından) yapacağımız değişiklikle tüm projedeki Modal’ları güncelleyebiliriz.
Modal’ın jenerik kalabilmesi için, component içerisinde API Request yapmamalıyız, veri ile ilgili mantıksal (logic) işlem yapmamalıyız. Aslında zaten frontend tarafında asla logic işlem yapmamalıyız da neyse :)
Modal’ın tek bir görevi vardır. Kullanıcıya bir şey göstermek veya göstermemek. Daha fazla görev ve anlam yüklememeliyiz.
Modal’ın içeriği Parent Page içerisinde hazırlanmalı ve set edilmeli
Modal’ın jenerik bir component olarak hayatını sürdürmesi için, içerisinde hard data yerine slot’lar bulunmalı. Parent page içerisinde Modal’ı kullanırken ilgili slot’larda ne tür bilgiler görüneceğine karar vermeliyiz.
Modal’ın title’ı bile Parent Page tarafından set edilmeli. Özetle Modal’ın tüm içeriği dinamik olmalı. Böylece ideal bir Modal’dan söz edebiliriz.
GitHub https://github.com/gayret/vue-modal