- معرفی Kendo UI
- بررسی ساختار ویجتهای وب Kendo UI
- کار با Kendo UI DataSource
- صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid
- استفاده از Kendo UI templates
- فرمت کردن اطلاعات نمایش داده شده به کمک Kendo UI Grid
- فعال سازی عملیات CRUD در Kendo UI Grid
- استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC
- اعتبار سنجی ورودیهای کاربر در Kendo UI
- Kendo UI MVVM
- یکپارچه سازی سیستم اعتبارسنجی ASP.NET MVC با Kendo UI validator
- بررسی ویجت Kendo UI File Upload
- استفاده از ویجت آپلود KendoUI بصورت پاپ آپ
- رسم نمودار توسط Kendo Chart
- استفاده از Kendo UI TreeView به همراه یک منبع داده راه دور
- ایجاد Drop Down Listهای آبشاری توسط Kendo UI
- فعال سازی قسمت آپلود تصویر و فایل Kendo UI Editor
WebDAV استانداردی است بر روی پروتکل HTTP که Requestها و Responseهای مدیریت یک فایل را بر روی سرویس دهنده وب، تشریح میکند.
برای درک چرایی وجود این استاندارد بهتر است ذهن خود را معطوف به نحوهی عملکرد سیستم فایل در OS کنیم که شامل APIهای خاص برای دسترسی نرم افزارهای گوناگون به فایلهای روی یک سیستم است.
حال فکر کنید یک سرور Cloud راه اندازی نمودهاید که قرار است مدیریت فایلها و پروندههای Office را بر عهده داشته باشد و چون امکان ویرایش اسناد Office بر روی وب را ندارید، نیاز است تا اجازه دهید نرم افزارهای Office مستقیما فایلها را از روی سرور شما باز کنند و بعد از تغییرات، به جای ذخیره در سیستم local، محتوا را به فایل روی سرور ارسال کنند.
در مفهوم web عملا این کار غیر استاندارد و نادرست است. همه درخواستها و جوابها باید بر روی پروتکل Http باشند. خوب حال تصور کنید نرم افزارهای Office قابلیت آن را داشته باشند که به جای تحویل محتوا به سیستم عامل برای ذخیرهی آن بر روی سیستم local، محتوا را به یک آدرس ارسال نمایند و پشت آن آدرس، متدی باشد که بتواند به درخواست رسیده، به درستی پاسخ دهد.
این یعنی باید سمت سرور متدی با قابلیت ارسال پاسخهای درست و در سمت کلاینت نرم افزاری با قابلیت ارسال درخواستهای مناسب وجود داشته باشد.
WebDAV استاندارد تشریح محتوای درخواستها و پاسخهای مربوط به مدیریت فایلها است.
خوشبختانه نرم افزارهای Office و بسیاری از نرم افزارهای دیگر، استاندارد WebDAV را پشتیبانی میکنند و فقط لازم است برای سرورتان متدی با قابلیت پشتیبانی از درخواستهای WebDAV پیاده سازی نمایید و البته متاسفانه کتابخانههای سورس باز چندانی برای WebDAV در سرور دات نت وجود ندارد. من ماژولی را برای کار با WebDAV نوشتم و سورسش را در Git قرار دادم. برای این مثال هم از همین کتابخانه استفاده میکنم.
ابتدا یک پروژهی وب MVC ایجاد نمایید و پکیج xDav را از nugget نصب کنید.
PM> Install-Package xDav
اگر به web.config نگاهی بیاندازیم میبینیم یک module به نام xDav به وب سرور اضافه شده که بررسی درخواستهای WebDAV را به عهده دارد.
<system.webServer> <modules> <add name="XDav" type="XDav.XDavModule, XDav" /> </modules> </system.webServer>
همچنین یک Section جدید هم به config برای پیکربندی xDav اضافه شده است.
<XDavConfig Name="xdav"> <FileLocation URL="xdav" PathType="Local"></FileLocation> </XDavConfig>
خاصیت Name برای xDav نشانگر درخواستهایی است که باید توسط این ماژول اجرا شوند. در اینجا یعنی درخواستهایی که آدرس آنها شامل "/xdav/" باشد، توسط این ماژول Handle میشوند. عبارت بعد از مقدار Name در URL هم طبیعتا نام فایل مورد نظر شماست.
FileLocation آدرس پوشه ای است که فایلها در آن ذخیره و یا بازخوانی میشوند. اگر FileType با مقدار Local تنظیم شود، یعنی باید یک پوشه به نام خاصیت URL که در اینجا xdav است در پوشهی اصلی وب شما وجود داشته باشد و اگر با Server مقدار دهی شود URL باید یک آدرس فیزیکی بر روی سرور داشته باشد . مثل "c:\webdav"URL=
ما در این مثال مقادیر را به صورت پیشفرض نگه میداریم. یعنی باید در پوشهی وب، یک Folder با نام xdav ایجاد کنیم.
در ادامه چند فایل word را برای تست در این پوشه کپی میکنم.
می خواهیم در صفحه Index، لیستی از فایلهای درون این پوشه را نمایش دهیم طوری که در صورت کلیک بر روی هر کدام از آنها، آدرس WebDav فایل مورد نظر را به Word ارسال کنیم.
بعد از نصب Office، در registry چند نوع Url تعریف میشود که معرف اپلیکیشنی است که آدرس به آن فرستاده شود. این دقیقا همان چیزیست که ما به آن نیاز داریم. کافیست آدرس WebDav فایل را بعد از عبارت " ms-word:ofe|u| " در یک لینک قرار دهیم تا آدرس به نرم افزار Word ارسال شود. یعنی آدرس URL باید این شکلی باشد:
ms-word:ofe|u|http://Webaddress/xdav/filename
Webaddress آدرس وبسایت و filename نام فایل مورد نظرمان است. عبارت /xdav/ هم که نشان میدهد درخواست هایی که این آدرس را دارند باید توسط ماژول xDav پردازش شوند.
کلاسی با نام DavFile در پوشهی Model ایجاد میکنم:
public class DavFile { public string Name { get; set; } public string Href(string webAddress) { return string.Format("ms-word:ofe|u|http://{0}/xdav/{1}", webAddress, Name); } }
اکشن متد Index را در Home Controller، مانند زیر تغییر دهید:
var dir = new DirectoryInfo(XDav.Config.ConfigManager.DavPath); var model = dir.GetFiles().ToList() .Select(f => new DavFile() { Name = f.Name }); return View(model);
یک لیست از فایل هایی که در پوشهی webDav قرار دارند تهیه میکنیم و به View ارسال میکنیم. View را هم مثل زیر بازنویسی میکنیم.
@model IEnumerable<WebDavServer.Models.DavFile> <h1> File List </h1> <ul> @foreach (var item in Model) { <li> <a href="@Html.Raw(item.Href(ViewContext.HttpContext.Request.Url.Authority))"> @Html.Raw(item.Name) </a></li> } </ul>
قرار است به ازای هر فایل، لینکی نمایش داده شود که با کلیک بر روی آن، آدرس فایل به word ارسال میشود. بعد از ثبت تغییرات، word محتوا را به همان آدرس ارسال میکند و ماژول xDav محتوا را در فایل فیزیکی سرور ذخیره خواهد کرد.
برنامه را اجرا کنید و بر روی فایلها کلیک نمایید. اگر نرم افزار Office روی کامپیوترتان باز باشد با کلیک بر روی هر کدام از فایلها، فایل word باز شده و میتوایند محتوا را تغییر داده و ذخیره نمایید.
نرم افزار کلاینت (word) درخواست هایی با verbهای مشخص که در استاندارد WebDav ذکر شده به آدرس مورد نظر میفرستد. سرور WebDav درخواست را بر اساس Verb آن Request پردازش کرده و Response استاندارد را ایجاد میکند.
نرم افزار word پس از دریافت یک URL، به جای فرمت فیزیکی فایل، درخواست هایی را با تایپهای Option, Head, lock, get, post و unlock ارسال میکند. محتوای درخواست و پاسخ هر کدام از تایپها در استاندارد webDav تعریف شده و ماژول xDav آن را پیاده سازی نموده است.
ASP.NET MVC #12
- برای حالت nullable هم همین فایل کافی است. یعنی در اصل بهتر است DisplayTemplate برای حالت nullable نوشته شود که برای هر دو حالت مورد استفاده قرار خواهد گرفت.
- در مورد validator هم میتونید یک attribute سفارشی تهیه کنید. در قسمت 13 راه کلی انجام کار رو توضیح دادم. برای تاریخ شمسی بحث مفصلی است. یک کلاس قبلا در این مورد تهیه کردم : (^) البته این فقط یک ایده برای شروع است که چه فرمتهایی میتونه وارد بشه و قابل قبول باشه.
تغییرات مورد نیاز سمت کلاینت جهت فعال سازی جستجو در jqGrid
در سمت کلاینت، در حین تعریف ستونها، ابتدا باید توسط مقدار دهی خاصیت search، ستونهای مشارکت کنندهی در حین جستجو را مشخص کرد:
colModel: [ { name: 'Name', index: 'Name', align: 'right', width: 200, search: true, stype: 'text', searchoptions: { sopt: searchOptions } }, { name: 'Supplier.Id', index: 'Supplier.Id', align: 'right', width: 200, search: true, stype: 'select', edittype: 'select', searchOperators: true, searchoptions: { sopt: searchOptions, dataUrl: '@Url.Action("SuppliersSelect","Home")' } } ],
- stype، روش مقایسهی مقادیر را مشخص میکند. مقدار پیش فرض آن text است و مقادیری مانند int، integer، float، number، numeric، date و datetime را میپذیرد.
- searchoptions برای تنظیم جزئیات نحوهی جستجوی بر روی فیلدها بکار میرود. توسط sopt میتوان آرایهای با مقادیر ذیل را مقدار دهی کرد:
var searchOptions = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', 'bw', 'bn', 'in', 'ni', 'ew', 'en', 'cn', 'nc'];
البته باید دقت داشت که آرایه فوق کاملترین حالت ممکن است و ضرورتی ندارد تمام حالات را برای یک فیلد تعریف کرد. چون برای مثال جستجو cn یا contains برای مقادیر رشتهای معنا دارد و نه سایر حالات.
- searchoptions گزینههای دیگری را نیز میتواند شامل شود. برای مثال در حین نمایش جستجوی داخل ردیفی یا صفحهی دیالوگ مخصوص آن، قصد داریم فیلد نام تولید کننده را توسط یک drop down نمایش دهیم و نه یک text box پیش فرض. برای این منظور dataUrl مقدار دهی شدهاست.
SuppliersSelect آن به اکشن متد ذیل اشاره میکند که لیست تولید کنندگان را با فرمت لیستی از SelectListItemها به یک partial view تولید کنندهی دراپ داون ارسال میکند:
public ActionResult SuppliersSelect() { var list = ProductDataSource.LatestProducts; var suppliers = list.Select(x => new SelectListItem { Text = x.Supplier.CompanyName, Value = x.Supplier.Id.ToString(CultureInfo.InvariantCulture) }).ToList(); return PartialView("_SelectPartial", suppliers); }
@model IList<SelectListItem> @Html.DropDownList("srch", Model)
- اگر دقت کرده باشید، نام فیلد تولید کننده با Supplier.Id مقدار دهی شدهاست. علت اینجا است که در زمان استفاده از drop down، مقدار Id آیتم انتخابی، به سرور ارسال میشود. به همین جهت کار کردن پویا با Supplier.Id سادهتر خواهد بود.
- برای نمایش دیالوگ و یا نوار ابزار توکار جستجو، میتوان دکمههایی را به فوتر گرید اضافه کرد:
$(document).ready(function () { $('#list').jqGrid({ // ... مانند قبل و توضیحات فوق }) .jqGrid('navGrid', '#pager', { add: false, edit: false, del: false }, {}, // default settings for edit {}, // default settings for add {}, // delete instead that del:false we need this { // search options multipleSearch: true, closeOnEscape: true, closeAfterSearch: true, ignoreCase: true }) .jqGrid('navButtonAdd', "#pager", { caption: "نوار ابزار جستجو", title: "Search Toolbar", buttonicon: 'ui-icon-search', onClickButton: function () { toolbarSearching(); } }); }); function toolbarSearching() { $('#list').filterToolbar({ groupOp: 'OR', defaultSearch: "cn", autosearch: true, searchOnEnter: true, searchOperators: true, // فعال سازی منوی اپراتورها stringResult : true // وجود این سطر سبب میشود تا اپراتورها به سرور ارسال شوند }); }; function singleSearching() { $('#list').searchGrid({ closeAfterSearch: true }); }; function advancedSearching() { $('#list').searchGrid({ multipleSearch: true, closeAfterSearch: true }); };
- با استفاده از متد jqGrid و پارامتر navGrid، در ناحیهی pager گرید، تنظیمات جستجو را فعال کردهایم.
multipleSearch به معنای امکان جستجوی همزمان بر روی بیش از یک فیلد است. closeOnEscape سبب بسته شدن صفحهی دیالوگ جستجو با فشردن دکمهی ESC میشود. اگر closeAfterSearch به true تنظیم نشود، صفحهی دیالوگ جستجو پس از جستجو، در صفحه باقی مانده و بسته نخواهد شد.
- این دکمهی جستجو، جزو موارد توکار jqGrid است. اگر قصد داشته باشیم یک دکمهی سفارشی دیگر را نیز اضافه کنیم، مجددا از متد jqGrid با پارامتر navButtonAdd در ناحیهی pager استفاده خواهیم کرد. کلیک بر روی آن سبب اجرای متد toolbarSearching میشود.
در اینجا حداقل سه نوع جستجو را میتوان فعال کرد:
- filterToolbar که سبب نمایش نوار ابزار جستجو، دقیقا بالای ستونهای جدول میشود.
- searchGrid که صفحهی دیالوگ مستقلی را جهت جستجو به صورت پویا تولید میکند. اگر خاصیت multipleSearch آن به true تنظیم نشود، این جستجو هربار تنها بر روی یک فیلد قابل انجام خواهد بود و برعکس.
- در حالت جستجوی نوار ابزاری، اگر خواص searchOperators و stringResult به true تنظیم شوند، مانند تصویر ذیل، به ازای هر ستون میتوان از عملگرهای مختلفی استفاده کرد. در غیراینصورت جستجوی انجام شده بر اساس groupOp و defaultSearch پیش فرض انجام میشود. یعنی And یا Or تمام موارد تنها در حالت مثلا contains یا تساوی و امثال آن و نه حالت پیشرفتهی انتخاب عملگرها توسط کاربر.
یک نکته
اگر میخواهید صفحهی جستجو در وسط صفحه ظاهر شود، میتوانید از تنظیمات CSS ذیل استفاده کنید:
/* align center search popup in jqgrid */ .ui-jqdialog { display: none; width: 300px; position: absolute; padding: .2em; font-size: 11px; overflow: visible; left: 30% !important; top: 40% !important; }
پردازش سمت سرور جستجوی پویای jqGrid
کدهای سمت سرور، با کدهای استفاده از dynamic LINQ مایکروسافت یکی است. با این تفاوت که اینبار قسمت where این کوئری نیز پویا میباشد. پیشتر قسمت order by را پویا پردازش کرده بودیم. برای ساخت where پویا که در dynamic LINQ به خوبی پشتیبانی میشود، باید ابتدا ساختار اطلاعات ارسالی به سرور را آنالیز کنیم:
//single field search //_search=true&nd=1403935889318&rows=10&page=1&sidx=Id&sord=asc&searchField=Id&searchString=4444&searchOper=eq&filters= //multi-field search //_search=true&nd=1403935941367&rows=10&page=1&sidx=Id&sord=asc&filters=%7B%22groupOp%22%3A%22AND%22%2C%22rules%22%3A%5B%7B%22field%22%3A%22Id%22%2C%22op%22%3A%22eq%22%2C%22data%22%3A%2244%22%7D%2C%7B%22field%22%3A%22SupplierID%22%2C%22op%22%3A%22eq%22%2C%22data%22%3A%221%22%7D%5D%7D&searchField=&searchString=&searchOper= // filters -> {"groupOp":"AND","rules":[{"field":"All","op":"cn","data":"fffff"},{"field":"Price","op":"bn","data":"ffff"}]} //toolbar search //_search=true&nd=1403935593036&rows=10&page=1&sidx=Id&sord=asc&Id=2&Name=333&SupplierID=1&CategoryID=1&Price=44
- در تمام این حالات پارامتر _search مساوی true است (تفاوت آن با درخواست اطلاعات معمولی).
- در حالت جستجوی نوار ابزاری، اگر گزینههای searchOperators و stringResult به true تنظیم نشوند، حالت toolbar search فوق را شاهد خواهیم بود. در غیراینصورت به حالت multi-field search سوئیچ میشود.
- در حالت جستجوی تک فیلدی توسط صفحه دیالوگ جستجوی jqGrid، فیلد در حال جستجو توسط searchField و مقدار آن توسط searchString به سرور ارسال شدهاند. مابقی پارامترها نال هستند.
- در حالت جستجوی چند فیلدی توسط صفحه دیالوگ جستجوی jqGrid، اینبار filters مقدار دهی شدهاست و سایر پارامترها نال هستند. مقدار filters ارسالی، در حقیقت یک شیء JSON است با ساختار کلی ذیل:
{ "groupOp": "AND", "groups" : [ { "groupOp": "OR", "rules": [ { "field": "name", "op": "eq", "data": "England" }, { "field": "id", "op": "le", "data": "5"} ] } ], "rules": [ { "field": "name", "op": "eq", "data": "Romania" }, { "field": "id", "op": "le", "data": "1"} ] }
public class SearchFilter { public string groupOp { set; get; } public List<SearchGroup> groups { set; get; } public List<SearchRule> rules { set; get; } } public class SearchRule { public string field { set; get; } public string op { set; get; } public string data { set; get; } public override string ToString() { return string.Format("'{0}' {1} '{2}'", field, op, data); } } public class SearchGroup { public string groupOp { set; get; } public List<SearchRule> rules { set; get; } }
اگر این موارد را کنار هم قرار دهیم، به متدی عمومی ApplyFilter با امضای ذیل خواهیم رسید.
public IQueryable<T> ApplyFilter<T>(IQueryable<T> query, bool _search, string searchField, string searchString, string searchOper, string filters, NameValueCollection form)
پس از آن، تغییراتی که در کدهای متد GetProducts باید اعمال شوند به صورت ذیل است:
[HttpPost] public ActionResult GetProducts(string sidx, string sord, int page, int rows, bool _search, string searchField, string searchString, string searchOper, string filters) { var list = ProductDataSource.LatestProducts; var pageIndex = page - 1; var pageSize = rows; var totalRecords = list.Count; var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize); var productsQuery = list.AsQueryable(); productsQuery = new JqGridSearch().ApplyFilter(productsQuery, _search, searchField, searchString, searchOper, filters, this.Request.Form); var productsList = productsQuery.OrderBy(sidx + " " + sord) .Skip(pageIndex * pageSize) .Take(pageSize) .ToList(); var productsData = new JqGridData { Total = totalPages, Page = page, Records = totalRecords, Rows = (productsList.Select(product => new JqGridRowData { Id = product.Id, RowCells = new List<string> { product.Id.ToString(CultureInfo.InvariantCulture), product.Name, product.Supplier.CompanyName, product.Category.Name, product.Price.ToString(CultureInfo.InvariantCulture) } })).ToArray() }; return Json(productsData, JsonRequestBehavior.AllowGet); }
- نوع متد به HttpPost تغییر کردهاست. این مورد برای ارسال اطلاعات حجیم جستجوها به سرور ضروری است و بهتر است از حالت Get استفاده نشود.
این حالت در سمت کلاینت نیز باید تنظیم شود:
$('#list').jqGrid({ //url access method type mtype: 'POST',
productsQuery = new JqGridSearch().ApplyFilter(productsQuery, _search, searchField, searchString, searchOper, filters, this.Request.Form);
برای مطالعه بیشتر
جستجوی تک فیلدی
جستجوی نوار ابزاری
جستجوی چند فیلدی
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
jqGrid03.zip
UI-Router ابزاری برای مسیریابی در AngularJS است که این امکان را برایتان فراهم میکند تا بخشهای برنامه رابط کاربریتان را به شکل یک ماشین حالت ساماندهی کنید. برخلاف سرویس route$ که بر اساس مسیریابی URLها ساماندهی شده و کار میکند، UI-Router بر اساس حالتها کار میکند، که این حالتها میتوانند در صورت لزوم مسیریابی هم داشته باشند.
UI-Router یکی از افزونههای مجموعه Angular-ui، و پاراگراف بالا معرفی آن در صفحه خانگیش است (تقریبا!). این افزونه جزئیات مفصلی دارد و در این مطلب تنها به معرفی آن خواهم پرداخت (بر اساس مطالب صفحه خانگیش). پیش از ادامه پیشنهاد میکنم اگر مطالب زیر را نخواندهاید ابتدا آنها را مرور کنید:
برای استفاده از UI-Router باید:
- فایل جاوا اسکریپت آن را دانلود کنید (released یا minified).
- در صفحه اصلی برنامهتان پس از include کردن فایل اصلی AngularJS فایل angular-ui-router.js (یا angular-ui-router.min.js) را include کنید.
- 'ui.router' را به لیست وابستگیهای ماژول اصلی اضافه کنید.
<!doctype html> <html ng-app="myApp"> <head> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script> <script src="js/angular-ui-router.min.js"></script> <script> var myApp = angular.module('myApp', ['ui.router']); // For Component users, it should look like this: // var myApp = angular.module('myApp', [require('angular-ui-router')]); </script> ... </head> <body> ... </body> </html>
حالتها و viewهای تو در تو
قابلیت اصلی UI-Router امکان تعریف حالتها و vieweهای تو در تو است. در مطلب مسیریابی در AngularJs #بخش اول دایرکتیو ng-view معرفی شده است. هنگام استفاده از سرویس route$ با این دایرکتیو میتوان محل مورد نظر برای بارگذاری محتویات مربوط به مسیرها را مشخص کرد. دایرکتیو ui-view در UI-Router همین نقش را دارد. فرض کنید این کد فایل index.html باشد:<!-- index.html --> <body> <div ui-view></div> <!-- We'll also add some navigation: --> <a ui-sref="state1">State 1</a> <a ui-sref="state2">State 2</a> </body>
در ادامه برای هر کدام از حالتها یک template اضافه میکنیم:
فایل state1.html:
<!-- partials/state1.html --> <h1>State 1</h1> <hr/> <a ui-sref="state1.list">Show List</a> <div ui-view></div>
<!-- partials/state2.html --> <h1>State 2</h1> <hr /> <a ui-sref="state2.list">Show List</a> <div ui-view></div>
دو نکته قابل توجه در این templateها وجود دارد. اول اینکه همانطور که میبینید templateها خود شامل تگی با دایرکتیو ui-view هستند. و دوم مقدار دایرکتیو ui-sref است که به صورت state1.list و state2.list آمده است. این جدا سازی با نقطه نشان دهنده سلسله مراتب حالتهاست. یعنی حالتهای state1 و state2 هرکدام حالت فرزندی به نام list دارند. در ادامه وقتی حالتها و مسیریابی را در ()app.config تعریف کنیم این مسائل از هالهای از ابهام که در آن هستند خارج میشوند! فعلا بیاید با راهنمای UI-Router پیش برویم و فایلهای template حالتهای فرزند را تعریف کنیم. templateهایی که قرار است در ui-view پدرانشان بارگذاری شوند:
<!-- partials/state1.list.html --> <h3>List of State 1 Items</h3> <ul> <li ng-repeat="item in items">{{ item }}</li> </ul>
<!-- partials/state2.list.html --> <h3>List of State 2 Things</h3> <ul> <li ng-repeat="thing in things">{{ thing }}</li> </ul>
myApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { // // For any unmatched url, redirect to /state1 $urlRouterProvider.otherwise("/state1"); // // Now set up the states $stateProvider .state('state1', { url: "/state1", templateUrl: "partials/state1.html" }) .state('state1.list', { url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('state2', { url: "/state2", templateUrl: "partials/state2.html" }) .state('state2.list', { url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.things = ["A", "Set", "Of", "Things"]; } }) }]);
خصوصیت url مشخص کننده مسیر حالت است. این خصوصیت همان مقداریست که به عنوان پارامتر اول به ()routeProvider.when$ پاس میشد. در این پارامتر میشود متغیرهای url را هم به همان ترتیب تعریف کرد. مثلا اگر حالت state1 در آدرسش یک پارامتر id داشته باشد میشود آن را به این ترتیب تعریف کرد:
.state('state1', { url: "/state1/:id", templateUrl: "partials/state1.html" })
$stateParams.id
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: function($scope) { $scope.items = ["A", "List", "Of", "Items"]; } })
- وابستگیهای فراهم شده در حالت پدر به وسیله "resolve"
- دادههای سفارشی مشخص شده در خصوصیت data حالت پدر
.state('state1', { url: "/state1", templateUrl: "partials/state1.html", data:{ foodata: 'addorder' } })
$state.current.data.foodata
Viewهای نامگذاری شده و چندگانه
<!-- index.html --> <body> <div ui-view="viewA"></div> <div ui-view="viewB"></div> <!-- Also a way to navigate --> <a ui-sref="route1">Route 1</a> <a ui-sref="route2">Route 2</a> </body>
myApp.config(function ($stateProvider) { $stateProvider .state('index', { url: "", views: { "viewA": { template: "index.viewA" }, "viewB": { template: "index.viewB" } } }) .state('route1', { url: "/route1", views: { "viewA": { template: "route1.viewA" }, "viewB": { template: "route1.viewB" } } }) .state('route2', { url: "/route2", views: { "viewA": { template: "route2.viewA" }, "viewB": { template: "route2.viewB" } } }) });
چند نکته
.state('list', { parent: "state1", url: "/list", templateUrl: "partials/state1.list.html", controller: "state1ListController as listCtrl1" } }) .state('list', { parent: "state2", url: "/list", templateUrl: "partials/state2.list.html", controller: "state2ListController as listCtrl2" } })
حالتهای انتزاعی
حساسیت به حروف بزرگ و کوچک
- در فایل "angular-ui-router.js" عبارت "new RegExp(compiled)" را پیدا کرده و آن را به "RegExp(compiled, 'i')" تبدیل کنید. و یا در "angular-ui-router.min.js" (هرکدام از فایلها که استفاده میکنید) عبارت new RegExp(o) را پیدا کرده و آن را به new RegExp(o, "i") تبدیل کنید. همین؛ صدایش را هم در نیاورید!
Extended Events/Trace
Extended Events، زیر ساخت مدیریت رخدادها در SQL Server است. برای مثال در نگارش 2016 آن بیشاز 300 رخداد در SQL Server تعریف شدهاند و زمانیکه در مورد اجرای کوئریها بحث میکنیم، این رخدادها بیشتر مدنظر ما هستند:
sql_statement_completed sp_statement_completed rpc_completed sql_batch_completed
علاوه بر اینها، رخدادهای بسط یافتهی زیر را نیز میتوان مورد استفاده قرار داد:
query_post_compilation_showplan query_post_execution_showplan query_pre_execution_showplan
استفاده از Extended Events برای جمع آوری اطلاعات آماری کوئریها
برای آزمایش نحوهی کار با Extended Events، ابتدا رویهی ذخیره شدهی زیر را ایجاد میکنیم:
USE [WideWorldImporters]; GO DROP PROCEDURE IF EXISTS [Application].[usp_GetCountryInfo]; GO CREATE PROCEDURE [Application].[usp_GetCountryInfo] @Country_Name NVARCHAR(60) AS SELECT * FROM [Application].[Countries] [c] JOIN [Application].[StateProvinces] [s] ON [s].[CountryID] = [c].[CountryID] WHERE [c].[CountryName] = @Country_Name; GO
سپس یک سشن Extended Events سفارشی را به صورت زیر ایجاد میکنیم:
/* Create XE session to capture sql_statement_completed and sp_statement_completed */ IF EXISTS ( SELECT * FROM sys.server_event_sessions WHERE [name] = 'QueryPerf') BEGIN DROP EVENT SESSION [QueryPerf] ON SERVER; END GO CREATE EVENT SESSION [QueryPerf] ON SERVER ADD EVENT sqlserver.sp_statement_completed(WHERE ([duration]>(1000))), ADD EVENT sqlserver.sql_statement_completed(WHERE ([duration]>(1000))) ADD TARGET package0.event_file(SET filename=N'C:\Temp\QueryPerf\test.xel',max_file_size=(256)) WITH ( MAX_MEMORY=16384 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB, MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF); GO
سپس نیاز است تا این سشن را که QueryPerf نام دارد، در قسمت management->extended events، اجرا و آغاز کرد:
در ادامه ابتدا بر روی بانک اطلاعاتی WideWorldImporters، کلیک راست کرده و یک پنجرهی new query جدید را ایجاد میکنیم:
WHILE 1 = 1 BEGIN EXECUTE [Application].[usp_GetCountryInfo] N'United States'; END
سپس مجددا یک پنجرهی new query دیگر را باز میکنیم:
WHILE 1 = 1 BEGIN SELECT [s].[StateProvinceName], [s].[SalesTerritory], [s].[LatestRecordedPopulation], [s].[StateProvinceCode] FROM [Application].[Countries] [c] JOIN [Application].[StateProvinces] [s] ON [s].[CountryID] = [c].[CountryID] WHERE [c].[CountryName] = 'United States'; END
کوئریهای هر دو پنجره را به صورت مجزایی اجرا کنید. سپس در قسمت management->extended events، بر روی سشن QueryPerf کلیک راست کرده و گزینهی View live data را انتخاب کنید:
این زندهترین خروجی یک سشن رخدادهای بسط یافتهاست. کار کردن با آن نسبت به روشی که در قسمت قبل بررسی کردیم، سادهتر و سریعتر است و همچنین گزارش آن به صورت خودکار تولید میشود.
یک نکته: در اینجا در قسمت Details، اگر بر روی هر ردیف کلیک کنید، امکان انتخاب و نمایش آن در لیست بالای صفحه توسط گزینهی Show Column in table وجود دارد.
در آخر در قسمت management->extended events، بر روی سشن QueryPerf کلیک راست کرده و گزینهی Stop Session را انتخاب کنید. اکنون اگر به پوشهی C:\Temp\QueryPerf مراجعه کنید، فایل xel حاوی اطلاعات این گزارش را نیز میتوانید مشاهده نمائید (به ازای هربار اجرای این سشن، یک فایل جدید را تولید میکند).
این فایل توسط Management Studio قابل گشودن و بررسی است و دقیقا همان نمای گزارش live data را به همراه دارد.
Blogger auto poster
لطفا آخرین نسخه را از سایت CodePlex دریافت کنید.
توضیحات تکمیلی جهت تنظیمات برنامه، مخصوص ورد پرس:
اینبار BlogUrl شما چیزی شبیه به https://mysite.wordpress.com/xmlrpc.php خواهد بود که به API وردپرس جهت ارسال خودکار مطالب اشاره میکند.
Username و password شما هم همان نام کاربری و کلمه عبور ورود به وبلاگ است.
در وردپرس برخلاف بلاگر، اگر تگی از پیش تعریف نشده باشد، به صورت خودکار ایجاد نمیشود. بنابراین اول باید categories مناسب را در پنل مدیریتی وبلاگ ایجاد کنید.
زمان را هم در کنترل پنل وبلاگ اصلاح کنید. UTC آن مهم است. اگر تنظیم نباشد احتمالا مطالب به صورت scheduled در کنترل پنل نمایش داده میشود و سر ساعت که رسید عمومی خواهد شد.
بازنویسی سطح دوم کش برای Entity framework 6
ایجاد نقشه سایت (Site Map) داینامیک
یک ایراد ویرایشی کوچک:
که من در کد نویسی تگ changefreq را 1 و تگ priority را always قراار دادم.ظاهرا جابجا نوشتین
- بازگشت اشعار حذف شده و ویرایش جدید گنجور رومیزی | حمیدرضا | blog.ganjoor.net
- نسخه پیش نمایش ویندوز ۸ زیر ذره بین (قسمت دوم) | آراد حقی | fa.aradhaghi.com
- PostgreSQL 9.1.1 منتشر شد | www.postgresql.org
- انتخاب INotifyPropertyChanged صحیح جهت برنامههای مخصوص ویندوز 8 | geekswithblogs.net
- معرفی DevExpress ASP.NET Theme Builder | community.devexpress.com
- نظرات Miguel de Icaza در مورد مونو و لینوکس | www.itwriting.com
- نگارش جدید StyleCop منتشر شد | geekswithblogs.net