مروری بر کدهای کلاس SqlHelper
در کل یک کلاس پایه تا حد امکان نباید try/catch داشته باشد. سطحهای بالاتر باید در این مورد تصمیم گیری کنند.
- من تگ a href الی آخر را دستی درست میکنم.
Url Routing در ASP.Net WebForms
<a href=<%= ResolveUrl("~") %> >Home</a> <a href="/" >Home</a>
در این قسمت قصد داریم یک فرم Ajaxایی را در ASP.NET MVC به همراه تمام مسایل اعتبارسنجی، پردازش اطلاعات و غیره را به کمک Twitter Bootstrap و jQuery Ajax پیاده سازی کنیم.
تهیه افزونه jquery.bootstrap-modal-ajax-form.js
از این جهت که مباحث مرتبط با نمایش و پردازش فرمهای مودال Ajaxایی به کمک Twitter Bootstrap اندکی نکته دار و طولانی هستند، بهتر است این موارد را به شکل یک افزونه، کپسوله کنیم. کدهای کامل افزونه jquery.bootstrap-modal-ajax-form.js را در ادامه ملاحظه میکنید:
// <![CDATA[ (function ($) { $.bootstrapModalAjaxForm = function (options) { var defaults = { renderModalPartialViewUrl: null, renderModalPartialViewData: null, postUrl: '/', loginUrl: '/login', beforePostHandler: null, completeHandler: null, errorHandler: null }; var options = $.extend(defaults, options); var validateForm = function (form) { //فعال سازی دستی اعتبار سنجی جیکوئری var val = form.validate(); val.form(); return val.valid(); }; var enableBootstrapStyleValidation = function () { $.validator.setDefaults({ highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.control-group').removeClass('success').addClass('error'); } $(element).trigger('highlited'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.control-group').removeClass('error').addClass('success'); } $(element).trigger('unhighlited'); } }); } var enablePostbackValidation = function () { $('form').each(function () { $(this).find('div.control-group').each(function () { if ($(this).find('span.field-validation-error').length > 0) { $(this).addClass('error'); } }); }); } var processAjaxForm = function (dialog) { $('form', dialog).submit(function (e) { e.preventDefault(); if (!validateForm($(this))) { //اگر فرم اعتبار سنجی نشده، اطلاعات آن ارسال نشود return false; } //در اینجا میتوان مثلا دکمهای را غیرفعال کرد if (options.beforePostHandler) options.beforePostHandler(); //اطلاعات نباید کش شوند $.ajaxSetup({ cache: false }); $.ajax({ url: options.postUrl, type: "POST", data: $(this).serialize(), success: function (result) { if (result.success) { $('#dialogDiv').modal('hide'); if (options.completeHandler) options.completeHandler(); } else { $('#dialogContent').html(result); if (options.errorHandler) options.errorHandler(); } } }); return false; }); }; var mainContainer = "<div id='dialogDiv' class='modal hide fade in'><div id='dialogContent'></div></div>"; enableBootstrapStyleValidation(); //اعمال نکات خاص بوت استرپ جهت اعتبارسنجی یکپارچه با آن $.ajaxSetup({ cache: false }); $.ajax({ type: "POST", url: options.renderModalPartialViewUrl, data: options.renderModalPartialViewData, contentType: "application/json; charset=utf-8", dataType: "json", complete: function (xhr, status) { var data = xhr.responseText; var data = xhr.responseText; if (xhr.status == 403) { window.location = options.loginUrl; //در حالت لاگین نبودن شخص اجرا میشود } else if (status === 'error' || !data) { if (options.errorHandler) options.errorHandler(); } else { var dialogContainer = "#dialogDiv"; $(dialogContainer).remove(); $(mainContainer).appendTo('body'); $('#dialogContent').html(data); // دریافت پویای اطلاعات مودال دیالوگ $.validator.unobtrusive.parse("#dialogContent"); // فعال سازی اعتبارسنجی فرمی که با ایجکس بارگذاری شده enablePostbackValidation(); // و سپس نمایش آن به صورت مودال $('#dialogDiv').modal({ backdrop: 'static', //با کلیک کاربر روی صفحه، صفحه مودال بسته نمیشود keyboard: true }, 'show'); // تحت نظر قرار دادن این فرم اضافه شده processAjaxForm('#dialogContent'); } } }); }; })(jQuery); // ]]>
- توابع enableBootstrapStyleValidation و enablePostbackValidation در مطلب «اعمال کلاسهای ویژه اعتبارسنجی Twitter bootstrap به فرمهای ASP.NET MVC» بررسی شدند.
- این افزونه با توجه به مقدار renderModalPartialViewUrl، یک partial view را از برنامه ASP.NET MVC درخواست میکند.
- سپس این partial view را به صورت خودکار به صفحه اضافه کرده و آنرا به صورت modal نمایش میدهد.
- پس از افزودن فرم Ajaxایی دریافتی، مسایل اعتبارسنجی را به آن اعمال کرده و سپس دکمه submit آنرا تحت کنترل قرار میدهد.
- در زمان submit، ابتدا بررسی میکند که آیا فرم معتبر است و اعتبارسنجی آن بدون مشکل است؟ اگر اینچنین است، اطلاعات فرم را به آدرس postUrl به صورت Ajaxایی ارسال میکند.
کدهای مدل برنامه
using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Mvc4TwitterBootStrapTest.Models { public class User { public int Id { set; get; } [DisplayName("نام")] [Required(ErrorMessage="لطفا نام را تکمیل کنید")] public string Name { set; get; } [DisplayName("نام خانوادگی")] [Required(ErrorMessage = "لطفا نام خانوادگی را تکمیل کنید")] public string LastName { set; get; } } }
کدهای کنترلر برنامه
using System.Web.Mvc; using Mvc4TwitterBootStrapTest.Models; namespace Mvc4TwitterBootStrapTest.Controllers { public class ModalFormAjaxController : Controller { [HttpGet] public ActionResult Index() { return View(); //نمایش صفحه اولیه } [HttpPost] //برای این حالت امنتر است //[AjaxOnly] public ActionResult RenderModalPartialView() { //رندر پارشال ویوو صفحه مودال به همراه اطلاعات مورد نیاز آن return PartialView(viewName: "_ModalPartialView", model: new User { Name = "", LastName = "" }); } [HttpPost] //[AjaxOnly] public ActionResult Index(User user) //ذخیره سازی اطلاعات { if (this.ModelState.IsValid) { //todo: SaveChanges; return Json(new { success = true }); } this.ModelState.AddModelError("", "خطایی رخ داده است"); return PartialView("_ModalPartialView", user); } } }
الف) متد Index حالت HttpGet که صفحه ابتدایی را نمایش خواهد داد.
ب) متد RenderModalPartialView یک partial view اضافه شده به برنامه به نام _ModalPartialView.cshtml را بازگشت میدهد. این partial view در حقیقت همان فرمی است که قرار است به صورت مودال نمایش داده شود و پردازش آن نیز Ajaxایی باشد.
ج) متد Index حالت HttpPost که نهایتا اطلاعات فرم مودال را دریافت خواهد کرد. اگر پردازش موفقیت آمیز بود، نیاز است همانند کدهای فوق return Json صورت گیرد. در غیراینصورت مجددا همان partial view را بازگشت دهید.
کدهای Index.cshtml
@{ ViewBag.Title = "Index"; var renderModalPartialViewUrl = Url.Action("RenderModalPartialView", "ModalFormAjax"); var postDataUrl = Url.Action("Index", "ModalFormAjax"); } <h2> Index</h2> <a href="#" class="btn btn-primary" id="btnCreate">ثبت اطلاعات</a> @section JavaScript { <script type="text/javascript"> $(function () { $('#btnCreate').click(function (e) { e.preventDefault(); //میخواهیم لینک به صورت معمول عمل نکند $.bootstrapModalAjaxForm({ postUrl: '@postDataUrl', renderModalPartialViewUrl: '@renderModalPartialViewUrl', renderModalPartialViewData: {}, loginUrl: '/login', beforePostHandler: function () { }, completeHandler: function () { // Refresh: برای حالتیکه نیاز به به روز رسانی کامل صفحه زیرین باشد // location.reload(); }, errorHandler: function () { } }); }); }); </script> }
- در اینجا یک لینک ساده در صفحه قرار گرفته و به کمک کلاس btn مجموعه bootstrap به شکل یک دکمه مزین شده است.
- در ادامه نحوه استفاده از افزونهای را که در ابتدای بحث طراحی کردیم، ملاحظه میکنید. کار با آن بسیار ساده است و تنها باید مسیرهای ارسال اطلاعات نهایی به سرور یا postDataUrl و مسیر دریافت partial view رندر شده یا renderModalPartialViewUrl به آن معرفی شود. سایر مسایل آن خودکار است.
کدهای _ModalPartialView.cshtml یا همان فرم مودال برنامه
@model Mvc4TwitterBootStrapTest.Models.User <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true"> ×</button> <h5> افزودن کاربر جدید</h5> </div> @using (Html.BeginForm("Index", " ModalFormAjax", FormMethod.Post, new { @class = "modal-form" })) { <div class="modal-body"> @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" }) <fieldset class="form-horizontal"> <legend>مشخصات کاربر</legend> <div class="control-group"> @Html.LabelFor(model => model.Name, new { @class = "control-label" }) <div class="controls"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name, null, new { @class = "help-inline" }) </div> </div> <div class="control-group"> @Html.LabelFor(model => model.LastName, new { @class = "control-label" }) <div class="controls"> @Html.EditorFor(model => model.LastName) @Html.ValidationMessageFor(model => model.LastName, null, new { @class = "help-inline" }) </div> </div> </fieldset> </div> <div class="modal-footer"> <button class="btn btn-primary" type="submit"> ارسال</button> <button class="btn" data-dismiss="modal" aria-hidden="true"> انصراف</button> </div> }
حاصل نهایی این مبحث را در دو شکل ذیل ملاحظه میکنید. صفحه index نمایش دهنده یک دکمه و در ادامه باز شدن یک فرم مودال، پس از کلیک بر روی دکمه ثبت اطلاعات.
الف) تزریق وابستگیها در سازنده کلاس
ب) تزریق وابستگیها در خواص عمومی کلاسها یا Setters injection
حالت الف متداولترین است و بیشتر زمانی کاربرد دارد که کار وهله سازی یک کلاس را میتوان راسا انجام داد. اما در فرمها یا یوزرکنترلهای ASP.NET Web forms به صورت پیش فرض کار وهله سازی فرمها و یوزرکنترلها توسط موتور ASP.NET انجام میشود و در این حالت اگر بخواهیم از تزریق وابستگیها استفاده کنیم، مدام به همان روش معروف Service locator و استفاده از container.Resolve در تمام قسمتهای برنامه میرسیم که آنچنان روش مطلوبی نیست.
اما ... در ASP.NET Web forms میتوان وهله سازی فرمها را نیز تحت کنترل قرار داد، که برای آن دو روش زیر وجود دارند:
الف) یک کلاس مشتق شده را از کلاس پایه PageHandlerFactory تهیه کنیم. این کلاس را پیاده سازی کرده و نهایتا بجای وهله ساز پیش فرض فرمهای موتور داخلی ASP.NET، در فایل وب کانفیگ برنامه استفاده کنیم. یک نمونه از پیاده سازی آنرا در اینجا میتوانید مشاهده کنید.
مشکلی که این روش دارد سازگاری آن با حالت Full trust است. یعنی برنامه شما در یک هاست Medium trust (اغلب هاستهای خوب) اجرا نخواهد شد.
ب) روش دوم، استفاده از یک Http Module است برای اعمال Setter injectionها، به صورت خودکار. اکنون که حالت الف را همه جا نمیتوان بکار برد یا به عبارتی نمیتوان وهله سازی فرمها را راسا در دست گرفت، حداقل میتوان خواص عمومی اشیاء صفحه تولید شده را مقدار دهی کرد که در ادامه، این روش را بررسی میکنیم.
تهیه ماژول انجام Setters injection به صورت خودکار در برنامههای ASP.NET Web forms به کمک StructureMap
پیشنیاز این بحث، مطلب «استفاده از StructureMap به عنوان یک IoC Container» میباشد که پیشتر مطالعه کردید (در حد نحوه نصب StructureMap و آشنایی با تنظیمات اولیه آن)
using System.Collections; using System.Web; using System.Web.UI; using StructureMap; namespace DI05.Core { /// <summary> /// تسهیل در کار تزریق خودکار وابستگیها در سطح فرمها و یوزرکنترلها /// </summary> public class StructureMapModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication app) { app.PreRequestHandlerExecute += (sender, e) => { var page = HttpContext.Current.Handler as Page; // The Page handler if (page == null) return; WireUpThePage(page); WireUpAllUserControls(page); }; } private static void WireUpAllUserControls(Page page) { // در اینجا هم کار سیم کشی یوزر کنترلها انجام میشود page.InitComplete += (initSender, evt) => { var thisPage = (Page)initSender; foreach (Control ctrl in getControlTree(thisPage)) { // فقط یوزر کنترلها بررسی شدند // اگر نیاز است سایر کنترلهای قرار گرفته روی فرم هم بررسی شوند شرط را حذف کنید if (ctrl is UserControl) { ObjectFactory.BuildUp(ctrl); } } }; } private static void WireUpThePage(Page page) { ObjectFactory.BuildUp(page); // برقراری خودکار سیم کشیها در سطح صفحات } private static IEnumerable getControlTree(Control root) { foreach (Control child in root.Controls) { yield return child; foreach (Control ctrl in getControlTree(child)) { yield return ctrl; } } } } }
پس از تهیه ماژول فوق، باید آنرا در فایل وب کانفیگ برنامه معرفی کرد:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpModules> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> </configuration>
مثالی از نحوه استفاده از StructureMapModule تهیه شده
فرض کنید لایه سرویس برنامه دارای اینترفیسها و کلاسهای زیر است:
namespace DI05.Services { public interface IUsersService { string GetUserEmail(int id); } } namespace DI05.Services { public class UsersService: IUsersService { public string GetUserEmail(int id) { //فقط جهت بررسی تزریق وابستگیها return "test@test.com"; } } }
using System; using StructureMap; using DI05.Services; namespace DI05 { public class Global : System.Web.HttpApplication { private static void initStructureMap() { ObjectFactory.Initialize(x => { x.For<IUsersService>().Use<UsersService>(); x.SetAllProperties(y => { y.OfType<IUsersService>(); }); }); } protected void Application_Start(object sender, EventArgs e) { initStructureMap(); } void Application_EndRequest(object sender, EventArgs e) { ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); } } }
مرحله آخر هم استفاده از سرویسهای برنامه به شکل زیر است:
using System; using DI05.Services; namespace DI05 { public partial class Default : System.Web.UI.Page { public IUsersService UsersService { set; get; } protected void Page_Load(object sender, EventArgs e) { lblEmail1.Text = string.Format("From Default Page: {0}", UsersService.GetUserEmail(1)); } } }
دریافت مثال کامل قسمت جاری
DI05.zip
یک نکتهی تکمیلی
برای ارتقاء نکات مطلب جاری به نگارش سوم StructureMap نیاز است موارد ذیل را لحاظ کنید:
الف) نصب بستهی وب آن
PM> Install-Package structuremap.web
ج) x.SetAllProperties را به x.Policies.SetAllProperties ویرایش کنید.
مدیریت سفارشی سطوح دسترسی کاربران در MVC
+ بیشتر از این نیازی نیست اطلاعاتی را در کوکیها یا جای دیگری ذخیره کنید. اطلاعاتی فراتر از این (مانند صفحه پروفایل یک شخص در سایت)، بسیار شخصی بوده و هر زمانیکه نیاز باشد، باید مستقلا از دیتابیس واکشی شوند.
Pipe یک متغیر یا عبارت را به عنوان ورودی دریافت کرده و آنرا به شکل دیگری برای نمایش تغییر میدهد. Pipeها معمولا در صفحات HTML مورد استفاده قرار میگیرند. با استفاده از عملگر Pipe (|) به شکل زیر میتوانید Pipe مورد نظر خود را اعمال کنید.
import { Component } from '@angular/core'; @Component({ selector: 'hero-birthday', template: `<p>The hero's birthday is {{ birthday | date }}</p>` }) export class HeroBirthdayComponent { birthday = new Date(1988, 3, 15); // April 15, 1988 }
در این مثال Pipe از قبل ساخته شده date را بر روی متغییر birthday اعمال میکنیم. خروجی کار به شکل زیر خواهد بود.
The hero's birthday is April 15, 1988
لازم به ذکر است Pipe هیچگونه اثری بر روی متغییر birthday نداشته و فقط نحوه نمایش آن را تغییر میدهد.
Pipeهای از پیش ساخته شده
در انگیولار ۲ یکسری Pipe از پیش ساخته شده مانند DatePipe، UpperCasePipe، LowerCasePipe، CurrencyPipe و PercentPipe وجود دارند که شما در تمامی صفحات HTML میتوانید بدون هیچ تنظیم اضافهای از آنها استفاده کنید. لیست Pipeهای از پیش ساخته شده را اینجا مشاهده کنید.
ارسال پارامتر به Pipe
<p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} </p>
مقدار پارامتر، هر نوع مجازی از Template expressions میتواند باشد (از جمله عبارت رشتهای یا حتی یک خصوصیت از کامپوننت). بنابراین در سرتاسر برنامه و در هر زمان میتوانید با تغییر پارامتر در لحظه، خروجی مدنظر خود را به کاربرنهایی نمایش دهید.
Template expressions: عباراتی (expressions) که بعد از اجرا توسط انگیولار، تبدیل به یک مقدار جهت نمایش در HTML، کامپوننت یا دایرکتیو میشود.
استفاده زنجیرهای از Pipeها
برای اعمال چند Pipe بر روی یک عبارت، میتوان از Pipeها به صورت پشت سر هم استفاده کرد. برای مثال در ادامه میخواهیم علاوه بر اعمال DatePipe با پارامتر fullDate جهت نمایش تاریخ به صورت Friday, April 15, 1988، حروف را نیز به صورت UpperCase نمایش دهیم. لازم به ذکر است برای نمایش حروف به صورت UpperCase از Pipe به همین نام استفاده میکنیم.
<p>The hero's birthday is {{ birthday | date:"fullDate" | uppercase}} </p>
خروجی کار به شکل زیر خواهد بود.
The hero's birthday is FRIDAY, APRIL 15, 1988
استفاده از Pipe در TypeScript
برای کسانیکه با انگیولار یک آشنایی دارند، Pipe در انگولار ۲ معادل filter در انگولار یک است. در انگیولار یک با تزریق سرویس filter$ به کنترلرها یا سرویسها، میتوانستیم filterهای تعریف شده شخصی و از پیش ساخته شده را جهت اعمال بر روی متغیرهای خود، استفاده کنیم. در انگیولار دو نیز این امکان فراهم شدهاست؛ ولی به سادگی تزریق filter$ نیست. یعنی لازم است علاوه بر تزریق Pipe به سرویس یا کامپوننتهای خود، Pipe مورد نظر خود را در لیست providers ماژول خود نیز اضافه کنید. برای نمونه اگر بخواهیم DatePipe را در component خود (نه در template) مورد استفاده قرار دهیم به شکل زیر عمل میکنیم:
import { Component } from '@angular/core'; // اضافه کردن DatePipe از @angular/common import { DatePipe } from '@angular/common'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app works!'; birthDay = new Date(1988, 3, 15); strBirthDay = ""; // تزریق DatePipe constructor(private datePipe: DatePipe) { this.strBirthDay = this.datePipe.transform(this.birthDay, 'yyyy-MM-dd'); } }
پس از وارد کردن DatePipe از angular/common@ به کامپوننت و تزریق آن از طریق سازنده کامپوننت، برای اعمال Pipe بر روی عبارت مورد نظر خود، از متد transform استفاده میکنیم.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { DatePipe } from '@angular/common' import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, HttpModule ], // افزودن Pipe به Providers providers: [DatePipe], bootstrap: [AppComponent] }) export class AppModule { }
نکتهای در مورد DatePipe و CurrencyPipe
Pipeهای Date و Currency به دلیل استفاده از شی Intl در داخل خود نیاز به ECMAScript Internationalization API دارند. مرورگرهای قدیمی و همچنین مرورگر Safari به دلیل عدم پشتیبانی از این قضیه به هنگام استفاده از این Pipeها دچار مشکل میشوند. برای حل این مشکل کافی است اسکریپت زیر را به صفحه خود اضافه کنید.
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>
در بخشهای بعدی نحوه ساخت Pipeهای سفارشی و همچنین نکات تکمیلی در مورد Pipeها را بررسی خواهیم کرد.
Debugger visualizers
سلام
خبرخوان (تکست و گرافیکی)، لیست و فید وبلاگهای «آی تی» ارائه شده است که وبلاگ شما نیز جز آنها قرار گرفته است.
این خبرخوان یکی دیگر از محصولات همکاری جمعی در پرشین بلاگرز است که با همکاری و مدیریت آقای سید یوسف منیری به وبلاگشهر عرضه شده است.
http://persianbloggers.blogspot.com/2008/12/it-p.html
پرشین بلاگرز شما را به بازدید و استفاده از این خبرخوان و 24 خبرخوان دیگر موجود دعوت میکند.