ASP.NET Core 6/MVC/Layout'lar
Layout'lar web geliştirme için çok önemli bir konudur ve view tarafındaki kod tekrarını önler. Çoğu web sitesi, sayfaları için genel bir şablon kullanır. Örneğin sol tarafta navigasyon menüsü olur; üst tarafta ana menü linklerinin, arama kutusunun, oturum açma ve yeni kullanıcı kaydı için butonların olduğu bir şerit olur. Sayfanın sonunda telif hakları, vb. uyarıların olduğu footer kısmı olur. Sayfada tek değişen orta kısımdır ve sayfanın asıl içeriğini belirtir. İşte bu tür bir senaryoda layout'lar biçilmiş kaftandır ve view'ların yalnızca sayfanın ortasını belirtmesini, geri kalanın layout tarafından oluşturulmasını sağlar. Web sitemizde hiç bu tür bir bölümleme yapmayacak olsak bile layout'lar faydalıdır. Çünkü bir HTML sayfasının asıl içerik üreten kısmı <body> kısmıdır. Layout'lar view'ların sadece <body> kısmının içeriğini belirtmesini, HTML sayfasının genel iskeletinin ve <head> kısmının layout tarafından oluşturulmasını sağlayabilir.
Layout'lar büyük ölçüde standart view'lara benzerler. Çoğunlukla bütün controller'ların action'ları tarafından kullanılabilmesi için Views/Shared klasörüne konulurlar. Layout isimleri alt tire (_) karakteriyle başlar ve varsayılan layout çoğunlukla _Layout.cshtml ismine sahip olur. Layout'ta asıl içeriğin yazılacağı kısmı RenderBody() metoduyla belirtiriz. Örnek:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Burası bir layout'tur ve sayfanın asıl içeriği aşağıda belirtilmiştir.</p>
@RenderBody()
</body>
</html>
Bir view ilgili layout'u kullanmak için sayfanın başında aşağıdaki kod bloğunu eklemelidir:
@{ Layout = "_Layout"; }
Layout belirtimlerinde bir özelliğe değer atandığı için C# kod bloğu açılması kaçınılmazdır.
View'dan Layout'a veri gönderme
[değiştir]Daha önce bir HTML sayfasının <head> kısmında çoğunlukla aynı şeyler bulunduğu için bu kısmı bir layout'a atayabileceğimizi söylemiştik. Ancak bir sayfanın <head> kısmında da sayfaya özgü HTML kodları bulunabilir. Sayfanın başlığını belirten <title> etiketi buna verilebilecek en yaygın örnektir. İşte bu tür senaryolarda layout'u kullanan view'ın layout'a veri göndermesi, layout'un da kendisine gönderilen bu veriye dayanarak içeriğini modifiye etmesi gerekir. Örneğin view dosyamızı şöyle değiştirelim:
@{ Layout = "_Layout"; ViewBag.Title = "Sayfa Başlığı"; }
Layout'u ise şöyle değiştirelim:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
<p>Burası bir layout'tur ve başlığı @ViewBag.Title olan sayfanın asıl içeriği aşağıda belirtilmiştir.</p>
@RenderBody()
</body>
</html>
Action, view ve layout'un çalışma sırası şöyledir: action > view > layout. Action view'ı renderlar, view layout'u render'lar, sonra layout'un ilgili kısmına kendini koyar.
_ViewStart.cshtml dosyası
[değiştir]Daha önce _ViewImports.cshtml dosyasını görmüştük ve her view'ın başında kullanılan @using deyimlerini _ViewImports.cshtml dosyasına koyup bütün view'ların aynı isim alanlarını import etmesini sağlamıştık. _ViewStart.cshtml dosyası da benzer işleve sahiptir. Her view render'lanmadan önce _ViewStart view'ı render'lanır ve her view'ın başına eklenecek kodları belirtir. Ancak kullanım amacı olarak _ViewImports ile _ViewStart farklılaşır. ViewImports'ta bir şeyleri import ederiz. Örneğin @using ile kullanacağımız isim alanlarını import ederiz ve ilgili isim alanındaki bütün sınıflara erişim hakkı kazanırız veya daha sonra göreceğimiz addTagHelper ile belirli bir kümedeki tag helper'ları import ederiz ve ilgili kümedeki bütün tag helper'ları kullanım hakkı elde ederiz. _ViewStart ise daha genel amaçlıdır. Şimdi projemizin Views/Shared klasörüne _ViewStart.cshtml isimli bir view ekleyelim ve içeriği şöyle olsun:
@{ Layout = "_Layout"; }
Bu kod projemizdeki bütün view'lar için layout'un _Layout.cshtml olduğunu belirtmektedir ve her bir view'ın layout belirtimi yapmasını gereksiz kılmaktadır. Bu aşamada Views/Shared klasörüne eklenen layout dosyası projedeki bütün view'lar için kullanılacaktır. Eğer belirli bir controller'ın view'ları için farklı layout kullanmak istiyorsak aynı isimli ve içeriği farklı olan layout'u Views/<controller> klasörüne koyabiliriz. Çünkü layout arama süreci view arama süreciyle aynıdır. Bir klasördeki view layout'unu da ilk önce kendi klasöründe arayacaktır. Veya aynı amaçla Views/<controller> klasörüne layout seçimi yapacak _ViewStart.cshtml dosyasını koyabiliriz. Bu _ViewStart.cshtml dosyası yalnızca ilgili controller'ın view'ları için çalışacaktır.
Benzer şekilde her bir view yine de kendi layout'unu belirtebilir veya Layout özelliğine null atayarak bir layout kullanmak istemediğini belirtebilir.
Layout section'ları
[değiştir]Section'lar layout'ların daha gelişmiş bir özelliğidir ve view'lara layout'a daha fazla müdahale etme imkanı sağlar. Layout, view'larca değiştirilemeyecek sabit kısımları ve içeriği view'lar tarafından doldurulacak değişken kısımları barındırır. Bu değişken kısımlara section denir ve içeriği layout'u kulanan view tarafından doldurulur.
Bazen web sitelerinde görürüz. Üst menü bütün sayfalar için aynıdır. Ancak soldaki navigasyon kısmında listelenen linkler sayfaya göre değişmektedir. Ancak bu navigasyon kısmının formatı aynıdır ve sayfada aynı bölgeyi kaplamaktadır, tek değişen linklerdir. Veya sayfanın footer kısmında sayfanın son güncelleme tarihi gibi her sayfada değişecek bilgiler bulunmaktadır. Benzer şekilde bu footer kısmının da formatı aynıdır. Kısaca section'lar formatın sabit kaldığı ama içeriğin view'dan view'a değişebildiği durumlar için kullanılabilir. Şimdi projenizin Views/Shared klasöründeki _Layout.cshtml dosyasını şöyle değiştirin:
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Verdana;
}
.header {
grid-area: header;
background: tan;
font-weight: bold;
font-size: 30px;
text-align: center;
padding: 15px;
}
.menu {
grid-area: menu;
background: wheat;
width: 200px;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
background: tan;
text-align: center;
font-style: italic;
font-size: 12px;
}
.grid-container {
display: grid;
grid-template-areas:
'header header header header'
'menu main main main'
'footer footer footer footer';
}
.menu > a {
text-decoration: none;
display: block;
}
.menu > a:hover {
background: blue;
color: white;
}
</style>
</head>
<body>
<div class="grid-container">
<div class="header">www.websitem.com</div>
<div class="menu">@RenderSection("menu")</div>
<div class="main">@RenderBody()</div>
<div class="footer">@RenderSection("footer")</div>
</div>
</body>
</html>
Bu layout klasik bir layout'tur. Sayfanın üstünde her sayfada sabit olacak "www.websitem.com" yazısı olacaktır. Sol taraftaki menüde sitedeki farklı sayfalara giden linkler olacaktır. Sayfanın altında sayfanın son güncelleme bilgisinin olduğu bir footer olacaktır. Sayfanın asıl içeriği ise tam ortaya yazılacaktır. Sayfanın header kısmı bütün site boyunca sabittir. Menü ve footer'ı formatı sabittir ama içeriği view'dan view'a değişebilmektedir. Formattan kastımız ilgili bölgenin genişliği, yüksekliği, arkaplan rengi, font büyüklüğü, sayfadaki konumu, vs.dir. Diğer bir deyişle sadece _Layout.cshtml dosyasını değiştirerek web sitesinin bütün görünümünü değiştirebileceğiz. Tamamlandığında web sitemizin görünümü şuna benzeyecek:
Şimdi Views/Home klasöründeki Index.cshtml dosyasını şöyle değiştirin:
@{
Layout = "_Layout";
}
@section menu {
<a href="http://www.websitesi1.com">Link 1</a>
<a href="http://www.websitesi2.com">Link 2</a>
<a href="http://www.websitesi3.com">Link 3</a>
}
What is Lorem Ipsum?
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Why do we use it?
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).
@section footer {
Bu sayfa en son @DateTime.Now.ToShortDateString() tarihinde güncellenmiştir.
}
Programı çalıştıdığınızda yukarıdaki resimdekine benzer bir görünüm almanız lazım. View'daki section'lar bir view'ın içerebileceği şekilde C# ve HTML kodu karışımını içerebilirler. View'da herhangi bir section'a yazılmamış kodlar ana section'da sayılır ve layout'ta RenderBody() ile işaretlenen yere konulur.
Bir view sadece kullandığı layout'ta tanımlanan section'lar için section belirtebilir. Aksi durumda çalışma zamanı hatası oluşur.
Opsiyonel section'lar kullanma
[değiştir]Varsayılan durumda bir view layout'taki bütün section'lar için içerik belirtmelidir. Aksi durumda çalışma zamanı hatası oluşur. Bu varsayılan durumu değiştirmek için layout'ta @RenderSection() metodu ikinci parametresi false olacak şekilde değiştirilir. Örnek (_Layout.cshtml):
<div class="footer">@RenderSection("footer", false)</div>
Artık footer section'ı opsiyoneldir ve bir view layout'a footer section'ının içeriğini vermezse sayfada footer olmayacaktır.
Section'ları test etme
[değiştir]Bazen bir layout kendisini kullanan view'ın bir section için içerik belirtip belirtmediğini öğrenmek isteyebilir. Bu tür bir kontrol olası çalışma zamanı hatalarının önüne geçecektir. Şimdi _Layout.cshtml dosyasındaki footer sınıfına ait olan div'in içeriğini aşağıdaki gibi değiştirin:
@if (IsSectionDefined("footer")) {
@RenderSection("footer", false)
} else {
@:Bu sayfanın son değiştirme bilgisi bulunmamaktadır.
}
Bu örneğimizde view'ın bir section için içerik sağlamaması durumunda footer'a yazılacak bir fallback değeri belirttik.
Layout'ların layout kullanması
[değiştir]Layout'lar layout kullanabilirler. Örneğin web sitenizin header kısmı bütün site için ortak olsun, ama sol taraftaki navigasyon linkleri controller'dan controller'a değişsin. Müşteri hizmetlerinin yürütüldüğü controller'daki bir view görüntülendiğinde müşteri hizmetleriyle ilgili linkler olsun, stok yönetimiyle ilgili bir controller'a ait view görüntülendiğinde stok yönetimiyle ilgili linkler olsun. Böyle bir durumda her bir Views/<controller> klasörüne ayrı bir layout dosyası koyarız. Bu layout dosyalarının da layout olarak ana layout'u kullanmasını sağlarız.