Sözünü vermiştim, ancak fırsat oldu. JSON, jQuery ve jTemplates ile ASP.net’in entegre bir biçimde çalıştığı örnek web sistemi yazacağım. Bir kaç bölüm halinde ayırmaya karar verdim. Bu birinci bölüme geçmeden kısa bir açıklama yapayım başlık hakkında.
JSON: Bir tür veri değişim formatı olarak düşünebilirsiniz. XML gibi yani, tek farkı daha az yer tutması ve jscript tarafından direk parse edilebilmesidir diyebiliriz. Ufak çapta veri taşımalarınızda kapsülleme olarak çok rahatlıkla kullanılabiliyor.
jQuery: Son zamanların en popüler jscript kütüphanesi diyebiliriz. Biraz kullanınca hakikaten bırakamıyorsunuz. Oldukça basit ve etkili ayrıca genişletilebilir. Bu birinci bölümde çok az bir yerde kullandık ama jTemplates bir çok işlem için kendisine ihtiyaç duyuyor zaten.
jTemplates: Pek ismi bilinmeyen bir jQuery eklentisi. JSON verileri belirlediğiniz template’lere bind etme işlemini yapıyor. Bir nevi repeater diyebiliriz belki ama repeater’ın çok çok ötesinde, devam da göreceksiniz.
Şimdi örneğe başlıyalım, öncelikle AdventureWorks veritabanı ile çalışacağız. Microsoft SQL Server’la hazır gelen örnek veritabanıdır bilmeyenler için, internetten de indirilebiliyor. Bu bölümde sadece bir tane combobox oluşturacağız. Ama özel bir combobox tabi. OptGrouplarla ayrılmış ve içiçe 2 ayrı veri bind’ı olan bir combobox. Önceden başınıza hiç geldi mi bilmiyorum fakat eğer içiçe repeater’lar kullanmak zorunda kaldı iseniz ne kadar menem birşey olduğunu biliyorsunuzdur. Ama jTemplates ile hakikaten çocuk oyuncağı gibi.
Bir eklemek istediğim konu daha var, şimdi bu örnekte bolca el yazımı jscript ve türlü türlü teknoloji olacak güzel, fakat neden asp.net update panel, repeater vs. varken bunları kullanıyoruz. Updatepanel basit bir AJAX uygulaması için çok etkili olsada, etkili bir AJAX uygulaması için hiç birşeydir. Çünkü overhead diyebileceğimiz transfer ve kontrol fazlaları abartı düzeylerde olmaktadır. Ufak bir örnek yapıp deneyebilirsiniz. Tek bir text box ve bir tek repeater olan sayfada; mesela az bir veriyi, sunucuya 20 k civarı bir boyutta gönderirken, oluşan sonuç ise sunucudan 40 k civarı döner. halbuki işe konu olan veri toplasanız 1 k bile olmaz. Belli noktalarda bu kontrol fazlalıkları ve viewstate’ler gerekli olabilir ama bu yapacağım örneklerde bu hiçte gerekli değil.
Repeater içinde benzer bir durum var, birazdan göreceğiniz istemci tarafı template tanımlaması yapılmadığı için repeater’a bind edilecek veri sunucu tarafta html olarak render edilip istemciye öyle gönderilmektedir. O yüzden gene 1 k’lık bir veri gelmesi gerekirken html etiketler ve viewstate’ler ile birlikte veri inanılmaz boyutlara ulaşabiliyor. Söylediğim gibi ufacık bir örnek yapıp trafiği izlerseniz ne demek istediğimi gayet net anlayacaksınız.
Örneğe başlayalım, öncelikle veriyi hazırlayacak servisi yazalım.
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
Imports System.Web.Script.Services
Imports Newtonsoft.Json
<System.Web.Script.Services.ScriptService()> _
<System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ToolboxItem(False)> _
Public Class servis
Inherits System.Web.Services.WebService
Private globalJSONSett As New JsonSerializerSettings With {.NullValueHandling = NullValueHandling.Ignore, _
.ReferenceLoopHandling = ReferenceLoopHandling.Ignore}
<WebMethod(), ScriptMethod()> _
Public Function GetCategoriesJSON() As String
Dim aw As New AWDataContext
Dim CatsGrup = From sc In aw.ProductCategories Order By sc.ProductCategoryID, sc.Name _
Select New With { _
.name = sc.Name, _
.id = sc.ProductCategoryID, _
.subs = (From s In sc.ProductSubcategories _
Select New With { _
.name = s.Name, _
.id = s.ProductSubcategoryID})}
Return JsonConvert.SerializeObject(CatsGrup, Formatting.None, globalJSONSett)
End Function
End Class
Burada göreceğiniz gibi, bir http servis yaptık, bu servis bir metod içeriyor. Bu metod AWDataContext olarak tanımlanmış LinQtoSQL bağlantısı ile bir linq sorgusu çağırıyor ve oluşan sınıfı JSON.net ile birlikte JSON verisi haline getirip sonuç olarak döndürüyor. Oluşan JSON çıktı şu şekilde olur.
{
"name": "Bikes",
"id": 1,
"subs": [
{
"name": "Mountain Bikes",
"id": 1
},
{
"name": "Road Bikes",
"id": 2
},
{
"name": "Touring Bikes",
"id": 3
}
]
},
{
"name": "Components",
"id": 2,
"subs": [
{
"name": "Handlebars",
"id": 4
},
{
"name": "Bottom Brackets",
"id": 5
},
{
"name": "Brakes",
"id": 6
},
{
"name": "Chains",
"id": 7
},
{
"name": "Cranksets",
"id": 8
},
{
"name": "Derailleurs",
"id": 9
},
{
"name": "Forks",
"id": 10
},
{
"name": "Headsets",
"id": 11
},
{
"name": "Mountain Frames",
"id": 12
},
{
"name": "Pedals",
"id": 13
},
{
"name": "Road Frames",
"id": 14
},
{
"name": "Saddles",
"id": 15
},
{
"name": "Touring Frames",
"id": 16
},
{
"name": "Wheels",
"id": 17
}
]
},
{
"name": "Clothing",
"id": 3,
"subs": [
{
"name": "Bib-Shorts",
"id": 18
},
{
"name": "Caps",
"id": 19
},
{
"name": "Gloves",
"id": 20
},
{
"name": "Jerseys",
"id": 21
},
{
"name": "Shorts",
"id": 22
},
{
"name": "Socks",
"id": 23
},
{
"name": "Tights",
"id": 24
},
{
"name": "Vests",
"id": 25
}
]
},
{
"name": "Accessories",
"id": 4,
"subs": [
{
"name": "Bike Racks",
"id": 26
},
{
"name": "Bike Stands",
"id": 27
},
{
"name": "Bottles and Cages",
"id": 28
},
{
"name": "Cleaners",
"id": 29
},
{
"name": "Fenders",
"id": 30
},
{
"name": "Helmets",
"id": 31
},
{
"name": "Hydration Packs",
"id": 32
},
{
"name": "Lights",
"id": 33
},
{
"name": "Locks",
"id": 34
},
{
"name": "Panniers",
"id": 35
},
{
"name": "Pumps",
"id": 36
},
{
"name": "Tires and Tubes",
"id": 37
}
]
}
]
JSON’ın yapısını incelerseniz bildiğiniz jscript olduğunu farkedeceksiniz. Çok boyutlu dizi tanımlaması gibi.
Şimdi ASPX sayfayı yaratalım ve içini dolduralım. Öncelikle yazdığımız servisi AJAX olarak çağırabilmek için bir ScriptManager’a ihtiyacımız var. Şu şekilde;
<asp:ScriptManager ID="scripMan" runat="server">
<Services>
<asp:ServiceReference Path="~/servis.asmx" />
</Services>
</asp:ScriptManager>
Yazdığımızı servise ait
<select>
<option value="">Tüm Kategoriler</option>
{#foreach $T as cat}
<optgroup label="{$T.cat.name}">
<option value="C{$T.cat.id}">Tüm {$T.cat.name}</option>
{#foreach $T.cat.subs as sub}
<option value="S{$T.sub.id}">{$T.sub.name}</option>
{#/for}
</optgroup>
{#/for}
</select>
HTML etiketler arasında bazı farklı etiketler var bunlar jTemplates’e ait etiketler, $T ana gelen veri sınıfını temsil ediyor. {#foreach $T as cat} tanımlaması ile bu ana sınıftaki tüm alt sınıfları (kategoriler) cat ismiyle çağıracağımızı belirtip döngüyü başlatıyoruz. {$T.cat.name} tanmlaması ile bu döngünün o an gelen elemanı için name özelliğini ilgili yere gömüleceğini belirtiyoruz. Anlayacağınız üzere süslü parantezler arasına yazılan her tanım birer jscript. Burda istediğiniz düzeyde jscript tanımlamaları ve işlemleri yapabilirsiniz. Diyez ile başlayan tanımlamalar ise jT’ye ait tanımlar foreach den başka for, if else gibi tüm temel alt düzey işleçler mümkün. Sayfasından inceleyebilirsiniz, ki zaten ilerleyen örneklerde daha derinlemesine işleyeceğiz.
Kategorilerin altına altkategorileri ekleme işleminin de yine aynı mantıkla foreach içine foreach ile yapıldığını görüyorsunuz. bu sefer yanlızca direk ana sınıf $T yerine $T.cat.subs’ı kullandık o an dönen sınıfın altındaki sınıfları anlatmak için. Şimdi veriyi alıp bu template’e bind etmek işlemi jscriptlerini yazalım.
$(document).ready(function() {
$("#deneme").setTemplateURL("templates/categoriesCombo.htm", null);
JJJOrnekPart1.servis.GetCategoriesJSON(onGetCategoriesJSONFinished, onGlobalError);
});
function onGetCategoriesJSONFinished(ret) {
$("#deneme").processTemplate(window.eval("(" + ret + ")"), null);
}
function onGlobalError(e) {
window.alert("Bir hata oluştu\n" + e._message);
}
Burada dikkatinizi çeken $(document).ready(…) olmuştur sanırım. İşte bu noktada jQuery işin içine giriyor. jQuery adından da anlaşılabileceği gibi bir sorgulama sistematiği ile çalışıyor. Burada ilk yaptığımız document objesine bağlı ready fonksiyonuna jscript kodu bind etmek. Bu fonksiyonun içinde ise bu sefer $(“#deneme”).processTemplate(…) kullandık bu da jQ ye ait genişleme durumunu gösteriyor. jT bu processTemplate fonksiyonunu jQ içerisine otomatik olarak ekledi ve biz bu sayede gelen veriyi ilgili template ile bind edebiliyoruz.
Geneli geçtiğimize göre biraz daha derinlemesine anlatayım. Öncelikle kalan HTML içeriği ekleyeyim.
<div id="deneme"></div>
HTML body içerisinde sadece bir div var deneme id’sine sahip. İlk olarak sayfa yüklenmesi bittiğinde jQ ready fonksiyonunu çağırır. Orada ilk satırda setTemplateURL fonksiyonu sayesinde deneme id’sine sahip bu div’e ait bir template olduğunu gösteriyoruz. İstersek bu template’i burda yaptığımız gibi dış dosya olarak değil, inline olarak da tanımlamamız mümkün (setTemplate fonksiyonu sayesinde). Burada dış dosya çağırımı ile yaptık. Bu anda jQ ilgili dosyayı indirip jT ye verecek, o da bu div için template sınıfını aktif hale getirecektir. İkinci satırda serviste tanımladımız fonksiyonu çağırıyoruz. Bu tip çağrımlarda iki adet standart parametre var, onSuccess ve onError. Bu iki parametrede birer fonksiyona point etmek zorunda. null geçebilirsiniz tabi eğer bir sonuç dönmeyecek ise. onSuccess olarak onGetCategoriesJSONFinished tanımladık, parametresi ret’e dönen JSON string olarak gelecek. onError için ise onGlobalError tanımladık. Global dememin sebebi tüm diğer çağırımları da bu fonksiyona point edeceğim. Burada ki e parametresi hata halindeki parametreleri içerecek bir sınıf oluyor. İşin güzelliği; eğer servis tarafında bir exception oluşur ise .net bu hatayı serialize edip istemci tarafa da aynı şekilde iletiyor. Yani sunucu da oluşan arızaya ait stack trace ya da mesaja aynen ulaşmanız mümkün. Bende burada _message özelliği ile sunucu tarafta oluşan exception’a ait mesajı gösteriyorum.
onGetCategoriesJSONFinished içerisinde ise gelen string veriyi önce parse etmek için jscript içerisinde built-in gelen JSON parser’ı kullanıyorum. Kullanımı basit, window.eval() içerisine string veriyi başına sonuna parantez ekleyip veriyorsunuz, çıktı olarak size o verilerden yaratılmış bir sınıf veriliyor. Yani mesela var a=window.eval… dedikten sonra a[0].name diye ilk değerin name özelliğini alabiliyorsunuz.
Bu aşamada jT veri ile önceden atamış olduğumuz template’i eşleştirip sonucu deneme div’i içerisine yazıyor. Bizimde elimizde tüm kategoriler ve alt kategoriler bulunan bir combo oluveriyor. Normalde zaten asp.net dropdownlist kontrolünde olmayan optgroup özelliğinin ötesinde böyle iç içe bir bind işlemini ne kadar kolay halledebildik.
Kodları şuradan indirebilirsiniz…
devam edeceğim…
Yazıya 4 Adet Yorum Yapılmış
Gökhan
11 Mayıs 2009 19:33
jTemplates gerçektende çok iyi sitesine bakınca oldukça işlevsel gözüküyor. Yazınız için teşekkürler.
sukru
12 Mayıs 2009 23:07
ben teşekkür ederim…
metin
11 Ekim 2009 00:49
Yazı için teşekkürler, emeğinize sağlık.
Yalnız kodların indirilebileceği link hatalı. Doğrusu şöyle:
http://blog.alatas.info/files/JJJOrnekPart1.rar
sukru
12 Ekim 2009 13:49
iki link de sorunsuz çalışıyor, anlık bir sorun olmuş olabilir. Teşekkürler…