Lazy Loading در AngularJS
آیا به این نتیجه رسیدید که اصل DRY را نقض کردهایم؟ بله همین طور است. تکرار کلاسهای css مربوط به بوت استرپ، تکرار هلپرهای توکار ASP.NET MVC بارها و بارها، خوانایی کد را پایین میارود و در برخی موارد هم خسته کننده خواهد بود. اگر با مباحث مربوط به EditorTemplateها قبلا آشنا شده باشید، خیلی سریع عنوان خواهید کرد که بهتر است از این امکان بهره برد؛ بله درست است. برای این منظور در مسیر Views/Shared/EditorTemplates، فایل cshtml. همنام با نوع داده مد نظر را ایجاد میکنیم.
String.cshtml
@model string @Html.TextBox("",ViewData.TemplateInfo.FormattedModelValue, new { @class="form-control",placeholder=ViewData.ModelMetadata.Watermark})
Enum.cshtml
@model Enum @Html.EnumDropDownListFor(m => Model, new { @class = "form-control" })
حال دوباره به نتیجه حاصل از تغییرات اعمال شده توجه کنید:
این نتیجه امیدوار کننده است ولی بازهم یکسری از کدها بی دلیل تکرار شدهاند. هلپرهای زیر نیز میتوانند در کاهش کدها به کمک ما برسند :
public static class BootstrapHelpers { public static IHtmlString BootstrapLabelFor<TModel,TProp>( this HtmlHelper<TModel> helper, Expression<Func<TModel,TProp>> property) { return helper.LabelFor(property, new { @class = "col-md-2 control-label" }); } public static IHtmlString BootstrapLabel( this HtmlHelper helper, string propertyName) { return helper.Label(propertyName, new { @class = "col-md-2 control-label" }); } }
از کلاس بالا برای عدم تکرار کلاسهای بوت استرپ مربوط به Label، استفاده میشود .
حال دوباره نتیجه را مشاهده کنید:
خیلی عالی؛ توانستیم از تکرار یکسری از کلاسهای بوت استرپ خلاص شویم. اما در ادامه با استفاده از یک Object Template به عنوان EditorTemplate برای نوع دادههای Complex، کار را تمام خواهیم کرد.
EditorTemplateهای تعریف شده در بالا، صرفا برای نوع دادههای خاصی مورد استفاده قرار خواهند گرفت؛ ولی پیاده سازی یک EditorTemplate جنریک که حتی از ویومدلهای موجود در پروژه نیز پشتیابی کند، به شکل زیر خواهد بود.
Object.cshtml
@model dynamic @foreach (var prop in ViewData.ModelMetadata.Properties .Where(p => p.ShowForEdit)) { if (prop.TemplateHint == "HiddenInput") { @Html.Hidden(prop.PropertyName) } else { <div class="form-group"> @Html.BootstrapLabel(prop.PropertyName) <div class="col-md-10"> @Html.Editor(prop.PropertyName) @Html.ValidationMessage(prop.PropertyName) </div> </div> } }
با استفاده از ViewData.ModelMetadata میتوان به خصوصیات مدل مربوط به ویو دسترسی پیدا کرد که در بالا با استفاده از همین خصوصیت به تمام پراپرتیهای مدل دسترسی پیدا کرده و مقداری کد تکراری باقی مانده را هم در اینجا کپسوله کردیم.
حال کافی است به شکل زیر عمل کنیم:
در نگارشهای اخیر کتابخانه jQuery (از نگارش 1.3 به بعد) متدی به نام live به آن اضافه شده است که کاربرد آنرا در ادامه مرور خواهیم کرد.
ابتدا مثال زیر را در نظر بگیرید:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestLive.aspx.cs" Inherits="TestJQueryAjax.TestLive" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="js/jquery.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('a.mylink').click(function(e) {
var $a = $(this);
alert($a.attr('id'));
});
$('a#lnkLoad').click(function(e) {
$('div#dynContent').load('live.ashx');
});
});
</script>
</head>
<body>
<form id="form1" runat="server">
<a href='#' id='lnk1' class='mylink'>link1</a>
<br />
<a href='#' id='lnkLoad'>load .ashx</a>
<div id='dynContent'>
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
namespace TestJQueryAjax
{
/// <summary>
/// Summary description for $codebehindclassname$
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class live : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("<a href='#' id='lnk2' class='mylink'>link2</a>");
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
همچنین اگر بر روی لینکی با id مساوی lnkLoad کلیک شود، محتوایی پویا از یک generic handler به نام live.ashx دریافت شده و به div ایی با id مساوی dynContent اضافه میگردد.
این محتوای دریافتی از generic handler ما نیز کلاسی مساوی mylink دارد، اما اینبار هر چقدر بر روی لینک اضافه شده به صفحه کلیک کنیم کار نمیکند. چرا؟ چون در هنگام فراخوانی document.ready ، این لینک وجود نداشته و روال رخدادگردانی به آن bind نشده است.
به صورت خلاصه میخواهیم روال کلیک بر روی لینکهایی با کلاس mylink همیشه کار کند. (چه در مورد عناصری در صفحه که از قبل وجود داشتهاند و چه عناصری که توسط عملیاتی Ajax ایی بعدا اضافه خواهند شد)
این مشکل با معرفی متد live حل شده است. برای این منظور تنها کافی است کد ما به صورت زیر تغییر کند:
<script type="text/javascript">
$(document).ready(function() {
$('a.mylink').live("click", function() {
var $a = $(this);
alert($a.attr('id'));
});
.
.
.
اکنون jQuery کلیه لینکهایی با کلاس مساوی mylink را که از این پس اضافه خواهند شد، به صورت live و زنده تحت نظر قرار میدهد و عکس العمل نشان خواهد داد.
2.Visual Studio 2019 RC منتشر شد
Top Issues Fixed in Visual Studio 2019 RC.2
- Find in files "Locating next match" UI is annoying.
- Find files keeps defaulting to current document.
- Quick references freezes VS 2019 RC.
- PackageId:MsSqlCmdLnUtils;PackageAction:Install;ReturnCode:1603;.
- Error List does not show errors because it is scoped to "Current Document".
- Cannot use conditional breakpoint on PropertyInfo.Name value.
- Visual Studio crashes when parsing macros at the end of a file.
- Search in Visual Studio 2019 is very slow.
- Visual Studio hangs when starting debugging.
- vdproj not supported in Visual Studio 2019 RC.
- Visual Studio installer welcome image contains offensive element for Chinese.
- VSIX Extension pre-req has been removed in Visual Studio 2019 RC breaking extension compatibility.
- Create Project from Start Screen Ignores Selected Project Folder.
- cpp properties is dialog does not show up when using Project menu item or select "Manage configuration" drop down menu.
- Visual Studio 2019 Build Tools - developer prompt title says "Developer Command Prompt for Visual Studio 2017".
- German tranlation regarding the Feedback Tool: Help > Send Feedback > Report a Problem.
- 自动完成功能,如果双击候选项,会丢失输入焦点,需要单机编辑器才能继续输入-AutoComplete function, if you double-click the candidate, will lose the input focus, need a stand-alone editor to continue to enter.
- Context menus are sometimes placed on the wrong monitor in a multiple monitor configuration.
- Visual Studio 2019 conflict with QQ Pinyin.
- Cannot drag maximized Visual Studio window.
- Fixed slow reload of multiple C# and Visual Basic projects.
- When IntelliSense is present, when a user types Shift + Enter, the active selection will be completed and a new line inserted.
- Fixed a PMA issue where editor tooltips and light bulb doesn't render properly.
- Notifications about crashes caused by extensions now show up again.
- Notifications about performance of Visual Studio have been secured against tampering.
- Fixed an issue with toolbar rendering when dragged across displays.
- Fixed an issue with Tools Options rendering when running with per-monitor awareness enabled.
- Various DpiHelper classes has been deprecated (extensibility).
- Fixed splash screen scaling to better match the primary monitor scale factor.
- Fixed an issue in settings import where warnings/errors were not always reported correctly.
- Fixed an issue where Tools Options reported software rendering regardless of rendering tier.
- Fixed an issue where the name of the open folder was not displayed in the title bar region.
- Fixed an issue with find in files positioning when per-monitor awareness is enabled.
- Fixed an issue with dock adorner rendering when per-monitor awareness is enabled.
سفارشی کردن صفحه بندی WebGrid در ASP.NET MVC
mode: WebGridPagerModes.All,
@{ var grid = new WebGrid( source: null, canPage: true, rowsPerPage: 10, canSort: true, defaultSort: "Title" ); grid.Bind(Model, rowCount: (int)ViewBag.PageCount, autoSortAndPage: false); var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1); } @grid.Table( tableStyle: "table table-striped table-hover", headerStyle: "webgrid-header", footerStyle: "webgrid-footer", alternatingRowStyle: "webgrid-alternating-row", selectedRowStyle: "webgrid-selected-row", rowStyle: "webgrid-row-style", columns: grid.Columns( grid.Column(header: "#", style: "text-align-center-col", format: @<text>@(rowIndex++)</text>), grid.Column(columnName: "Title", header: "عنوان", style: "myfont"), grid.Column(columnName: "URL", header: "آدرس", style: "myfont"), grid.Column(header: "", style: "text-align-center-col smallcell", format: item => @Html.ActionLink(linkText: "ویرایش", actionName: "Edit", controllerName: "Link", routeValues: new { area = "Admin", id = item.Code }, htmlAttributes: new { @class = "btn-sm btn-info vertical-center" })), grid.Column(header: "", format: @<form action="Link/Delete/@item.Code" method="post"> <input type="submit" class="btn-sm btn-danger submitlink" onclick="return confirm('آیا از حذف این آیتم مطمئن هستید ؟');" value="حذف" /> </form>))) <div class="text-center"> @grid.PagerList(mode: WebGridPagerModes.All) </div>
@{ var grid = new WebGrid( source: Model, canPage: true, rowsPerPage: 10, canSort: true, defaultSort: "Title" ); // grid.Bind(Model, rowCount: (int)ViewBag.PageCount, autoSortAndPage: false); // Delete this Line var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1); }
2- استفاده از {}less.
Dotless یک پیاده سازی از کتابخانه جاوا اسکریپتی LESS برای دات نت میباشد. پکیج نیوگت DotLess را نیز میتوانید از اینجا دریافت کنید. بعد از اضافه شدن فایلهای آن، یک ارجاع به dotless.core به پروژه تان اضافه خواهد شد. همچنین در فایل Web.Config در قسمت HttpHandler خط زیر اضافه خواهد شد:
<add type="dotless.Core.LessCssHttpHandler,dotless.Core" validate="false" path="*.LESS" verb="*" />
خط فوق یعنی به محض مواجه شدن با فایل LESS، پردازشگر فایلهای LESS وارد عمل میشود. همچنین خط زیر نیز جهت پیکربندی به قسمت configSections در فایل Web.Config اضافه میشود:
<section name="dotless" type="dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core" />
همچنین اگر مایل بودید میتوانید تنظیمات مربوط به فشرده سازی و caching را نیز فعال کنید:
<dotless minifyCss="false" cache="true" />
3- استفاده از افزونهی Web Essentials
Web Essentials برای کامپایل فایلهای LESS از کامپایلر node استفاده میکند. کار با این افزونه خیلی ساده است. کافی است پسوند فایل CSS موجود در پروژه تان را درون ویژوال استودیو، به less. تغییر دهید. با دوبار کلیک بر روی فایل، ویرایشگر فایلهای LESS برای شما نمایش داده میشود، همزمان نیز فایل یک فایل CSS و یک نسخه از فایل CSS را به صورت فشرده، برایتان تولید میکند. خب، هر بار که فایل LESS را تغییر دهید، Web Essentials به صورت خودکار فایلهای css. و min.css. را برایتان روز رسانی میکند.
خوب با کلیک بر روی فایل less، ویرایشگر فایلهای less نمایش داده میشود که با تغییر فایل css میتوانید پیش نمایش آنرا در سمت راست مشاهده کنید:
تعریف متغیر
با استفاده از syntax زیر میتوانید متغیرهای خود را تعریف کنید:
@variable-name: variableValue;
یکی از قابلیتهای جالب در حین مقداردهی متغیرها به خصوص زمانیکه مقدار یک کد رنگی باشد، نمایش کادر انتخاب رنگ است، این کادر بلافاصله بعد از نوشتن علامت # در ابتدای مقدار متغیر نمایش داده میشود:
به طور مثال با تعریف متغیر فوق هر جایی میتوانیم برای تعیین رنگ از آن استفاده کنیم:
@primary-color: #ff6a00; body { background-color: @primary-color; }
استفاده از توابع
LESS شامل تعداد زیادی توابع از پیش نوشته شده است که میتوانید به راحتی از آنها استفاده کنید، توابعی از جمله کار با رنگ ها، اعمال ریاضی و غیره. استفاده از آنها خیلی ساده است. به طور مثال در کد زیر از تابع percentage جهت تبدیل 0.5 به 50% استفاده کرده ایم:
.myClass { width: percentage(0.5); }
استخراج یک فایل
یکی دیگر از قابلیتهای Web Essentials استخراج(Extract) یک فایل میباشد به طور مثال فایل LESS شما شامل متغیرهای زیر است:
@primary-color: #7BA857; @primary-color-light: #B6DE8F; @primary-color-lighter: #D3EFC3; @primary-color-lightest: #EFFAE6; @secondary-color: #AE855C; @text-color-light: #666666; @text-color-dark: #0444;
به راحتی میتوانید تعاریف فوق را درون یک فایل LESS دیگر با نام colors.less قرار دهید:
تغییر تنظیمات پیش فرض Web Essentials
افزونه Web Essentials دارای یک قسمت جهت تغییر تنظیمات پیش فرض برای کار با LESS میباشد که با مراجعه به منوی Tools در ویژوال استودیو و سپس Options میتوانید آنها را تغییر دهید:
Auto-compile dependent files on save: توسط این گزینه میتوانیم تعیین کنیم که فایلهای که import کرده ایم تنها در صورتی که تغییر کرده و ذخیره شده باشند، در فایل CSS جاری کامپایل شوند.
Compile files on build: توسط این گزینه میتوانیم تعیین کنیم که فایلهای less در زمان Build پروژه کامپایل شوند.
Compile files on save: توسط این گزینه میتوانیم تعیین کنیم که فایلهای less در زمان ذخیره کردن پروژه کامپایل شوند.
Create source map files: اگر این گزینه True باشد فایل map. نیز تولید خواهد شد.
Custom output directory: اگر میخواهید خروجی در پوشهی موردنظر شما نمایش داده شود میتوانید آدرس آن را تعیین کنید.
Don't save raw compilation output: با فعال بودن این گزینه فایل CSS عادی ایجاد نخواهد شد.
Process source maps: توسط این گزینه میتوانید قابلیتهای ویرایشگر فایلهای source map را فعال یا غیرفعال کنید.
Strict Math: با فعال بودن این گزینه LESS تمام اعمال ریاضی درون فایل CSS را پردازش خواهد کرد.
Show preview pane: از این گزینه نیز جهت نمایش یا عدم نمایش preview window استفاده میشود.
روش های مقایسه اشیاء با null
Check | Code | Description |
Is Null | if(variable is null) return true; |
|
Is Not Null | if(variable is { }) return false |
|
Is Not Null | if(variable is object) return false |
|
Is Null | if(variable == null) return true |
|
Is Not Null | if(variable != null) return false |
|
Lazy Loading در AngularJS
<div style="direction: rtl"> <a href="#/state1">حالت 1</a> | <a href="#/state2">حالت 2</a> | <a href="#/state3">حالت 3</a> <div ui-view style="font-weight:bold; text-align:center;"></div> </div>
تگ زیر یک دایرکتیو دارد: <br/> <div ng-hello-directive></div>
angular.module('app').lazy.directive('ngHelloDirective', function () { return function (scope, elem, attr) { elem.html('سلام دایرکتیو تنبل!'); }; });
.state('state3', { url: '/state3', templateUrl: 'app/state3.html', resolve: { fileDeps: ['$q', '$rootScope', function ($q, $rootScope) { var deferred = $q.defer(); var deps = ['app/helloDirective.js']; $script(deps, function () { $rootScope.$apply(function () { deferred.resolve(); }); }); return deferred.promise; }] } });
angular.module('moduleOfDirective', []).directive('ngDirectiveName', ...
app = angular.module("app", ['ui.router', 'moduleOfDirective']);
angular.module('app', []).lazy.directive('ngDirectiveName', ...
fields: { "Price": { type: "number", validation: { required: true, min: 1 } } }
Kendo UI Validation و HTML 5
در HTML 5 امکان تعریف نوعهای خاص کنترلهای ورودی کاربر مانند email، url، number، range، date، search و color وجود دارد. برای مثال در اینجا اگر کاربر تاریخ غیرمعتبری را وارد کند، مرورگر پیام اعتبارسنجی متناظری را به او نمایش خواهد داد. همچنین در HTML 5 امکان افزودن ویژگی required نیز به کنترلهای ورودی پیش بینی شدهاست. اما باید درنظر داشت که مرورگرهای قدیمی از این امکانات پشتیبانی نمیکنند. در این حالت Kendo UI با تشویق استفاده از روش معرفی شده در HTML 5، با آن یکپارچه شده و همچنین این قابلیتهای اعتبارسنجی HTML 5 را در مرورگرهای قدیمی نیز میسر میکند. Kendo UI Validation جزو نسخهی سورس باز Kendo UI با مجوز Apache نیز میباشد.
نمونهای از امکانات اعتبارسنجی توکار HTML 5 را در اینجا مشاهده میکنید:
<input type="text" name="firstName" required /> <input type="text" name="twitter" pattern="https?://(?:www\.)?twitter\.com/.+i" /> <input type="number" name="age" min="1" max="42" /> <input type="number" name="age" min="1" max="100" step="2" /> <input type="url" name="url" /> <input type="email" name="email" />
یکپارچه سازی اعتبارسنجی Kendo UI با اعتبارسنجی HTML 5
در اینجا یک فرم تشکیل شده با ساختار HTML 5 را ملاحظه میکنید. هر دو فیلد ورودی، با ویژگی استاندارد required مزین شدهاند. همچنین توسط ویژگی type، ورودی دوم جهت دریافت آدرس ایمیل معرفی شدهاست.
چون فیلد دوم دارای دو اعتبارسنجی تعریف شده است، دارای دو ویژگی *-data برای تعریف پیامهای اعتبارسنجی متناظر نیز میباشد. الگوی تعریف آنها data-[rule]-msg است.
<div class="k-rtl"> <form id="testView"> <label for="firstName">نام</label> <input id="firstName" name="firstName" type="text" class="k-textbox" required validationmessage="لطفا نامی را وارد کنید"> <br> <label for="emailId">آدرس پست الکترونیک</label> <input id="emailId" name="emailId" type="email" dir="ltr" required class="k-textbox" data-required-msg="لطفا ایمیلی را وارد کنید." data-email-msg="ایمیل وارد شده معتبر نیست."> <br> <input type="submit" class="k-button" value="ارسال"> </form> </div> <script type="text/javascript"> $(function () { $("form#testView").kendoValidator(); }); </script>
تعیین محل نمایش پیامهای اعتبارسنجی
پیامهای اعتبارسنجی Kendo UI به صورت خودکار در کنار فیلد متناظر با آن نمایش داده میشوند. اما اگر نیاز به تعیین مکان دستی آنها وجود داشت (جهت خوانایی بهتر) باید به نحو ذیل عمل کرد:
<input type="text" id="name" name="name" required> <span class="k-invalid-msg" data-for="name"></span>
تعریف سراسری پیامهای اعتبارسنجی
در مثال فوق، به ازای تک تک فیلدهای ورودی، پیام اعتبارسنجی متناظر با required وارد شد. میتوان این پیامها را حذف کرد و در قسمت messages متد kendoValidator قرار داد:
<script type="text/javascript"> $(function () { $("form#testView").kendoValidator({ messages: { // {0} would be replaced with the input element's name required: '{0} را تکمیل کنید.', email: 'ایمیل وارد شده معتبر نیست.' } }); }); </script>
- در این پیامها {0} با مقدار ویژگی name فیلد ورودی متناظر جایگزین میشود.
- اگر هم در markup و هم در تعاریف kendoValidator، پیامهای اعتبارسنجی تعریف شوند، حق تقدم با تعاریف markup خواهد بود.
اعتبارسنجی سفارشی سمت کاربر
علاوه بر امکانات استاندارد HTML 5، امکان تعریف دستورهای اعتبارسنجی سفارشی نیز وجود دارد:
<script type="text/javascript"> $(function () { $("form#testView").kendoValidator({ rules: { customRule1: function (input) { if (!input.is("[id=firstName]")) return true; var re = /^[A-Za-z]+$/; return re.test(input.val()); } //, customRule1: …. }, messages: { // {0} would be replaced with the input element's name required: '{0} را تکمیل کنید.', email: 'ایمیل وارد شده معتبر نیست.', customRule1: 'اعداد مجاز نیستند.' } }); }); </script>
- همانطور که ملاحظه میکنید، برای تعریف منطق اعتبارسنجی سفارشی، باید از خاصیت rules ورودی متد kendoValidator شروع کرد. در اینجا نام یک متد callback دلخواهی را وارد کرده و سپس بر اساس منطق اعتبارسنجی مورد نظر، باید true/false را بازگشت داد. برای نمونه در این مثال اگر کاربر در فیلد نام، عدد وارد کند، ورودی او مورد قبول واقع نخواهد شد.
- باید دقت داشت که اگر بررسی input.is صورت نگیرد، منطق تعریف شده به تمام کنترلهای صفحه اعمال میشود.
- پیام متناظر با این دستور سفارشی جدید، در قسمت messages، دقیقا بر اساس نام callback method تعریف شده در قسمت rules باید تعریف شود.
فراخوانی دستی اعتبارسنجی یک فرم
در حالت پیش فرض، با کلیک بر روی دکمهی ارسال، اعتبارسنجی کلیه عناصر فرم به صورت خودکار انجام میشود. اگر بخواهیم در این بین یک پیام سفارشی را نیز نمایش دهیم میتوان به صورت زیر عمل کرد:
<script type="text/javascript"> $(function () { $("form#testView").submit(function (event) { event.preventDefault(); var validator = $("form#testView").data("kendoValidator"); if (validator.validate()) { alert("validated!"); } else { alert("There is invalid data in the form."); } }); $("form#testView").kendoValidator(); }); </script>
اعتبارسنجی سفارشی در DataSource
در تعریف فیلدهای مدل DataSource، امکان تعریف اعتبارسنجیهای پیش فرضی مانند rquired، min، max و امثال آن وجود دارد که نمونهای از آنرا در بحث فعال سازی CRUD در Kendo UI Grid مشاهده کردید:
fields: { "serviceName": { type: "string", defaultValue: "Inspection", editable: true, nullable: false, validation: { /*...*/ } }, // ... }
schema: { model: { id: "ProductID", fields: { ProductID: { editable: false, nullable: true }, ProductName: { validation: { required: true, custom1: function (input) { if (input.is("[name='ProductName']") && input.val() != "") { input.attr("data-custom1-msg", "نام محصول باید با حرف بزرگ انگلیسی شروع شود"); return /^[A-Z]/.test(input.val()); } return true; } // ,custom2: ... } }, UnitPrice: { type: "number", validation: { required: true, min: 1} }, Discontinued: { type: "boolean" }, UnitsInStock: { type: "number", validation: { min: 0, required: true} } } } }
متد ()input.val مقدار کنترل جاری را بر میگرداند. برای دسترسی به مقدار سایر کنترلها میتوان از روش ()fieldName").val#")$ استفاده کرد.