<DntInputPersianDate @bind-Value="Person.MarriageDate" LabelName="تاریخ " ShowCalendarOnFocus="true" BeginningOfCentury="1400" CalendarFromYear="1350" CalendarToYear="@DateTime.Now.Year" UsePersianNumbers="true" CalendarShowHolidays="true" CalendarShowTodayButton="true" ShowCalendarIcon="false" ShowCalendarLabel="false" CalendarUseShortPersianDayNamesOfWeek="false" />
- با نصب و اجرای Visual Studio 2013 Express for Web یا Visual Studio 2013 شروع کنید.
- یک پروژه جدید بسازید (از صفحه شروع یا منوی فایل)
- گزینه #Visual C و سپس ASP.NET Web Application را انتخاب کنید. نام پروژه را به "WebFormsIdentity" تغییر داده و OK کنید.
- در دیالوگ جدید ASP.NET گزینه Empty را انتخاب کنید.
دقت کنید که دکمه Change Authentication غیرفعال است و هیچ پشتیبانی ای برای احراز هویت در این قالب پروژه وجود ندارد.
افزودن پکیجهای ASP.NET Identity به پروژه
دقت کنید که نصب کردن این پکیج وابستگیها را نیز بصورت خودکار نصب میکند: Entity Framework و ASP.NET Idenity Core.
افزودن فرمهای وب لازم برای ثبت نام کاربران
در دیالوگ باز شده نام فرم را به Register تغییر داده و تایید کنید.
فایل ایجاد شده جدید را باز کرده و کد Markup آن را با قطعه کد زیر جایگزین کنید.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="WebFormsIdentity.Register" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body style=" <form id="form1" runat="server"> <div> <h4 style="Register a new user</h4> <hr /> <p> <asp:Literal runat="server" ID="StatusMessage" /> </p> <div style="margin-bottom:10px"> <asp:Label runat="server" AssociatedControlID="UserName">User name</asp:Label> <div> <asp:TextBox runat="server" ID="UserName" /> </div> </div> <div style="margin-bottom:10px"> <asp:Label runat="server" AssociatedControlID="Password">Password</asp:Label> <div> <asp:TextBox runat="server" ID="Password" TextMode="Password" /> </div> </div> <div style="margin-bottom:10px"> <asp:Label runat="server" AssociatedControlID="ConfirmPassword">Confirm password</asp:Label> <div> <asp:TextBox runat="server" ID="ConfirmPassword" TextMode="Password" /> </div> </div> <div> <div> <asp:Button runat="server" OnClick="CreateUser_Click" Text="Register" /> </div> </div> </div> </form> </body> </html>
این تنها یک نسخه ساده شده Register.aspx است که از چند فیلد فرم و دکمه ای برای ارسال آنها به سرور استفاده میکند.
فایل کد این فرم را باز کرده و محتویات آن را با قطعه کد زیر جایگزین کنید.
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using System; using System.Linq; namespace WebFormsIdentity { public partial class Register : System.Web.UI.Page { protected void CreateUser_Click(object sender, EventArgs e) { // Default UserStore constructor uses the default connection string named: DefaultConnection var userStore = new UserStore<IdentityUser>(); var manager = new UserManager<IdentityUser>(userStore); var user = new IdentityUser() { UserName = UserName.Text }; IdentityResult result = manager.Create(user, Password.Text); if (result.Succeeded) { StatusMessage.Text = string.Format("User {0} was created successfully!", user.UserName); } else { StatusMessage.Text = result.Errors.FirstOrDefault(); } } } }
کد این فرم نیز نسخه ای ساده شده است. فایلی که بصورت خودکار توسط VS برای شما ایجاد میشود متفاوت است.
کلاس IdentityUser پیاده سازی پیش فرض EntityFramework از قرارداد IUser است. قرارداد IUser تعریفات حداقلی یک کاربر در ASP.NET Identity Core را در بر میگیرد.
کلاس UserStore پیاده سازی پیش فرض EF از یک فروشگاه کاربر (user store) است. این کلاس چند قرارداد اساسی ASP.NET Identity Core را پیاده سازی میکند: IUserStore, IUserLoginStore, IUserClaimStore و IUserRoleStore.
کلاس UserManager دسترسی به APIهای مربوط به کاربران را فراهم میکند. این کلاس تمامی تغییرات را بصورت خودکار در UserStore ذخیره میکند.
کلاس IdentityResult نتیجه یک عملیات هویتی را معرفی میکند (identity operations).
پوشه App_Data را به پروژه خود اضافه کنید.
فایل Web.config پروژه را باز کنید و رشته اتصال جدیدی برای دیتابیس اطلاعات کاربران اضافه کنید. این دیتابیس در زمان اجرا (runtime) بصورت خودکار توسط EF ساخته میشود. این رشته اتصال شبیه به رشته اتصالی است که هنگام ایجاد پروژه بصورت خودکار برای شما تنظیم میشود.
<?xml version="1.0" encoding="utf-8"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\WebFormsIdentity.mdf;Initial Catalog=WebFormsIdentity;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> </system.web> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> </configuration>
همانطور که مشاهده میکنید نام این رشته اتصال DefaultConnection است.
روی فایل Register.aspx کلیک راست کنید و گزینه Set As Start Page را انتخاب کنید. اپلیکیشن خود را با کلیدهای ترکیبی Ctrl + F5 اجرا کنید که تمام پروژه را کامپایل نیز خواهد کرد. یک نام کاربری و کلمه عبور وارد کنید و روی Register کلیک کنید.
ASP.NET Identity از اعتبارسنجی نیز پشتیبانی میکند، مثلا در این مرحله میتوانید از اعتبارسنج هایی که توسط ASP.NET Identity Core عرضه میشوند برای کنترل رفتار فیلدهای نام کاربری و کلمه عبور استفاده کنید. اعتبارسنج پیش فرض کاربران (User) که UserValidator نام دارد خاصیتی با نام AllowOnlyAlphanumericUserNames دارد که مقدار پیش فرضش هم true است. اعتبارسنج پیش فرض کلمه عبور (MinimumLengthValidator) اطمینان حاصل میکند که کلمه عبور حداقل 6 کاراکتر باشد. این اعتبارسنجها بصورت propertyها در کلاس UserManager تعریف شده اند و میتوانید آنها را overwrite کنید و اعتبارسنجی سفارشی خود را پیاده کنید. از آنجا که الگوی دیتابیس سیستم عضویت توسط Entity Framework مدیریت میشود، روی الگوی دیتابیس کنترل کامل دارید، پس از Data Annotations نیز میتوانید استفاده کنید.
تایید دیتابیس LocalDbIdentity که توسط EF ساخته میشود
گره (DefaultConnection (WebFormsIdentity و سپس Tables را باز کنید. روی جدول AspNetUsers کلیک راست کرده و Show Table Data را انتخاب کنید.
پیکربندی اپلیکیشن برای استفاده از احراز هویت OWIN
نصب پکیجهای احراز هویت روی پروژه
به دنبال پکیجی با نام Microsoft.Owin.Host.SystemWeb بگردید و آن را نیز نصب کنید.
پکیج Microsoft.Aspnet.Identity.Owin حاوی یک سری کلاس Owin Extension است و امکان مدیریت و پیکربندی OWIN Authentication در پکیجهای ASP.NET Identity Core را فراهم میکند.
پکیج Microsoft.Owin.Host.SystemWeb حاوی یک سرور OWIN است که اجرای اپلیکیشنهای مبتنی بر OWIN را روی IIS و با استفاده از ASP.NET Request Pipeline ممکن میسازد. برای اطلاعات بیشتر به OWIN Middleware in the IIS integrated pipeline مراجعه کنید.
افزودن کلاسهای پیکربندی Startup و Authentication
فایل Startup.cs را باز کنید و قطعه کد زیر را با محتویات آن جایگزین کنید تا احراز هویت OWIN Cookie Authentication پیکربندی شود.
using Microsoft.AspNet.Identity; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; [assembly: OwinStartup(typeof(WebFormsIdentity.Startup))] namespace WebFormsIdentity { public class Startup { public void Configuration(IAppBuilder app) { // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888 app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Login") }); } } }
این کلاس حاوی خاصیت OwinAttribute است که کلاس راه انداز OWIN را نشانه گذاری میکند. هر اپلیکیشن OWIN یک کلاس راه انداز (startup) دارد که توسط آن میتوانید کامپوننتهای application pipeline را مشخص کنید. برای اطلاعات بیشتر درباره این مدل، به OWIN Startup Class Detection مراجعه فرمایید.
افزودن فرمهای وب برای ثبت نام و ورود کاربران
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin.Security; using System; using System.Linq; using System.Web; namespace WebFormsIdentity { public partial class Register : System.Web.UI.Page { protected void CreateUser_Click(object sender, EventArgs e) { // Default UserStore constructor uses the default connection string named: DefaultConnection var userStore = new UserStore<IdentityUser>(); var manager = new UserManager<IdentityUser>(userStore); var user = new IdentityUser() { UserName = UserName.Text }; IdentityResult result = manager.Create(user, Password.Text); if (result.Succeeded) { var authenticationManager = HttpContext.Current.GetOwinContext().Authentication; var userIdentity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie); authenticationManager.SignIn(new AuthenticationProperties() { }, userIdentity); Response.Redirect("~/Login.aspx"); } else { StatusMessage.Text = result.Errors.FirstOrDefault(); } } } }
فایل Login.aspx را باز کنید و کد Markup آن را مانند قطعه کد زیر تغییر دهید.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="WebFormsIdentity.Login" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body style="font-family: Arial, Helvetica, sans-serif; font-size: small"> <form id="form1" runat="server"> <div> <h4 style="font-size: medium">Log In</h4> <hr /> <asp:PlaceHolder runat="server" ID="LoginStatus" Visible="false"> <p> <asp:Literal runat="server" ID="StatusText" /> </p> </asp:PlaceHolder> <asp:PlaceHolder runat="server" ID="LoginForm" Visible="false"> <div style="margin-bottom: 10px"> <asp:Label runat="server" AssociatedControlID="UserName">User name</asp:Label> <div> <asp:TextBox runat="server" ID="UserName" /> </div> </div> <div style="margin-bottom: 10px"> <asp:Label runat="server" AssociatedControlID="Password">Password</asp:Label> <div> <asp:TextBox runat="server" ID="Password" TextMode="Password" /> </div> </div> <div style="margin-bottom: 10px"> <div> <asp:Button runat="server" OnClick="SignIn" Text="Log in" /> </div> </div> </asp:PlaceHolder> <asp:PlaceHolder runat="server" ID="LogoutButton" Visible="false"> <div> <div> <asp:Button runat="server" OnClick="SignOut" Text="Log out" /> </div> </div> </asp:PlaceHolder> </div> </form> </body> </html>
محتوای فایل Login.aspx.cs را نیز مانند لیست زیر تغییر دهید.
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Owin.Security; using System; using System.Web; using System.Web.UI.WebControls; namespace WebFormsIdentity { public partial class Login : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { if (User.Identity.IsAuthenticated) { StatusText.Text = string.Format("Hello {0}!", User.Identity.GetUserName()); LoginStatus.Visible = true; LogoutButton.Visible = true; } else { LoginForm.Visible = true; } } } protected void SignIn(object sender, EventArgs e) { var userStore = new UserStore<IdentityUser>(); var userManager = new UserManager<IdentityUser>(userStore); var user = userManager.Find(UserName.Text, Password.Text); if (user != null) { var authenticationManager = HttpContext.Current.GetOwinContext().Authentication; var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie); authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, userIdentity); Response.Redirect("~/Login.aspx"); } else { StatusText.Text = "Invalid username or password."; LoginStatus.Visible = true; } } protected void SignOut(object sender, EventArgs e) { var authenticationManager = HttpContext.Current.GetOwinContext().Authentication; authenticationManager.SignOut(); Response.Redirect("~/Login.aspx"); } } }
- متد Page_Load حالا وضعیت کاربر جاری را بررسی میکند و بر اساس وضعیت Context.User.Identity.IsAuthenticated تصمیم گیری میکند.
- متد SignIn
- پروژه را با Ctrl + F5 اجرا کنید و کاربر جدیدی بسازید. پس از وارد کردن نام کاربری و کلمه عبور و کلیک کردن دکمه Register باید بصورت خودکار به سایت وارد شوید و نام خود را مشاهده کنید.
- همانطور که مشاهده میکنید در این مرحله حساب کاربری جدید ایجاد شده و به سایت وارد شده اید. روی Log out کلیک کنید تا از سایت خارج شوید. پس از آن باید به صفحه ورود هدایت شوید.
- حالا یک نام کاربری یا کلمه عبور نامعتبر وارد کنید و روی Log in کلیک کنید.
- «صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid »
- «استفاده از Kendo UI templates»
صورت مساله
میخواهیم به یک چنین تصویری برسیم؛ که دارای گروه بندی اطلاعات است، فرمت شرطی روی ستون قیمت آن اعمال شده و تاریخ نمایش داده شده در آن نیز شمسی است. همچنین برای مثال ستون قیمت آن دارای ته جمع صفحه بوده و به علاوه یک دکمهی سفارشی به نوار ابزار آن اضافه شدهاست.
مباحث قسمت سمت سرور این مثال با مطلب «صفحه بندی، مرتب سازی و جستجوی پویای اطلاعات به کمک Kendo UI Grid» دقیقا یکی است. فقط یک خاصیت AddDate نیز در اینجا اضافه شدهاست.
تغییر نحوهی نمایش pager
اگر به قسمت pager تصویر فوق دقت کنید، یک دکمهی refresh، تعداد موارد هر صفحه و امکان وارد کردن دستی شماره صفحه، در آن پیش بینی شدهاست. این موارد را با تنظیمات ذیل میتوان فعال کرد:
$("#report-grid").kendoGrid({ // ... pageable: { previousNext: true, // default true numeric: true, // default true buttonCount: 5, // default 10 refresh: true, // default false input: true, // default false pageSizes: true // default false },
بومی سازی پیغامهای گرید
پیغامهای فارسی را که در تصویر فوق مشاهده میکنید، حاصل پیوست فایل kendo.fa-IR.js هستند:
<!--https://github.com/loudenvier/kendo-global/blob/master/lang/kendo.fa-IR.js--> <script src="js/messages/kendo.fa-IR.js" type="text/javascript"></script>
گروه بندی اطلاعات
برای گروه بندی اطلاعات در Kendo UI Grid دو قسمت باید تغییر کنند.
ابتدا باید فیلد پیش فرض گروه بندی در قسمت data source گرید تعریف شود:
var productsDataSource = new kendo.data.DataSource({ // ... group: { field: "IsAvailable" }, // ... });
$("#report-grid").kendoGrid({ // ... groupable: true, // allows the user to alter what field the grid is grouped by // ...
اضافه کردن ته جمعهای ستونها
این ته جمعها که aggregate نام دارند باید در دو قسمت فعال شوند:
var productsDataSource = new kendo.data.DataSource({ //... aggregate: [ { field: "Name", aggregate: "count" }, { field: "Price", aggregate: "sum" } ] //... });
سپس این متدها را میتوان مطابق فرمت hash syntax قالبهای Kendo UI در قسمت footerTemplate هر ستون تعریف کرد:
$("#report-grid").kendoGrid({ // ... columns: [ { field: "Name", title: "نام محصول", footerTemplate: "تعداد: #=count#" }, { field: "Price", title: "قیمت", footerTemplate: "جمع: #=kendo.toString(sum,'c0')#" } ] // ... });
فرمت شرطی اطلاعات
در ستون قیمت، میخواهیم اگر قیمتی بیش از 2490 بود، با پس زمینهی قهوهای و رنگ زرد نمایش داده شود. برای این منظور میتوان یک قالب Kendo UI سفارشی را طراحی کرد:
<script type="text/x-kendo-template" id="priceTemplate"> #if( Price > 2490 ) {# <span style="background:brown; color:yellow;">#=kendo.toString(Price,'c0')#</span> #} else {# #= kendo.toString(Price,'c0')# #}# </script>
$("#report-grid").kendoGrid({ //... columns: [ { field: "Price", title: "قیمت", template: kendo.template($("#priceTemplate").html()), footerTemplate: "جمع: #=kendo.toString(sum,'c0')#" } ] //... });
فرمت تاریخ میلادی به شمسی در حین نمایش
برای تبدیل سمت کلاینت تاریخ میلادی به شمسی از کتابخانهی moment-jalaali.js کمک گرفته شدهاست:
<!--https://github.com/moment/moment/--> <script src="js/cultures/moment.min.js" type="text/javascript"></script> <!--https://github.com/jalaali/moment-jalaali--> <script src="js/cultures/moment-jalaali.js" type="text/javascript"></script>
$("#report-grid").kendoGrid({ //... columns: [ { field: "AddDate", title: "تاریخ ثبت", template: "#=moment(AddDate).format('jYYYY/jMM/jDD')#" } ] //... });
اضافه کردن یک دکمه به نوار ابزار گرید
نوار ابزار Kendo UI Grid را نیز میتوان توسط یک قالب سفارشی آن مقدار دهی کرد:
$("#report-grid").kendoGrid({ // ... toolbar: [ { template: kendo.template($("#toolbarTemplate").html()) } ] // ... });
<script> // این اطلاعات برای تهیه خروجی سمت سرور مناسب هستند function getCurrentGridFilters() { var dataSource = $("#report-grid").data("kendoGrid").dataSource; var gridState = { page: dataSource.page(), pageSize: dataSource.pageSize(), sort: dataSource.sort(), group: dataSource.group(), filter: dataSource.filter() }; return kendo.stringify(gridState); } </script> <script id="toolbarTemplate" type="text/x-kendo-template"> <a class="k-button" href="\#" onclick="alert('gridState: ' + getCurrentGridFilters());">نوار ابزار سفارشی</a> </script>
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید:
KendoUI05.zip
var app = angular.module('myApp', []);
app.directive('angry', function () { return { restrict: 'E', template: '<div style="color:red"> I am angry!</div>' } })
restrict: که چهار مقدار E و A و C و M را میپذیرد که به EACM نیز معروف هستند.
E: زمانی که قصد داشته باشیم یک المان جدید بسازیم از E به معنای element در restrict استفاده میکنیم(<my-directive></my-directive> )؛
A: زمانی که قصد داشته باشیم Directive مورد نظر به عنوان Attribute در تگها استفاده شود از A به معنای Attribute در restrict استفاده میشود(<div my-directive="exp"></div> )؛
C: از C نیز برای تعریف Directive به عنوان مقادیر ویژگی کلاس استفاده میکنیم(<div class="my-directive: exp "></div> )؛
M: حالت M نیز برای استفاده Directive در کامنتها است(<!-- directive: my-directive exp --> ).
در ادامه یک Directive دیگر به نام happy میسازیم:
app.directive('happy', function () { return { restrict: 'A', template: '<div style="color:blue"> I am happy!</div>' }; })
<script type="text/javascript" src="~/scripts/Modules/module1.js"></script> <div ng-app="myApp"> <angry></angry> <div happy></div> </div>
ادامه دارد...
ایجاد پروژههای خالی Blazor
در انتهای قسمت قبل، با روش ایجاد پروژههای خالی Blazor به کمک NET SDK 5x. آشنا شدیم. به همین جهت دو پوشهی جدید BlazorWasmSample و BlazorServerSample را ایجاد کرده و از طریق خط فرمان و با کمک NET CLI.، در پوشهی اولی دستور dotnet new blazorwasm و در پوشهی دومی دستور dotnet new blazorserver را اجرا میکنیم.
البته اجرای این دو دستور، نیاز به اتصال به اینترنت را هم برای بار اول دارند؛ تا فایلهای مورد نیاز و بستههای مرتبط را دریافت و restore کنند. بسته به سرعت اینترنت، حداقل یک ربعی را باید صبر کنید تا دریافت ابتدایی بستههای مرتبط به پایان برسد. برای دفعات بعدی، از کش محلی NuGet، برای restore بستههای blazor استفاده میشود که بسیار سریع است.
بررسی ساختار پروژهی Blazor Server و اجرای آن
پس از اجرای دستور dotnet new blazorserver در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همانطور که مشاهده میکنید، ساختار این پروژه، بسیار شبیه به یک پروژهی استاندارد ASP.NET Core از نوع Razor pages است.
- در پوشهی properties آن، فایل launchSettings.json قرار دارد که برای نمونه، شماره پورت اجرایی برنامه را در حالت اجرای توسط دستور dotnet run و یا توسط IIS Express مشخص میکند.
- پوشهی wwwroot آن، مخصوص ارائهی فایلهای ایستا مانند wwwroot\css\bootstrap است. در ابتدای کار، این پوشه به همراه فایلهای CSS بوت استرپ است. در ادامه اگر نیاز باشد، فایلهای جاوا اسکریپتی را نیز میتوان به این قسمت اضافه کرد.
- در پوشهی Data آن، سرویس تامین اطلاعاتی اتفاقی قرار دارد؛ به نام WeatherForecastService که هدف آن، تامین اطلاعات یک جدول نمایشی است که در ادامه در آدرس https://localhost:5001/fetchdata قابل مشاهده است.
- در پوشهی Pages، تمام کامپوننتهای Razor برنامه قرار میگیرند. یکی از مهمترین صفحات آن، فایل Pages\_Host.cshtml است. کار این صفحهی ریشه، افزودن تمام فایلهای CSS و JS، به برنامهاست. بنابراین در آینده نیز از همین صفحه برای افزودن فایلهای CSS و JS استفاده خواهیم کرد. اگر به قسمت body این صفحه دقت کنیم، تگ جدید کامپوننت قابل مشاهدهاست:
<body> <component type="typeof(App)" render-mode="ServerPrerendered" />
همچنین در همینجا، تگهای دیگری نیز قابل مشاهده هستند:
<body> <component type="typeof(App)" render-mode="ServerPrerendered" /> <div id="blazor-error-ui"> <environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment> <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.server.js"></script> </body>
- در پوشهی Shared، یکسری فایلهای اشتراکی قرار دارند که قرار است در کامپوننتهای واقع در پوشهی Pages مورد استفاه قرار گیرند. برای نمونه فایل Shared\MainLayout.razor، شبیه به master page برنامههای web forms است که قالب کلی سایت را مشخص میکند. داخل آن Body@ را مشاهده میکنید که به معنای نمایش صفحات دیگر، دقیقا در همین محل است. همچنین در این پوشه فایل Shared\NavMenu.razor نیز قرار دارد که ارجاعی به آن در MainLayout.razor ذکر شده و کار آن نمایش منوی آبیرنگ سمت چپ صفحهاست.
- در پوشهی ریشهی برنامه، فایل Imports.razor_ قابل مشاهدهاست. مزیت تعریف usingها در اینجا این است که از تکرار آنها در کامپوننتهای razor ای که در ادامه تهیه خواهیم کرد، جلوگیری میکند. هر using تعریف شدهی در اینجا، در تمام کامپوننتها، قابل دسترسی است؛ به آن global imports هم گفته میشود.
- در پوشهی ریشهی برنامه، فایل App.razor نیز قابل مشاهدهاست. کار آن تعریف قالب پیشفرض برنامهاست که برای مثال به Shared\MainLayout.razor اشاره میکند. همچنین کامپوننت مسیریابی نیز در اینجا ذکر شدهاست:
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"> <Found Context="routeData"> <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
- فایل appsettings.json نیز همانند برنامههای استاندارد ASP.NET Core در اینجا مشاهده میشود.
- فایل Program.cs آن که نقطهی آغازین برنامهاست و کار فراخوانی Startup.cs را انجام میدهد، دقیقا با یک فایل Program.cs برنامهی استاندارد ASP.NET Core یکی است.
- در فایل Startup.cs آن، همانند قبل دو متد تنظیم سرویسها و تنظیم میانافزارها قابل مشاهدهاست.
public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton<WeatherForecastService>(); }
قسمتهای جدید متد Configure آن، ثبت مسیریابی توکار BlazorHub است که مرتبط است با اتصال دائم SignalR برنامه و اگر مسیری پیدا نشد، به Host_ هدایت میشود:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); }
که به همراه 13 درخواست و نزدیک به 600 KB دریافت اطلاعات از سمت سرور است.
این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. اگر صفحهی شمارشگر آنرا باز کنیم، با کلیک بر روی دکمهی آن، هرچند مقدار current count افزایش مییابد، اما شاهد post-back متداولی به سمت سرور نیستیم و این صفحه بسیار شبیه به صفحات برنامههای SPA (تک صفحهای وب) به نظر میرسد:
همانطور که عنوان شد، مدخل blazor.server.js فایل Pages\_Host.cshtml، کار به روز رسانی UI و هدایت رخدادها را به سمت سرور به صورت خودکار انجام میدهد. به همین جهت است که post-back ای را مشاهده نمیکنیم و برنامه، شبیه به یک برنامهی SPA به نظر میرسد؛ هر چند تمام رندرهای آن سمت سرور انجام میشوند و توسط SignalR به سمت کلاینت بازگشت داده خواهند شد.
برای نمونه اگر بر روی دکمهی شمارشگر کلیک کنیم، در برگهی network مرورگر، هیچ اثری از آن مشاهده نمیشود (هیچ رفت و برگشتی را مشاهده نمیکنیم). علت اینجا است که اطلاعات متناظر با این کلیک، از طریق web socket باز شدهی توسط SignalR، به سمت سرور ارسال شده و نتیجهی واکنش به این کلیکها و رندر HTML نهایی سمت سرور آن، از همین طریق به سمت کلاینت بازگشت داده میشود.
بررسی ساختار پروژهی Blazor WASM و اجرای آن
پس از اجرای دستور dotnet new blazorwasm در یک پوشهی خالی و ایجاد پروژهی ابتدایی آن:
همان صفحات پروژهی خالی Blazor Server در اینجا نیز قابل مشاهده هستند. این برنامهی نمونه، به همراه سه صفحهی نمایش Home، نمایش یک شمارشگر و نمایش اطلاعاتی از پیش آماده شدهاست. صفحات و کامپوننتهای پوشههای Pages و Shared نیز دقیقا همانند پروژهی Blazor Server قابل مشاهده هستند. مفاهیمی مانند فایلهای Imports.razor_ و App.razor نیز مانند قبل هستند.
البته در اینجا فایل Startup ای مشاهده نمیشود و تمام تنظیمات آغازین برنامه، داخل فایل Program.cs انجام خواهند شد:
namespace BlazorWasmSample { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); } } }
تفاوت ساختاری دیگر این پروژهی WASM، با نمونهی Blazor Server، ساختار پوشهی wwwroot آن است:
که به همراه فایل جدید نمونهی wwwroot\sample-data\weather.json است؛ بجای سرویس متناظر سمت سرور آن در برنامهی blazor server. همچنین فایل جدید wwwroot\index.html نیز قابل مشاهدهاست و محتوای تگ body آن به صورت زیر است:
<body> <div id="app">Loading...</div> <div id="blazor-error-ui"> An unhandled error has occurred. <a href="" class="reload">Reload</a> <a class="dismiss">🗙</a> </div> <script src="_framework/blazor.webassembly.js"></script> </body>
- در ابتدای بارگذاری برنامه، یک loading نمایش داده میشود که در اینجا نحوهی تعریف آن مشخص است. همچنین اگر خطایی رخ دهد نیز توسط div ای با id مساوی blazor-error-ui اطلاع رسانی میشود.
- مدخل blazor.webassembly.js در اینجا، کار دریافت وب اسمبلی و فایلهای NET runtime. را انجام میدهد؛ برخلاف برنامههای Blazor Server که توسط فایل blazor.server.js، یک ارتباط دائم SignalR را با سرور برقرار میکردند تا کدهای رندر شدهی سمت سرور را دریافت و نمایش دهند و یا اطلاعاتی را به سمت سرور ارسال کنند: برای مثال بر روی دکمهای کلیک شدهاست، اطلاعات مربوطه را در سمت سرور پردازش کن و نتیجهی نهایی رندر شده را بازگشت بده. اما در اینجا همه چیز داخل مرورگر اجرا میشود و برای این نوع اعمال، رفت و برگشتی به سمت سرور صورت نمیگیرد. به همین جهت تمام کدهای #C ما به سمت کلاینت ارسال شده و داخل مرورگر به کمک فناوری وب اسمبلی، اجرا میشوند. در اینجا از لحاظ ارسال تمام کدهای مرتبط با UI برنامهی سمت کلاینت به مرورگر کاربر، تفاوتی با فریمورکهایی مانند Angular و یا React نیست و آنها هم تمام کدهای UI برنامه را کامپایل کرده و یکجا ارسال میکنند.
در ادامه در همان پوشه، دستور dotnet run را اجرا میکنیم تا پروژه کامپایل و همچنین وب سرور آن نیز اجرا شده و برنامه در آدرس https://localhost:5001 قابل دسترسی شود.
که به همراه 205 درخواست و نزدیک به 9.6 MB دریافت اطلاعات از سمت سرور است. البته اگر همین صفحه را refresh کنیم، دیگر شاهد دریافت مجدد فایلهای DLL مربوط به NET Runtime. نخواهیم بود و اینبار از کش مرورگر خوانده میشوند:
در این برنامهی سمت کلاینت، ابتدا تمام فایلهای NET Runtime. و وب اسمبلی دریافت شده و سپس اجرای تغییرات UI، در همین سمت و بدون نیاز به اتصال دائم SignalR ای به سمت سرور، پردازش و نمایش داده میشوند. به همین جهت زمانیکه بر روی دکمهی شمارشگر آن کلیک میکنیم، اتفاقی در برگهی network مرورگر ثبت نمیشود و رفت و برگشتی به سمت سرور صورت نمیگیرد.
عدم وجود اتصال SignalR، مزیت امکان اجرای آفلاین برنامهی WASM را نیز میسر میکند. برای مثال یکبار دیگر همان برنامهی Blazor Server را به کمک دستور dotnet run اجرا کنید. سپس آنرا در مرورگر در آدرس https://localhost:5001 باز کنید. اکنون پنجرهی کنسولی که dotnet run را اجرا کرده، خاتمه دهید (قسمت اجرای سمت سرور آنرا ببندید).
بلافاصله تصویر «سعی در اتصال مجدد» فوق را مشاهده خواهیم کرد که به دلیل قطع اتصال SignalR رخ دادهاست. یعنی یک برنامهی Blazor Server، بدون این اتصال دائم، قادر به ادامهی فعالیت نیست. اما چنین محدودیتی با برنامههای Blazor WASM وجود ندارد.
البته بدیهی است اگر یک Web API سمت سرور برای ارائهی اطلاعاتی به برنامهی WASM نیاز باشد، API سمت سرور برنامه نیز باید جهت کار و نمایش اطلاعات در سمت کلاینت در دسترس باشد؛ وگرنه قسمتهای متناظر با آن، قادر به نمایش و یا ثبت اطلاعات نخواهند بود.
npm install @aspnet/signalr-client --save
بررسی محتوای پوشهی node_modules\@aspnet\signalr-client
پس از نصب بستهی «aspnet/signalr-client@»، در مسیر node_modules\@aspnet\signalr-client\dist دو پوشهی src و browser را خواهید یافت. پوشهی src حاوی منبع کامل این کلاینت و همچنین فایلهای Typings مخصوص تایپاسکریپت است.
و پوشهی browser آن شامل دو گروه فایل است:
- در اینجا گروهی از فایلها، حاوی عبارت ES5 هستند و تعدادی خیر. SignalR JavaScript بر اساس ES 6 یا EcmaScript 2015 تهیه شدهاست و از مفاهیمی مانند Promises و arrow functions استفاده میکند. باید دقت داشت که تعدادی از مرورگرها مانند IE از این قابلیتها پیشتیبانی نمیکنند. در بین این فایلها، آنهایی که حاوی عبارت ES5 نیستند، یعنی بر اساس ES 6 تهیه شدهاند. سایر فایلها توسط قابلیت Transpile مربوط به TypeScript به ES5 ترجمه شدهاند. به علاوه حجم این فایلها نیز بیشتر میباشد؛ چون حاوی تعاریف وابستگیهایی هستند که در ES 5 وجود خارجی ندارند. بنابراین بسته به نوع مرورگر مدنظر، یکی از این دو گروه را باید انتخاب کرد؛ ES 6 برای مرورگرهای جدید و ES 5 برای مرورگرهای قدیمی.
- به علاوه در اینجا تعدادی از فایلها حاوی عبارت msgpackprotocol هستند. نگارش جدید SignalR از پروتکلهای هاب سفارشی مانند پروتکلهای باینری نیز پشتیبانی میکند. همچنین حاوی یک پیاده سازی توکار از پروتکلهای باینری بر اساس MessagePack نیز هست. چون حجم کدهای پشتیبانی کنندهی از این پروتکل ویژه بالا است، آنرا به یک فایل مجزا انتقال دادهاند تا در صورت نیاز مورد استفاده قرار گیرد. بنابراین اگر از این پروتکل استفاده نمیکنید، نیازی هم به الحاق آن در صفحات خود نخواهید داشت. فایل third-party-notices.txt نیز مربوط است به یادآوری مجوز استفادهی از MessagePack که MIT میباشد.
- در هر گروه نیز، دو فایل min و معمولی قابل مشاهدهاست. فایلهای min برای توزیع نهایی مناسب هستند و فایلهای غیرفشرده شده برای حالت دیباگ.
استفاده از کلاینت جاوا اسکریپتی SignalR Core
برای کار با کلاینت جاوا اسکریپتی SignalR Core از همان فایلهای موجود در پوشهی node_modules/@aspnet/signalr-client/dist/browser استفاده میکنیم. تفاوت این کلاینت با نگارش قبلی SignalR به صورت یک ذیل است:
1) ارجاع به فایل قدیمی signalR-2.2.1.min.js با فایل جدید signalR-client-1.0.0-alpha1.js جایگزین میشود. اگر میخواهید مرورگرهای قدیمی را پشتیبانی کنید، نگارش ES5 آنرا لحاظ کنید.
2) پروکسیها با new HubConnection جایگزین شدهاند.
3) برای ثبت callbackهای سمت کلاینت، از متد جدید on استفاده میشود.
4) بجای متد done مربوط به jQuery، در اینجا از متد then مربوط به ES6 کمک گرفته شدهاست.
5) کار فراخوانی متدهای هاب توسط متد invoke انجام میشود.
یک مثال: بازنویسی قسمت سمت کلاینت مثال «کار با SignalR Core از طریق یک کلاینت Angular» با jQuery
هرچند کلاینت جدید SignalR Core وابستگی به jQuery ندارد، اما جهت سهولت کار با DOM، کدهای سمت کلاینت مثال قبلی را با jQuery بازنویسی میکنیم. تمام کدهای سمت سرور این مثال با مطلب «کار با SignalR Core از طریق یک کلاینت Angular» یکی است؛ مانند ایجاد هاب، فعالسازی SiganlR در فایل آغازین برنامه و ثبت مسیرهاب. بنابراین در اینجا، این قسمت از کدهای سمت سرور را مجددا تکرار نمیکنیم و تمام نکات آن یکی هستند.
برای کار با کلاینت جاوا اسکریپتی SignalR Core، اینبار دستور ذیل را در ریشهی پروژهی وب اجرا میکنیم (یا هر پروژهای که قرار است مدیریت فایلهای سمت کلاینت و Viewهای برنامه را انجام دهد):
npm init npm install @aspnet/signalr-client --save bower install
مرحلهی بعدی کار، تنظیمات فایل bundleconfig.json است؛ تا تمام اسکریپتهای مورد نیاز جمعآوری و یکی شوند:
[ { "outputFileName": "wwwroot/css/site.min.css", "inputFiles": [ "wwwroot/lib/bootstrap/dist/css/bootstrap.min.css", "wwwroot/css/site.css" ] }, { "outputFileName": "wwwroot/js/site.min.js", "inputFiles": [ "wwwroot/lib/jquery/dist/jquery.min.js", "wwwroot/lib/bootstrap/dist/js/bootstrap.min.js", "node_modules/@aspnet/signalr-client/dist/browser/signalr-client-1.0.0-alpha1-final.min.js", "wwwroot/lib/jquery-validation/dist/jquery.validate.min.js", "wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js", "wwwroot/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js", "wwwroot/js/site.js" ], "minify": { "enabled": false, "renameLocals": false }, "sourceMap": false } ]
با توجه به خروجیهای نهایی فایل bundleconfig.json، تنها نیاز است مداخل ذیل را به فایل layout برنامه اضافه کرد:
<link href="~/css/site.min.css" rel="stylesheet" asp-append-version="true" /> <script src="~/js/site.min.js" type="text/javascript" asp-append-version="true"></script>
مرحلهی بعد، تغییر نام متد send قسمت قبل به broadcastMessage است:
public class MessageHub : Hub { public Task Send(string message) { return Clients.All.InvokeAsync("broadcastMessage", message); } }
در ادامه یک کنترلر ساده را به نام JsClientController با View ذیل ایجاد میکنیم:
<form method="post" asp-action="Index" asp-controller="Home" data-ajax="true" role="form"> <div class="form-group"> <label label-for="message">Message: </label> <input id="message" name="message" class="form-control"/> </div> <button class="btn btn-primary" type="submit">Send To Home/Index</button> <button class="btn btn-success" id="sendmessageDirect" type="button">Send To /message hub directly</button> </form> <div id="discussion"> </div>
از اولین دکمه برای ارسال یک پیام به کنترلر Home که در آن توسط <IHubContext<MessageHub پیامی به تمام کلاینتها ارسال میشود، استفاده شدهاست. دومین دکمه متد Send هاب را مستقیما فراخوانی میکند؛ با این کدهای سمت کلاینت:
@section Scripts { <script type="text/javascript" asp-append-version="true"> $(function() { var connection = new signalR.HubConnection('/message'); connection.on('broadcastMessage', function (message) { // Add the message to the page. var encodedMsg = $('<div />').text(message).html(); $('#discussion').append('<li>' + encodedMsg + '</li>'); }); connection.start().then(function () { console.log('connected.'); $('#sendmessageDirect').click(function () { // Call the Send method on the hub. connection.invoke('send', $('#message').val()); }); }); }); </script> }
- سپس در متد on هست که مشخص میکنیم متد سمت کلاینتی که قرار است از سمت سرور فراخوانی شود، چه نامی دارد. نام آنرا در این مثال broadcastMessage درنظر گرفتهایم. در اینجا پارامتر message از سمت سرور دریافت شده و سپس در صفحهی جاری نمایش داده میشود.
بدیهی است متد Send میتواند تعداد پارامترهای بیشتری را بپذیرد و همچنین متد broadcastMessage نیز محدودیتی از لحاظ تعداد پارامتر ندارد. اگر پارامترهای بیشتری را تعریف کردید، در همینجا باید قید شوند.
- در ادامه کار شروع این اتصال آغاز میشود. در متد then هست که باید کار اتصال دکمهی sendmessageDirect صورت گیرد. چون عملیات اتصال ممکن است زمانبر باشد و connection ارسالی هنوز آغاز نشده باشد. در اینجا نحوهی فراخوانی مستقیم متد Send سمت سرور را با یک پارامتر ملاحظه میکنید. این متد نیز میتواند بر اساس امضای متد Send سمت سرور، تعداد پارامترهای بیشتری را قبول کند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید: SignalRCore2WebApp02.zip
برای اجرا آن باید این دستورات را به ترتیب وارد کنید:
dotnet restore npm install npm install -g bower bower install dotnet watch run
- Bar and Column
- Line and Vertical Line
- Area and Vertical Area
- Bullet
- Pie and Donut
- Scatter
- Scatter Line
- Bubble
-
Radar and Polar
برای رسم نمودار میتوانیم به صورت زیر عمل کنیم:
1- ابتدا باید استایلهای مربوط به Data Visualization را به صفحه اضافه کنیم:
<link href="Content/kendo.dataviz.min.css" rel="stylesheet" /> <link href="Content/kendo.dataviz.default.min.css" rel="stylesheet" />
<div id="chart"></div>
<div id="chart" style="width: 400px; height: 600px"></div>
$("#chart").kendoChart();
همانطور که مشاهده میکنید هیچ دادهایی را هنوز برای چارت تعیین نکردهایم؛ در نتیجه همانند تصویر فوق یک چارت خالی بر روی صفحه نمایش داده میشود. برای چارت فوق میتوانیم خواصی از قبیل عنوان و ... را تعیین کنیم:
$("#chart").kendoChart({ title: { text: "چارت آزمایشی" } });
نمایش دادهها بر روی چارت:
دادهها را میتوان هم به صورت local و هم به صورت remote دریافت و بر روی چارت نمایش داد. اینکار را میتوانیم توسط قسمت series انجام دهیم:
$("#chart").kendoChart({ title: { text: "عنوان چارت" }, series: [ { name: "دادههای چارت", data: [200, 450, 300, 125] } ] });
$("#chart").kendoChart({ title: { text: "عنوان چارت" }, series: [ { name: "دادههای چارت", data: [200, 450, 300, 125] } ], categoryAxis: { categories: [2000, 2001, 2002, 2003] } });
دریافت اطلاعات از سرور:
کدهای سمت سرور:
public class ProductsController : ApiController { public IEnumerable<ProductViewModel> Get() { var products = _productService.GetAllProducts(); var query = products.GroupBy(p => p.Name).Select(p => new ProductViewModel { Value = p.Key, Count = p.Count() }); return query; } } public class ProductViewModel { public string Value { get; set; } public int Count { get; set; } }
سپس برای دریافت اطلاعات از سمت سرور باید DataSource مربوط به چارت را مقداردهی کنیم:
var productsDataSource = new kendo.data.DataSource({ transport: { read: { url: "api/products", dataType: "json", contentType: 'application/json; charset=utf-8', type: 'GET' } }, error: function (e) { alert(e.errorThrown.stack); }, pageSize: 5, sort: { field: "Id", dir: "desc" } }); $("#chart").kendoChart({ title: { text: "عنوان چارت" }, dataSource: productsDataSource, series: [ { field: "Count", categoryField: "Value", aggregate: "sum" } ] });
موارد فوق را میتوانیم به صورت یک افزونه نیز کپسوله کنیم.
کدهای افزونه jquery.ChartAjax:
(function($) { $.fn.ShowChart = function(options) { var defaults = { url: '/', text: 'نمودار دایره ایی', theme: 'blueOpal', font: '13px bbc-nassim-bold', legendPosition: 'left', seriesField: 'Count', seriesCategoryField: 'Value', titlePosition: 'top', chartWidth: 400, chartHeight: 400 }; var options = $.extend(defaults, options); return this.each(function() { var chartDataSource = new kendo.data.DataSource({ transport: { read: { url: options.url, dataType: "json", contentType: 'application/json; charset=utf-8', type: 'GET' } }, error: function (e) { // handle error } }); $(this).kendoChart({ chartArea: { height: options.chartHeight }, theme: options.theme, title: { text: options.text, font: options.font, position: options.titlePosition }, legend: { position: options.legendPosition, labels: { font: options.font } }, seriesDefaults: { labels: { visible: false, format: "{0}%" } }, dataSource: chartDataSource, series: [ { type: "pie", field: options.seriesField, categoryField: options.seriesCategoryField, aggregate: "sum", } ], tooltip: { visible: true, template: "${category}: ${value}", font: options.font } }); }); }; })(jQuery);
chartArea : جهت تعیین طول و عرض چارت
theme : جهت تعیین قالبهای از پیشتعریف شده:
- Black
- BlueOpal
- Bootstrap
- Default
- Flat
- HighContrast
- Material
- MaterialBlack
- Metro
- MetroBlack
- Moonlight
- Silver
-
Uniform
title : جهت تعیین عنوان چارت
legend : جهت تنظیم ویژگیهای قسمت گروهبندی چارت
tooltip : جهت تنظیم ویژگیهای مربوط به نمایش tooltip در هنگام hover بر روی چارت.
لازم به ذکر است در قسمت series میتوانید نوع چارت را تعیین کنید.
$('#chart').ShowChart({ url: "/Report/ByUnit", legendPosition: "bottom" });
دریافت سورس مثال جاری (KendoChart.zip)
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 استفاده میشود.
NiftyDotNet!
NiftyDotNet یک کنترل ASP.Net 2.0 است که امکان ساخت قطعاتی با گوشههای گرد را به سهولت میسر میسازد. این کنترل در حقیقت محصور کنندهی مجموعه Nifty Corners Cube است.
صفحهی اصلی در گوگل کد
یک سری مثال کاربردی
دریافت سورس، مثالها و فایل بایناری آن از رپیدشیر
مثال:
گرد کردن گوشههای div ایی با id مساوی box1 که توسط خاصیت Selectors این کنترل مشخص شده است (اگر class این div برای مثال مساوی cls1 بود این مقدار میبایست مساوی div.cls1 قرار میگرفت؛ شبیه به روش jQuery).
<%@ Register Assembly="NiftyDotNet" Namespace="NiftyDotNet" TagPrefix="cc1" %>
<div id="container">
<cc1:Nifty ID="Nifty1" runat="server" CornerSize="Big" Selectors="div#box1">
</cc1:Nifty>
<div id="box1" style="background: #e6e6e6; color: Black; width: 18em;">
<h1>
NiftyDotNet</h1>
<p>
Just drag & drop to round!</p>
</div>
</div>
Unobtrusive Ajax چیست؟
در حالت معمولی، با استفاده از متد ajax جیکوئری، کار ارسال غیرهمزمان اطلاعات، به سمت سرور صورت میگیرد. چون در این روش کدهای جیکوئری داخل صفحات برنامههای ما قرار میگیرند، به این روش، «روش چسبنده» میگویند. اما با استفاده از افزونهی «jquery.unobtrusive-ajax.min.js» مایکروسافت، میتوان این کدهای چسبنده را تبدیل به کدهای غیرچسنبده یا Unobtrusive کرد. در این حالت، پارامترهای متد ajax، به صورت ویژگیها (attributes) به شکل data-ajax به المانهای مختلف صفحه اضافه میشوند و به این ترتیب، افزونهی یاد شده به صورت خودکار با یافتن مقادیر ویژگیهای data-ajax، این المانها را تبدیل به المانهای ایجکسی میکند. در این حالت به کدهایی تمیزتر و عاری از متدهای چسبندهی ajax قرار گرفتهی در داخل صفحات وب خواهیم رسید.
روش طراحی Unobtrusive را در کتابخانههای معروفی مانند بوت استرپ هم میتوان مشاهده کرد.
پیشنیازهای فعال سازی Unobtrusive Ajax در ASP.NET Core 1.0
توزیع افزونهی «jquery.unobtrusive-ajax.min.js» مایکروسافت، از طریق bower صورت میگیرد که پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 14 - فعال سازی اعتبارسنجی ورودیهای کاربران» با آن آشنا شدیم. در اینجا نیز برای دریافت آن، تنها کافی است فایل bower.json را به نحو ذیل تکمیل کرد:
{ "name": "asp.net", "private": true, "dependencies": { "bootstrap": "3.3.6", "jquery": "2.2.0", "jquery-validation": "1.14.0", "jquery-validation-unobtrusive": "3.2.6", "jquery-ajax-unobtrusive": "3.2.4" } }
[ { "outputFileName": "wwwroot/css/site.min.css", "inputFiles": [ "bower_components/bootstrap/dist/css/bootstrap.min.css", "content/site.css" ] }, { "outputFileName": "wwwroot/js/site.min.js", "inputFiles": [ "bower_components/jquery/dist/jquery.min.js", "bower_components/jquery-validation/dist/jquery.validate.min.js", "bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js", "bower_components/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js", "bower_components/bootstrap/dist/js/bootstrap.min.js" ], "minify": { "enabled": true, "renameLocals": true }, "sourceMap": false } ]
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> <link href="~/css/site.min.css" rel="stylesheet" /> </head> <body> <div> <div> @RenderBody() </div> </div> <script src="~/js/site.min.js" type="text/javascript" asp-append-version="true"></script> @RenderSection("Scripts", required: false) </body> </html>
استفاده از معادلهای واقعی Unobtrusive Ajax در ASP.NET Core 1.0
واقعیت این است که HTML Helper ایجکسی حذف شدهی از ASP.NET Core 1.0، کاری بجز افزودن ویژگیهای data-ajax را که توسط افزونهی jquery.unobtrusive-ajax.min.js پردازش میشوند، انجام نمیدهد و این افزونه مستقل است از مباحث سمت سرور و به نگارش خاصی از ASP.NET گره نخورده است. بنابراین در اینجا تنها کاری را که باید انجام داد، استفاده از همان ویژگیهای اصلی است که این افزونه قادر به شناسایی آنها است.
خلاصهی آنها را جهت انتقال کدهای قدیمی و یا تهیهی کدهای جدید، در جدول ذیل میتوانید مشاهده کنید:
HTML attribute | AjaxOptions |
data-ajax-confirm | Confirm |
data-ajax-method | HttpMethod |
data-ajax-mode | InsertionMode |
data-ajax-loading-duration | LoadingElementDuration |
data-ajax-loading | LoadingElementId |
data-ajax-begin | OnBegin |
data-ajax-complete | OnComplete |
data-ajax-failure | OnFailure |
data-ajax-success | OnSuccess |
data-ajax-update | UpdateTargetId |
data-ajax-url | Url |
در ASP.NET Core 1.0، به علت حذف متدهای کمکی Ajax دیگر خبری از AjaxOptions نیست. اما اگر علاقمند به انتقال کدهای قدیمی به ASP.NET Core 1.0 هستید، معادلهای اصلی این پارامترها را میتوانید در ستون HTML attribute مشاهده کنید.
چند نکته:
- اگر قصد استفادهی از این ویژگیها را دارید، باید ویژگی "data-ajax="true را نیز حتما قید کنید تا سیستم Unobtrusive Ajax فعال شود.
- ویژگی data-ajax-mode تنها با ذکر data-ajax-update (و یا همان UpdateTargetId پیشین) معنا پیدا میکند.
- ویژگی data-ajax-loading-duration نیاز به ذکر data-ajax-loading (و یا همان LoadingElementId پیشین) را دارد.
- ویژگی data-ajax-mode مقادیر before، after و replace-with را میپذیرد. اگر قید نشود، کل المان با data دریافتی جایگزین میشود.
- سه callback قابل تعریف data-ajax-complete، data-ajax-failure و data-ajax-success، یک چنین پارامترهایی را از سمت سرور در اختیار کلاینت قرار میدهند:
parameters | Callback |
xhr, status | data-ajax-complete |
data, status, xhr | data-ajax-success |
xhr, status, error | data-ajax-failure |
برای مثال میتوان ویژگی data-ajax-success را به نحو ذیل در سمت کلاینت مقدار دهی کرد:
data-ajax-success = "myJsMethod"
function myJsMethod(data, status, xhr) { }
return Json(new { param1 = 1, param2 = 2, ... });
مثالهایی از افزودن ویژگیهای data-ajax به المانهای مختلف
در حالت استفاده از Form Tag Helpers که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» بررسی شدند، یک فرم ایجکسی، چنین تعاریفی را پیدا خواهد کرد:
با این ViewModel فرضی
using System.ComponentModel.DataAnnotations; namespace Core1RtmEmptyTest.ViewModels.Account { public class RegisterViewModel { [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } } }
@using Core1RtmEmptyTest.ViewModels.Account @model RegisterViewModel @{ } <form method="post" asp-controller="TestAjax" asp-action="Index" asp-route-returnurl="@ViewBag.ReturnUrl" class="form-horizontal" role="form" data-ajax="true" data-ajax-loading="#Progress" data-ajax-success="myJsMethod"> <input asp-for="Email" class="form-control" /> <span asp-validation-for="Email" class="text-danger"></span> <button type="submit">ارسال</button> <div id="Progress" style="display: none"> <img src="images/loading.gif" alt="loading..." /> </div> </form> @section scripts{ <script type="text/javascript"> function myJsMethod(data, status, xhr) { alert(data.param1); } </script> }
این View از کنترلر ذیل استفاده میکند:
using Core1RtmEmptyTest.ViewModels.Account; using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { public class TestAjaxController : Controller { public IActionResult Index() { return View(); } [HttpPost] public IActionResult Index([FromForm]RegisterViewModel vm) { var ajax = isAjax(); if (ajax) { // it's an ajax post } if (ModelState.IsValid) { //todo: save data return Json(new { param1 = 1, param2 = 2 }); } return View(); } private bool isAjax() { return Request?.Headers != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest"; } } }
و یا Action Link ایجکسی نیز به صورت خلاصه به این نحو قابل تعریف است:
<div id="EmployeeInfo"> <a asp-controller="MyController" asp-action="MyAction" data-ajax="true" data-ajax-loading="#Progress" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#EmployeeInfo"> Get Employee-1 info </a> <div id="Progress" style="display: none"> <img src="images/loading.gif" alt="loading..." /> </div> </div>
نکتهای در مورد اکشن متدهای ایجکسی در ASP.NET Core 1.0
همانطور که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API»، قسمت «تغییرات Model binding پیش فرض، برای پشتیبانی از ASP.NET MVC و ASP.NET Web API» نیز ذکر شد:
public IActionResult Index([FromBody] MyViewModel vm) { return View(); }