کتابخانه SweetAlert
برای آشنایی بیشتر با این helper توصیه میکنم ابتدا این مقاله را مطالعه نمایید.
به صورت پیش فرش WbebGrid صفحه بندی را به صورت خیلی ساده فقط با نمایش اعداد و جهت نماهای جلو و عقب، نشان میدهد که برای پروژههای رسمی تا حدودی جالب نیست.
در این مطلب قصد داریم از کتابخانهی bootstrap جهت صفحه بندی استفاده کنیم و در نهایت به صفحه بندی زیر برسیم:
برای اینکار، ابتدا قبل از هر چیزی به یک متد الحاقی برای انجام صفحه بندی سفارشی سازی شده، نیاز داریم که کدهای این متد به صورت زیر خواهد بود:
public static class WebGridExtensions { public static HelperResult PagerList( this WebGrid webGrid, WebGridPagerModes mode = WebGridPagerModes.NextPrevious | WebGridPagerModes.Numeric, string firstText = null, string previousText = null, string nextText = null, string lastText = null, int numericLinksCount = 5) { return PagerList(webGrid, mode, firstText, previousText, nextText, lastText, numericLinksCount, explicitlyCalled: true); } private static HelperResult PagerList( WebGrid webGrid, WebGridPagerModes mode, string firstText, string previousText, string nextText, string lastText, int numericLinksCount, bool explicitlyCalled) { int currentPage = webGrid.PageIndex; int totalPages = webGrid.PageCount; int lastPage = totalPages - 1; var ul = new TagBuilder("ul"); var li = new List<TagBuilder>(); if (ModeEnabled(mode, WebGridPagerModes.FirstLast)) { if (String.IsNullOrEmpty(firstText)) { firstText = "اولین"; } var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(0), firstText) }; if (currentPage == 0) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious)) { if (String.IsNullOrEmpty(previousText)) { previousText = "قبلی"; } int page = currentPage == 0 ? 0: currentPage - 1; var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(page), previousText) }; if (currentPage == 0) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.Numeric) && (totalPages > 1)) { int last = currentPage + (numericLinksCount / 2); int first = last - numericLinksCount + 1; if (last > lastPage) { first -= last - lastPage; last = lastPage; } if (first < 0) { last = Math.Min(last + (0 - first), lastPage); first = 0; } for (int i = first; i <= last; i++) { var pageText = (i + 1).ToString(CultureInfo.InvariantCulture); var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(i), pageText) }; if (i == currentPage) { part.MergeAttribute("class", "active"); } li.Add(part); } } if (ModeEnabled(mode, WebGridPagerModes.NextPrevious)) { if (String.IsNullOrEmpty(nextText)) { nextText = "بعدی"; } int page = currentPage == lastPage ? lastPage: currentPage + 1; var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(page), nextText) }; if (currentPage == lastPage) { part.MergeAttribute("class", "disabled"); } li.Add(part); } if (ModeEnabled(mode, WebGridPagerModes.FirstLast)) { if (String.IsNullOrEmpty(lastText)) { lastText = "آخرین"; } var part = new TagBuilder("li") { InnerHtml = GridLink(webGrid, webGrid.GetPageUrl(lastPage), lastText) }; if (currentPage == lastPage) { part.MergeAttribute("class", "disabled"); } li.Add(part); } ul.InnerHtml = string.Join("", li); var html = ""; if (explicitlyCalled && webGrid.IsAjaxEnabled) { var span = new TagBuilder("span"); span.MergeAttribute("data-swhgajax", "true"); span.MergeAttribute("data-swhgcontainer", webGrid.AjaxUpdateContainerId); span.MergeAttribute("data-swhgcallback", webGrid.AjaxUpdateCallback); span.InnerHtml = ul.ToString(); html = span.ToString(); } else { html = ul.ToString(); } return new HelperResult(writer => { writer.Write(html); }); } private static String GridLink(WebGrid webGrid, string url, string text) { TagBuilder builder = new TagBuilder("a"); builder.SetInnerText(text); builder.MergeAttribute("href", url); if (webGrid.IsAjaxEnabled) { builder.MergeAttribute("data-swhglnk", "true"); } return builder.ToString(TagRenderMode.Normal); } private static bool ModeEnabled(WebGridPagerModes mode, WebGridPagerModes modeCheck) { return (mode & modeCheck) == modeCheck; } }
پس از آن در View یی که اطلاعات را نمایش میدهید، فقط لازم است کد زیر را اضافه نمایید:
<div> @grid.PagerList(mode: WebGridPagerModes.All) </div>
در ادامه برای تکمیل بحث مثالی را از نحوهی نمایش اطلاعات و صفحه بندی سفارشی نشان خواهیم داد:
PartialView لازم برای نمایش اطلاعات
تنظیمات لازم گرید :
@{ WebGrid grid = new WebGrid(Model, rowsPerPage: 10, ajaxUpdateContainerId: "grid"); var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1); }
@grid.Table( tableStyle: "table table-striped table-hover", headerStyle: "webgrid-header", alternatingRowStyle: "webgrid-alternating-row", selectedRowStyle: "webgrid-selected-row", rowStyle: "webgrid-row-style", columns: grid.Columns( grid.Column(columnName: "Name", header: "نام استان", style: "myfont"), grid.Column(columnName: "NameEn", header: "نام استان ( انگلیسی )", style: "myfont"), grid.Column(header: "", format: item => @Html.ActionLink("مدیریت شهرها", actionName: MVC.Admin.City.ActionNames.Index, controllerName: MVC.Admin.City.Name, routeValues: new {Code=item.Code },htmlAttributes:null)), grid.Column(header: "", style: "text-align-center-col smallcell", format: item => @Html.ActionLink(linkText: "ویرایش", actionName: "Edit", controllerName: "Province", routeValues: new { area = "Admin", code = item.Code }, htmlAttributes: new { @class = "btn-sm btn-info vertical-center" })) ) ) <div> @grid.PagerList(mode: WebGridPagerModes.All) </div>
خروجی حاصل به صورت زیر خواهد بود :
اگر طبق توضیحات بالا عمل کرده باشید، در نهایت صفحه بندی شما به صورت عمودی نمایش داده میشود؛ یعنی هر کدام از شماره صفحات در یک سطر. دلیل آن هم این است که تگ ul، کلاس .pagination را ندارد. در کدهای بوت استراپ تعریف شده است که تمام li هایی که به صورت مستقیم داخل کلاس .pagination هستند خصوصیات مورد نظر را بگیرند.
برای این کار دو راه حل وجود دارد :
راه حل اول: تغییر کدهای css
کدهای نوشته شده برای صفحه بندی در بوت استراپ را از حالت زیر:
.pagination > li
.pagination li
یادآوری : علامت < در CSS یعنی به صورت مستقیم و در شاخهی اول.
راه حل دوم - افزودن کلاس .pagination به تگ ul:
ابتدا کلاس .pagination را از تگ div حذف نمایید:
<div > @grid.PagerList(mode: WebGridPagerModes.All) </div>
و در کدهایی کلاس WebGridExtensions، در قسمتی که تگ ul اصافه میشود، کلاس مورد نظر را به آن اضافه میکنیم:
var ul = new TagBuilder("ul"); ul.AddCssClass("pagination");
دانلود کدهای این مثال
نکتهای در مورد Webgrid
اگر نیاز داشتید به یکباره تمام اطلاعات را در گرید لود نکنید و به صورت n تاn تا رکوردها را نمایش دهید، در این حالت پس از پاس دادن لیستی از اطلاعات به View مورد نظر لازم است تعداد کل رکوردها را در یک متغییر به سمت View بفرستید. این کار به این دلیل میباشد که بتوان صفحه بندی را تولید کرد. برای این کار در بخش تنظیمات Webgrid مقدار source را برابر null قرار دهید و از قطعه کد زیر جهت بایند کردن گرید، بعد از کدهای تنظیمات WebGrid استفاده نمایید:
grid.Bind(Model, rowCount: (int)ViewBag.PageCount);
برای مثال فرض کنید یک View ویژه را بر اساس جدول و یا جداول بانک اطلاعاتی خود طراحی کردهاید:
using (var db = new BloggingContext()) { db.Database.ExecuteSqlCommand( @"CREATE VIEW View_BlogPostCounts AS SELECT Name, Count(p.PostId) as PostCount from Blogs b JOIN Posts p on p.BlogId = b.BlogId GROUP BY b.Name"); }
public class BlogPostsCount { public string BlogName { get; set; } public int PostCount { get; set; } }
سپس برای معرفی آن به Context باید دو مرحله انجام شود:
الف) این کلاس به صورت DbQuery در Context معرفی میشود:
public class BloggingContext : DbContext { public DbQuery<BlogPostsCount> BlogPostCounts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Query<BlogPostsCount>().ToView("View_BlogPostCounts") .Property(v => v.BlogName).HasColumnName("Name"); }
using (var db = new BloggingContext()) { var postCounts = db.BlogPostCounts.ToList();
مثال کامل این نکته
با کلیدهای ترکیبی Ctrl + Tab و Ctrl + Shift + Tab میتوانید بین پنجرههای Active جابجا شوید. با کلیدهای جهتنما هم میتوانید به بقیه پنجرهها از جمله Properties منتقل شوید. ناگفته نماند که این امکان در اکثر قسمتهای ویندوز و نرم افزارهایی مثل کروم و حتی SSMS نیز وجود دارد.
به طور کلی کلید شیفت برای دو کاربرد «برگشت از عملیات» و «انتخاب» استفاده میشود.
برای اینکه جستجویی را فیلتر کنید، میتوانید از الگوهای زیر استفاده کنید:
f جستجو در فایلها؛ r برای فایلهای اخیر؛ t جستجو در تایپها (مثل کلاس)؛ m برای جستجو در اعضا (متدها، پراپرتیها و Eventها).
برای مثال: اگر بنویسید t Controller، فقط Typeها را جستوجو میکند و نمایش میدهد.
۳) کلیپبورد چرخشی : «Ctrl + Shift + V»
ویژوال استودیو تا ۲۰ مقدار کپیشده قبلی را در حافظه خود ذخیره نگه میدارد. با دکمههای ترکیبی گفته شده میتوان از مقادیر قبلی کلیپبورد هم استفاده کرد. این قابلیت از ویژوال استودیو ۲۰۰۳ وجود داشته است.
۴) اجرای سریع : « Ctrl + Q »
برای دسترسی سریع به تنظیمات ویژوال استودیو (مثل فونت) یا بازکردن پنجرههایی که کلید میانبر استاندارد برای آنها تعریف نشدهاست، میتوان از این منو استفاده کرد.
حتی میتوان @nuget را نوشت، سپس اسم پکیج موردنظر نوشته میشود. با انجام این کار Manage NuGet Packages باز میشود و پکیجهای مرتبط نمایش داده میشوند.
۵) پیدا کردن ابتدا و انتهای براکت: « Ctrl + ] »
کاربرد این روش در هنگامی است با Legacy codeی مواجه میشوید که بلوکهای بزرگی از کد را دارد که فهم ابتدا و انتهای آن سخت است. با زدن این کلیدها میتوانید به محلی که براکت باز و بسته مربوطه هست، منتقل شوید.
۶) رفتن به محل کرسر قبلی: « Ctrl + - »
با زدن کلیکهای کنترل و Dash میتوانید به جاهایی که قبلا کرسر بوده است، بروید. البته اگر Shift را هم بگیرید، برعکس این کار انجام میشود.
منابع:
Gulp #1
- راه اندازی یک وب سرور
- بارگذاری مجدد مرورگر به صورت خودکار بعد از ذخیرهی هر فایل
- تبدیل پیش پردازندههای CSS مانند LESS, SASS به CSS
- بهینه سازی فایلهای asset شامل CSS,JS و همچنین عکسها
چرا گالپ ؟
نصب گالپ
sudo npm install -g gulp
gulp -v
MongoDB #12
>db.COLLECTION_NAME.ensureIndex({KEY:1})
>db.mycol.ensureIndex({"title":1})
>db.mycol.ensureIndex({"title":1,"description":-1})
پارامتر | نوع داده | توضیحات |
background | Boolean | ایندکس را در پس زمینه میسازد؛ بنابراین عمل ساخت ایندکس، بقیه فعالیتهای پایگاه داده را مسدود یا متوقف نمیکند. برای این کار مقدار را true تعیین کنید. مقدار پیش فرض false است. |
unique | Boolean | یک ایندکس یکتا را میسازد. در این حالت مجموعه، اجازه درج سندهایی را که مقدار کلید یا کلیدهای آنها از قبل وجود دارند، نخواهد داد. برای ساخت یک ایندکس یکتا مقدار را true تعیین کنید. مقدار پیش فرض آن false است. |
name | string | نام ایندکس. اگر تعیین نشود MongoDB نام خودکاری را توسط الحاق نام فیلدها و ترتیب مرتب سازی تولید میکند. |
dropDups | Boolean | ایندکسی را بر روی فایل میسازد که ممکن است مقادیر تکراری داشته باشد. MongoDB فقط اولین پیشامد از یک کلید را ایندکس میکند و همه سندهایی را که پیشامد ثانویه کلید هستند، از مجموعه حذف میکند. برای ساخت ایندکس یکتا مقدار را true تعیین کنید. مقدار پیش فرض آن false است. |
sparse | Boolean | اگر مقدار آن true تعیین شود، ایندکس فقط به سندهایی با فیلد تعیین شده رجوع میکند. این ایندکسها از فضای کمی استفاده کرده و در برخی موقعیتها متفاوت رفتار میکنند. مقدار پیش فرض آن false است. |
expireAfterSeconds | integer | تعیین یک مقدار به ثانیه بعنوان TTL، برای کنترل کردن اینکه چه مدت MongoDB اسناد را در این مجموعه نگه دارد. |
v | index version | شمارهی نسخه ایندکس. نسخهی ایندکس پیش فرض بستگی به نسخه mongod درحال اجرای هنگام ساخت ایندکس دارد. |
weights | document | وزن یک عدد بین 1 تا 99999 است و مشخص کنندهی اهمیت فیلد با دیگر فیلدهای ایندکس شده از نظر امتیاز است. |
default_language | string | برای یک ایندکس متنی، زبانی برای تعیین کردن لیست کلمات متوقف کننده و نقش هایی برای ریشهیابی و نشانه گذاری کلمات. |
language_override | string | برای یک ایندکس متنی، تعیین کننده نام فیلد در سند که شامل زبانی برای لغو کردن زبان پیش فرض است. مقدار پیش فرض آن language است. |
SQL Server express edition نگارش مجانی و ساده شدهی اس کیوال سرور است. این نگارش مجانی فاقد SQL Server agent برای زمان بندی انجام امور تکراری، برای مثال تهیه بک آپهای خودکار است. این مورد در کل ایرادی محسوب نمیشود زیرا میتوان این عملیات را با استفاده از سیستم استاندارد scheduled tasks ویندوز نیز پیاده سازی کرد.
برنامه خط فرمان سورس بازی به نام ExpressMaint موجود است که میتواند از دیتابیسهای اس کیوال سرور اکسپرس (و غیر اکسپرس) بک آپ تهیه کند. فقط کافی است این برنامه را به عنوان یک scheduled task ویندوز معرفی کنیم تا در زمانهای تعیین شده در مکانهایی مشخص، بک آپ تهیه کند. همچنین این برنامه فایلهای بک آپ تهیه شده را نیز تعیین اعتبار میکند.
با پارامترهای خط فرمان آن در اینجا میتوانید آشنا شوید. خلاصه کاربردی آن را به صورت چند دستور در ادامه مرور خواهیم کرد.
الف) یک فایل bat را با محتوای زیر درست کنید :
C:\backup\expressmaint.exe -S (local)\sqlexpress -D ALL_USER -T DB -R C:\backup -RU WEEKS -RV 2 -B C:\backup -BU DAYS -BV 2 -V -C
ب) برای اجرای زمان بندی شدهی این فایل bat تهیه شده، دستورات زیر را در خط فرمان اجرا کنید (فرض بر این است که فایل bat تهیه شده در مسیر مشخص شده C:\backup\backup.bat قرار دارد) :
AT 23:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
AT 11:30 /EVERY:m,t,w,th,f,s,su C:\backup\backup.bat
روشی که در اینجا ذکر شد منحصر به نگارش express نیست و با کلیه نگارشهای SQL Server سازگار است.
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[myIf]', inputs:["myIf"] }) export class UnlessDirective { constructor(private _templateRef:TemplateRef<any>,private _viewContainerRef:ViewContainerRef) { } set myIf(condition:true) { if(!condition) { this._viewContainerRef.clear(); } else{ this._viewContainerRef.createEmbeddedView(this._templateRef); } } }
سپس ورودی را به شکل متد نوشته و عبارت set را جلوی آن مینویسیم. عبارت set به این معناست که وقتی مقداری به این ورودی set شد، متد myIf را اجرا کن. اگر نتیجه شرط ورودی false شده باشد، محتوای ViewContainer پاک خواهد شد و اگر محتوای true داشته باشد templateRef به آن اضافه خواهد شد.
برای استفادهی از دایرکتیو نوشته شده، از تکه کد زیر استفاده میکنیم:
<input type="text" #x (keyup)="0"/> <div *myIf="x.value=='yes'"> it means we are on condition </div>
تمامی پاسخهای دریافتی از Web API توسط Client، باید در قالب کدهای وضعیت HTTP باشند. دو کلاس جدید با نامهای HttpResponseMessage و HttpResponseException همراه با ASP.NET MVC 4 معرفی شده اند که ارسال کدهای وضعیت پردازش درخواست به Client را آسان میسازند. به عنوان مثال، ارسال وضعیت برای چهار عمل اصلی بازیابی، ایجاد، آپدیت و حذف رکورد را بررسی میکنیم.
بازیابی رکورد
بر اساس مستندات پروتوکل HTTP، در صورتی که منبع درخواستی Client پیدا نشد، باید کد وضعیت 404 برگشت داده شود. این حالت را در متد ذیل پیاده سازی کرده ایم.
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)); } return item; }
ایجاد رکورد
برای ایجاد رکورد، Client درخواستی از نوع POST را همراه با دادههای رکورد در بدنهی درخواست به Server ارسال میکند. در ذیل، پیاده سازی ساده ای از این حالت را مشاهده میکنید.
public Product PostProduct(Product item) { item = repository.Add(item); return item; }
- کد وضعیت پردازش درخواست: به طور پیش فرض، Web API، کد 200 را در پاسخ ارسال میکند، اما بر اساس مستندات پروتوکل HTTP، زمانی که یک درخواست از نوع POST منجر به تولید منبعی می-شود، Server باید کد وضعیت 201 را به Client برگشت بدهد.
- آدرس منبع جدید ایجاد شده: بر اساس مستندات پروتوکل HTTP، زمانی که منبعی بر روی Server ایجاد میشود، باید آدرس منبع جدید ایجاد شده از طریق هدر Location به Client ارسال شود.
public HttpResponseMessage PostProduct(Product item) { item = repository.Add(item); var response = Request.CreateResponse(HttpStatusCode.Created, item); string uri = Url.Link("DefaultApi", new { id = item.Id }); response.Headers.Location = new Uri(uri); return response; }
آپدیت رکورد
آپدیت با استفاده از درخواستهای از نوع PUT انجام میگیرد. یک مثال ساده در این مورد.
public void PutProduct(int id, Product product) { product.Id = id; if (!repository.Update(product)) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)); } }
حذف یک رکورد
حذف یک رکورد، با استفاده از درخواستهای از نوع DELETE انجام میگیرد. یک مثال ساده در این مورد.
public HttpResponseMessage DeleteProduct(int id) { repository.Remove(id); return new HttpResponseMessage(HttpStatusCode.NoContent); }
فراخوانی متدها و مدیریت کدهای وضعیت HTTP در سمت Client
حال ببینیم چگونه میتوان از متدهای قبل در سمت Client استفاده و خطاهای احتمالی آنها را مدیریت کرد.
بهتر است مثال را برای حالتی که در آن رکوردی آپدیت میشود بررسی کنیم. کدهای مورد نیاز برای فراخوانی متد PutProduct در سمت Client به صورت ذیل است.
var id = $("#myTextBox").val(); $.ajax({ url: "/api/Test/" + id, type: 'PUT', data: { Id: "1", Name: "Tomato Soup", Category: "Groceries", Price: "1.39M" }, cache: false, statusCode: { 200: function (data) { alert("آپدیت انجام شد"); }, 404: function () { alert("خطا در آپدیت"); } } });
در حالتی که آپدیت با موفقیت همراه باشد، بدنهی پاسخ به شکل ذیل است.
و در صورتی که خطایی رخ دهد، بدنهی پاسخ دریافتی به صورت ذیل خواهد بود.