Mvc’de Model ve Özelleştirilmiş Kontrol Kavramına Derinlemesine Giriş
Mvc’nin enstrumanları ve çalışma mantığının daha iyi anlaşılması için örnek bir uygulama yapıcağız. Bu uygulamamızda bir kullanıcıya role yetkileri verip istendiğinde rol isimlerini ve açıklamalarını değiştirebileceğiz.Daha sonra rolün önem sırasına göre bir değerlendirme yapıcağız.
Model:Mvc’deki M’nin anlamı olması dışında, database ile olan tüm işlemlerimizin kullanıldığı yapıdır. Birbaşka konuda view modellerdir.View modeller database’i temsil eden moddellerin view tarafındaki karşılıklarıdır. View tarafında ayrıca bir modele ihtiyaç duymamızın iki nedeni vardır. Birincisi databasedeki tüm verileri view’da göstermek istemeyebiliriz. Gereksiz yere tüm datanın view tarafına gönderilmesi gereksiz bir performance kaybına neden olacaktır.İkinci neden ise database’de yapılabilecek bir değişiklik, view tarafında herhangi bir hataya sebebiyet vermemelidir. Bu nedenle bir Role için bir de bu rol’ün önem derecesine not vereceğimiz dropdownlist için iki ayrı view model’i aşağıda görüldüğü gibi oluşturduk. İkisi için tek model yapmamamızın nedeni dağıtık mimariye uymak isteyişimizdir. Yani ileride role veya bu derecelendirme modellerini başka bir yerde, farklı amaçlarla kullanabiliriz. İkisini tek bir modelde toplasa idik, rol’e ihtiyaç duyulup derecelendirme yapılmayacak durumlarda tüm yapıyı içeren bu global model ile çalışmak zorunda kalacak ve gerekmediği halde derecelendirme modelini de gönderecektik.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
namespace User.Models { public class RoleModel { public int ID { get; set; } public bool IsAllow { get; set; } public string Name { get; set; } public string Description { get; set; } public string ListID { get; set; } } public class ListModel { public int ID { get; set; } public string Text { get; set; } } } |
Routing: Sayfamızın adresini yazdıktan sonra yani bir request işleminden sonra routing devreye girer. RoutConfig’in bir mvc projesindeki yeri App_Star/RouteConfig.cs. dir.Buradan ilgili controller’ın ilgili action’ını bulunur.Routing konusunu ilerde detaylı olarak inceliyeceğiz.
Controller Ve Action: Data ile view arasındaki bağlantıyı kuran yapı controllerdır. Aşağıda görüldüğü gibi HomeController’ın Index Actionın’da view’ımız için gerekli dummy dataları, List<RoleModel>‘imize doldurup gönderdik. Ayrıca dropdownlist’imiz için oluşturduğumuz ListModel’i ViewBag ile gönderdik. İlgili datayı ViewBag ile göndermemizin nedeni Index View’umuz ‘@model List<User.Models.RoleModel>’ beklemesine rağmen ayrıca bir List<ListModel> beklememesidir. Gönderilecek olan bu datanın kısalığı, pek önem teşkil etmemesi ve yukarıda da bahsettiğim dağıtık mimarinin uygulanmaya çalışması ListModel’in ViewBag ile gönderilmesindeki en doğru seçim olduğunu göstermektedir. (Not:ViewBag sadece controller’dan view’a doğru tek yönlü çalışan dynamic tipte veri tutan ve sonra silinen veri taşıma objesidir.) Ödev:Siz daha sonra ListModelimiz de view tarafında oluşturup dropdownlistimizi doldurmayı deneyin. İlgili kod bloğunu alttaki yorumlara koyabilirsiniz.Böylece ilgili dataları, ilgili modellerimize doldurduk. Normalde bu işlemi bussines katmanında CodeFirst ,EntityFramwork veya başka araçlarla yapılmalıdır. Ama burada amacımız Mvc‘deki data akış prosedürlerini incelemektir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
public List<RoleModel> FillData() { List<RoleModel> Datas = new List<RoleModel>() { new RoleModel() { ID=1, Description = "Uygulama Yönetimi Ekranları", IsAllow = true, Name = "Administrator" }, new RoleModel() { ID=2, Description = "Giriş Yapabilen Tüm Kullanıcılar", IsAllow = false, Name = "CommonScreens" }, new RoleModel() { ID=4, Description = "Ajans Silme", IsAllow = true, Name = "AgencyDelete" }, new RoleModel() { ID=6, Description = "Ajans Detayı Görme", IsAllow = true, Name = "AgencyDetail" } }; return Datas; } public List<ListModel> FillList() { List<ListModel> Datas = new List<ListModel>() { new ListModel() { ID=1, Text = "Worse" }, new ListModel() { ID=2, Text = "Bad" }, new ListModel() { ID=3, Text = "Good" }, new ListModel() { ID=4, Text = "Better" }, new ListModel() { ID=5, Text = "Best" } }; return Datas; } |
Yukarıda görüldüğü üzere dummy datalarımızı ve bunları dolduran methodlarımızı yazdık. Artık bunları Index View’a gönderebiliriz.
1 2 3 4 5 6 |
[HttpGet] public ActionResult Index() { ViewBag.List = FillList(); return View(FillData()); } |
Yukarıda görüldüğü üzere ViewBag.List’imizde dinamik List oluşturduk ve ListModeleimizi gönderdik. Aynı şekilde View’a da RoleModelimizi gönderdik.
Aşağıda görüldüğü gibi View: Index view’ımız model olarak RoleModel listesi beklemektedir. Ayrıca @using ile dll olarak bulunan TestRadio.Model classımızı da view’ımızda kullandık.
1 2 |
@using TestRadio.Models @model List<User.Models.RoleModel> |
View’ımızın dizaynı aşağıdaki gibidir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<table> <tr style="background-color: cornflowerblue;font-family:fantasy;"><td></td><td>İzin Ver</td><td>Engelle</td><td>Yetki</td><td>Açıklama</td><td>Oyla</td></tr> @{ var count = 0;} @foreach (var item in Model) { count++; <tr> <td>@Html.Hidden("hdn" + @count, item.ID, new { @class = "hdnData" })</td> @*<td>@Html.RadioButton("rd" + item.ID, true, item.IsAllow)</td>*@ <td>@Html.BoraRadioButton(item.ID, true, item.IsAllow)</td> <td>@Html.BoraRadioButton(item.ID, false, !item.IsAllow)</td> @*<td>@Html.RadioButton("rd" + item.ID, false, !item.IsAllow)</td>*@ <td> @*@Html.TextBox("txtN" + item.ID, item.Name,new{@id="txtN"+item.ID})*@ @Html.BoraTextbox("N",item.ID,item.Name) </td> <td> @*@Html.TextBox("txtD" + item.ID, item.Description, new { @id = "txtD" + item.ID })*@ @Html.BoraTextbox("D", item.ID, item.Description) </td> <td> @Html.DropDownList("Rank", new SelectList(ViewBag.List, "ID", "Text"), new { @id = "drop" + item.ID }) </td> </tr> } </table><br /> <input type="button" onclick="save();" value="Kaydet" /> |
Örnek Ekran görüntüsü aşağıdadır.
Yukarıda RadioButton , TextBox ve Dropdownlistlerimizi RoleModel Listemiz’i foreach ile dönerek doldurduk. Aşağıda RadioButton’umuzun uzun hali gözükmektedir.Burada belli bazı parametreleri kodumuz gereği eklemek zorunda kaldık.Tüm RadioButtonlar için bu işlemin yapılması gayet zahmetli ve zaman alıcı bir iştir. İşimizi kolaylaştırmak adına RadioButton için Custom HtmlHelper Control‘ünü aşağıda görüldüğü gibi yazdık. Projemizde yetki ve açıklama alanlarının zorunlu olması, bu alanların boş geçilememesi ve bunun denenmesi halinde çeşitli şekillerde uyarı verilmesi, ayrıca daha sonradan ilgili textbox’a ulaşabilmek adına tanımlamamız gereken çeşitli parametrelerin olması CustomTextBox çözümüne gitmemizeki enbüyük etkenlerdir. Kodlama aşağıda görüldüğü gibidir.
1 |
@*<td>@Html.RadioButton("rd" + item.ID, true, item.IsAllow)</td>*@ |
Custom HtmlHelper BoraRadioButton:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
namespace TestRadio.Models { public static class HtmlHelperExtensions { public static MvcHtmlString BoraRadioButton(this HtmlHelper helper, string name) { return BoraRadioButton(helper, name, true,true, null); } public static MvcHtmlString BoraRadioButton(this HtmlHelper helper, int ID, bool value,bool isChechked) { return BoraRadioButton(helper, ("rd" + ID.ToString()), value,isChechked, null); } public static MvcHtmlString BoraRadioButton(this HtmlHelper helper, string name, bool value,bool isChechked,object htmlAttributes) { TagBuilder radioButton = new TagBuilder("input"); radioButton.Attributes.Add("type", "radio"); radioButton.Attributes.Add("name", name); radioButton.Attributes.Add("id", name); if (isChechked) { radioButton.Attributes.Add("checked", "checked"); } radioButton.Attributes.Add("value", value.ToString()); radioButton.MergeAttributes(new System.Web.Routing.RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(radioButton.ToString(TagRenderMode.Normal)); } } } |
Custom HtmlHelper BoraTextBox:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public static MvcHtmlString BoraTextbox(this HtmlHelper helper, string name) { return BoraTextbox(helper, name, null, null); } public static MvcHtmlString BoraTextbox(this HtmlHelper helper,string prm, int ID, string text) { return BoraTextbox(helper, ("txt"+prm +ID.ToString()), text, null); } public static MvcHtmlString BoraTextbox(this HtmlHelper helper, string name, string text, object htmlAttributes) { TagBuilder textBox = new TagBuilder("input"); textBox.Attributes.Add("type", "text"); textBox.Attributes.Add("name", name); textBox.Attributes.Add("id", name); if (!string.IsNullOrEmpty(text)) { textBox.Attributes.Add("value", text); } textBox.Attributes.Add("onblur", "javascript:if(this.value==''){this.style.backgroundColor = 'yellow';this.focus();};"); textBox.Attributes.Add("onkeyup", "javascript:if(this.value!=''){this.style.backgroundColor = 'white';}else{this.style.backgroundColor = 'yellow';};"); textBox.MergeAttributes(new System.Web.Routing.RouteValueDictionary(htmlAttributes)); return MvcHtmlString.Create(textBox.ToString(TagRenderMode.Normal)); } |
BoraTextBox’a dikkat ederseniz.ClientSide validation yazdık.Boş kontrolü yapıyoruz.Kısaca textboxlarımız required yani zorunlu. TextBox’ların onblur’unda ve keyup event’inde javascript function’ını yazdık.Eğer boş ise arka rengi sarı yapıp bu textbox’dan ayrılmaya izin vermiyoruz.
1 2 |
textBox.Attributes.Add("onblur", "javascript:if(this.value==''){this.style.backgroundColor = 'yellow';this.focus();};"); textBox.Attributes.Add("onkeyup", "javascript:if(this.value!=''){this.style.backgroundColor = 'white';}else{this.style.backgroundColor = 'yellow';};"); |
DropDownList’imizi de ViewBag.List’den doldurduk.
1 |
@Html.DropDownList("Rank", new SelectList(ViewBag.List, "ID", "Text"), new { @id = "drop" + item.ID }) |
Ödev:Bir tane de CustomDropDownList siz yazın. İlgili kodu alttaki yourumlar kısmına koyabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<script src="~/Scripts/jquery-2.1.1.min.js"></script> <script type="text/javascript"> function RoleData() { this.ID; this.IsAllow; this.Name; this.Description; }; function save() { var dataModel = []; $(".hdnData").each(function () { dataModel.push({ 'ID': this.value, 'name': $("#txtN" + this.value).val(), 'description': $("#txtD" + this.value).val(), 'IsAllow': $('input[name=rd' + this.value + ']:radio:checked').val(), 'ListID': $('#drop' + this.value + " :selected").val() }); }); $.ajax({ type: 'POST', url: '/Home/Index/', data: JSON.stringify(dataModel), contentType: "application/json", dataType: 'text', success: function (data) { alert(data); } }); }; </script> <input type="button" onclick="save();" value="Kaydet" /> |
Yukarıda görüldüğü gibi javascript’imizde datamodel[ ] Array dizisi oluşturduk.Herbir Data için uniq olan ID değerlerine erişmek için tüm hidden controlleri döndük. Tüm nesnelerimizin isimlendirmelerini bu uniq ID’lere göre yaptığımız için ihitiyacımız olan dataları kolaylıkla çektik. Tüm controller’deki çektiğimiz bu dataları array modelimize doldurduk. Not:Dikkat ederseniz data modelîmizin property isimleri ile RoleModel’in property isimleri aynı.
1 |
dataModel.push({ 'ID': this.value, 'name': $("#txtN" + this.value).val(), 'description': $("#txtD" + this.value).val(), 'IsAllow': $('input[name=rd' + this.value + ']:radio:checked').val(), 'ListID': $('#drop' + this.value + " :selected").val() }); |
Amaç ajax posta controller’a gönderilen data List<RoleModel> model’e uygun bir client side data modeli oluşturmak.
Controller Index Post Action:
1 2 3 4 5 6 7 8 9 10 11 12 |
[HttpPost] public ActionResult Index(List<RoleModel> model) { if (ModelState.IsValid) { return Content("Data Saved"); } else { return Content("Data Error"); } } |
Yukarıda görüldüğü üzere post edilen data geçerlimi diye bakıldıktan sonra, save işlemi yapıldı. Ve sonuç mesajı gönderildi.
1 2 3 4 5 6 7 8 9 10 |
$.ajax({ type: 'POST', url: '/Home/Index/', data: JSON.stringify(dataModel), contentType: "application/json", dataType: 'text', success: function (data) { alert(data); } }); |
Yukarıda görüldüğü gibi doldurulan datamodel JSON formatına çevrildi. Ajaxpost’da datanın çekileceği url Controller/Action(url:’/Home/Index’) olarak belirtildi. Dikkat ederseniz iki tane Index Action’ı var.İkisini ayıran tek şey [Post] attribute’ü.Daha sonra custom attribute’ler de yazıcaz.Hatta bu attributeları filter amaçlı olarak kullanıcaz.
Herkese iyi çalışmalar.
Bir sonraki makalede görüşmek üzere hoşçakalın.
Not:Örnek Url (İptal):http://testradio.azurewebsites.net/
Source Code:http://www.borakasmer.com/projects/TestRadio.rar
Bilgi için teşekkürler. Ancak aşşağıda değil aşağıda olacak. Tüm makalede yanlış yazılmış.
Düzeltme için teşekkürler.
Emeğinize sağlık verdiğiniz bilgilerin hepsi cok güzel.
kodların olmasıda ayrıca süper
teşekkürler
Ben teşekkür ederim :)
Hocam merhaba,
Makaledeki bilgiler çok başarılı teşekkürler emeğiniz için.
Bir sorum olacaktı; dropdown value değerini veritabanında integer olarak tutsak ve listelerken string karşılığını döndürsek nasıl yapabiliriz?
Listelerken veri tipi değişmeniz işinize yaramıyor mu ?
Listelerken veri tipi değişmeniz işinize yaramıyor mu ?