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$ استفاده کرد:
$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"]; } })
تا اینجای کار، اگر آدرس "state1/" وارد شود، فایل "partials/state1.html" در "ui-view" فایل "index.html" بارگذاری خواهد شد. اگر آدرس "state1/list/" وارد شود، ابتدا فایل "partials/state1.html" در "ui-view" فایل "index.html" بارگذاری شده، سپس فایل "partials/state1.list.html" در "ui-view" آمده در فایل فایل "partials/state1.html" بارگذاری میشود. این همان امکان حالتها و viewهای تو در تو است که UI-Router فراهم میکند.
اینجا میتوانید خروجی کدهای بالا را مشاهده کنید.
اگر مستقیما url یک حالت فرزند وارد شود، یا به عبارت دیگر، اگر بخواهیم مستقیما برنامه به حالتی که فرزند حالت دیگر است برود، UI-Router برنامه را ابتدا به حالت پدر، و پس از آن به حالت فرزند خواهد برد. حالت فرزند دو چیز را از حالت پدر به ارث میبرد:
- وابستگیهای فراهم شده در حالت پدر به وسیله "resolve"
- دادههای سفارشی مشخص شده در خصوصیت data حالت پدر
.state('state1', { url: "/state1", templateUrl: "partials/state1.html", data:{ foodata: 'addorder' } })
$state.current.data.foodata
Viewهای نامگذاری شده و چندگانه
یکی دیگر از قابلیتهای کاربردی UI-Router امکان داشتن چند ui-view در هر template است (استفاده همزمان از این قابلیت و حالتهای تو در تو، امکان مدیریت واسط کاربری را به خوبی فراهم میکند). برای توضیح این قابلیت، با راهنمای UI-Router همراه شویم:
1. دستورالعمل برپایی UI-Router که در بالا آمده را اجرا کنید.
2. یک یا چند ui-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>
3. حالتهای برنامهتان را در روال config ماژول تعریف کنید:
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" } } }) });
4. خروجی کدهای بالا را اینجا مشاهده کنید.
چند نکته
UI-Router جزئیات فراوانی دارد و آنچه آمد تنها پرده برداری از آن بود. دلم میخواست میتوانستم بیش از این آن را معرفی کنم، اما متاسفانه این روزها وقت آزاد کافی ندارم. در انتها میخواهم به چند نکته اشاره کنم:
روش controller as
برای استفاده از روش controller as در UI-Router باید به این ترتیب عمل کنید:
.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" } })
حالتهای انتزاعی
حالت انتزاعی حالتی است که url ندارد و در نتیجه برنامه نمیتواند در آن حالت قرار گیرد. حالتهای انتزاعی بسیار به درد خور هستند! مثلا فرض کنید چند حالت دارید که اشتراکاتی با هم دارند (همه باید در template مشابهی بارگذاری شود، یا وابستگیهای یکسانی دارند، یا حتی سطح دسترسی یکسان). با تعریف یک حالت انتزاعی و جمع کردن همه وابستگیها در آن، و تعریف حالتهای مورد نظرتان به عنوان فرزندان حالت انتزاعی، میتوانید اشتراکات حالتهای برنامه را سادهتر مدیریت کنید.
حساسیت به حروف بزرگ و کوچک
در سرویس route$ با مقداردهی خصوصیت caseInsensitiveMatch میتوانستیم مشخص کنیم که بزرگ و کوچک بودن حروف در تطبیق آدرس صفحه با پارامتر route در نظر گرفته بشود یا نه. خودمانیش اینکه url به حروف بزرگ و کوچک حساس باشد یا نه. متاسفانه در UI-Router از این امکان خبری نیست (البته فعلا) و آدرسهای تعریف شده به حروف بزگ و کوچک حساس هستند.
اینجا روشی برای حل این مشکل پیشنهاد شده، به این ترتیب که همه urlهای وارد شده به حروف کوچک تبدیل شود (راستش من این راه حل را نمیپسندم!).
چند روز قبل هم تغییراتی در کد UI-Router داده شده که امکان حساس نبودن به حروف کوچک و بزرگ فراهم شود. این تغییر هنوز در نسخه نهایی فایل UI-Router نیامده است. هرچند اگر بیاید هم آنچه تا امروز (23 اسفند 92) انجام شده مشکل را حل نمیکند.
اگر شما هم مثل من میخواهید کلا آدرسها به حروف بزرگ و کوچک حساس نباشند، و فرصت حل کردن اساسی مشکل را هم ندارید به این ترتیب عمل کنید:
- در فایل "angular-ui-router.js" عبارت "new RegExp(compiled)" را پیدا کرده و آن را به "RegExp(compiled, 'i')" تبدیل کنید. و یا در "angular-ui-router.min.js" (هرکدام از فایلها که استفاده میکنید) عبارت new RegExp(o) را پیدا کرده و آن را به new RegExp(o, "i") تبدیل کنید. همین؛ صدایش را هم در نیاورید!
- Identity server یک محصول کاملا مجزای از ASP.NET Core Identity است و توسط تیم دیگری خارج از مایکروسافت توسعه داده میشود و این دو ارتباطی به هم ندارند.
- هرچند امکان استفادهی از ASP.NET Core Identity برای مدیریت کاربران Identity server هم وجود دارد.
- کاری که در این سری انجام شده، ابتدا در «قسمت چهارم - نصب و راه اندازی IdentityServer» فایلهای Quick Start UI را به پروژهی IDP اضافه کردیم (یعنی دقیقا از کدهای اصلی تیم Identity server استفاده شده). بعد در این قسمت، سایر کدهای اصلی بستهی EF Core آنرا (^ و ^) به پروژه اضافه و سفارشی سازی کرده و در قسمتهای دیگر، این کدها را تکمیل کردهایم. بنابراین در این سری از کدهای استاندارد خود Identity server استفاده شده و سپس توسعهی بیشتری پیدا کردهاند. برای نمونه کلاسهای موجودیتهای مثال این سری، از اینجا تامین شدهاند.
پاسخ به بازخوردهای پروژهها
Helper AjaxDialog چطوری کار میکنه؟
سؤال شما «همون AjaxDialog هست که شامل LinkDialog و ButtonDialog میباشد» بود. یعنی این متدها را پیشتر مشاهده کردید.
حاصل جستجوی اینها در کل پروژه مانند Ajax.LinkDialog و Ajax.ButtonDialog فایلهای استفاده شده را مشخص میکنند. مثالهای استفاده را در این فایلها پیدا کنید. پیشنیاز آن که jQuery UI Dialog هست پیشتر عنوان شد و باید به پروژه شما اضافه شود. نحوهی استفاده نهایی از این متدهای کمکی در فایل adminjs.js وجود دارد.
حاصل جستجوی اینها در کل پروژه مانند Ajax.LinkDialog و Ajax.ButtonDialog فایلهای استفاده شده را مشخص میکنند. مثالهای استفاده را در این فایلها پیدا کنید. پیشنیاز آن که jQuery UI Dialog هست پیشتر عنوان شد و باید به پروژه شما اضافه شود. نحوهی استفاده نهایی از این متدهای کمکی در فایل adminjs.js وجود دارد.
Find all "Ajax.ButtonDialog", Subfolders, Find Results 1, Entire Solution, "" Matching lines: 20 Matching files: 10 Total files searched: 750
مطالب
JQuery Plugins #1
جیکوئری به عنوان مهمترین و پرکاربردترین کتابخانه جاوا اسکریپتی، حالا در اکثر سایتهای اینترنتی استفاده میشود و هر روز به قابلیتها و امکانات آن اضافه میگردد. اما بیش از خود این کتابخانه، پلاگینهای آن است که تحول عظیمی را در طراحی وب سایتها ایجاد نموده است. از انواع اسلایدها، تصاویر، منوها، Tooltip ها، نمودارها، انیمیشن، جداول و هزاران پلاگین دیگر، همه و همه کدهای جاوا اسکریپتی است که با استفاده از جی کوئری به صورت پلاگین نوشته شده است و امکان استفاده مجدد را به ما میدهد.
محتوای پلاگین
حفظ خاصیت زنجیرهای پلاگین ها
در پلاگین بالا با از تابع each برای روی this و برگرداندن آن با return برای حفظ خاصیت زنجیرهای پلاگین استفاده مینماییم. در تابع each میبایست از (this)$ برای انجام عملیات بر روی شیء پاس داده شده استفاده کنیم. بدین صورت بعد از صدا زدن پلاگین، دوباره میتوانیم از هر پلاگین یا تابع جی کوئری دیگری بر روی خروجی استفاده نماییم.
از کجا شروع کنیم
برای نوشتن پلاگین یک تابع با نام خاصیتی جدید را به jQuery.fn اضافه مینماییم.
اما، برای اینکه بتوانیم از میانبر $ در پلاگین استفاده نماییم و تداخلی با سایر کتابخانهها نداشته باشد، از الگوی (IIFE (Immediately Invoked Function Expression D به صورت زیر استفاده مینماییم:
jQuery.fn.myPlugin = function() { //محتویات پلاگین را اینجا مینویسیم };
(function( $ ) { $.fn.myPlugin = function() { //محتویات پلاگین را اینجا مینویسیم }; })( jQuery );
محتوای پلاگین
حال میتوانیم در تابع، کدهای پلاگین خود را بنویسیم. برای دسترسی به شیء پاس داده شده به پلاگین، از کلمه کلیدی this استفاده کرده و لازم نیست از (this)$ استفاده نماییم. در زیر یک پلاگین ساده تهیه شده است که با رفتن ماوس بر روی یک متن، خطی زیر آن میکشد:
(function($){ $.fn.underline= function() { this.hover(function(){ $(this).css( { text-decoration : underline }) }, function(){ $(this).css( { text-decoration : none } ) }); }; })(jQuery); $("p").underline();
پلاگین بالا مقدار یا شیء ایی را بر نمیگرداند؛ اما اگر بخواهیم مقداری را برگردانیم از return استفاده مینماییم:
(function( $ ){ $.fn.maxHeight = function() { var max = 0; this.each(function() { max = Math.max( max, $(this).height() ); }); return max; }; })( jQuery );
var tallest = $('div').maxHeight(); // بیشترین ارتفاع عنصر را برمی گرداند
حفظ خاصیت زنجیرهای پلاگین ها
در مثال بالا یک مقدار عددی برگردانده شده است؛ اما برای اینکه بتوانیم بصورت زنجیر وار خروجی پلاگین را به تابع یا هر پالاگین دیگری پاس دهیم از تابع each بصورت زیر استفاده مینماییم:
(function( $ ){ $.fn.lockDimensions = function( type ) { return this.each(function() { var $this = $(this); if ( !type || type == 'width' ) { $this.width( $this.width() ); } if ( !type || type == 'height' ) { $this.height( $this.height() ); } }); }; })( jQuery );
$('div').lockDimensions('width').css('color', 'red');
پیش فرضها و تنظیمات
در پلاگینهای پیشرفتهتر میتوانیم تنظیمات پیش فرضی را برای پلاگین در نظر بگیریم و این تنظیمات را به عنوان پارامتر ورودی از کاربر دریافت نماییم. جی کوئری دارای تابعی به نام extend است که امکان گسترش و ترکیب دو شیء را امکان پذیر میسازد به مثال زیر توجه نمایید:
(function( $ ){ $.fn.tooltip = function( options ) { var settings = $.extend( { 'location' : 'top', 'background-color' : 'blue' }, options); return this.each(function() { // Tooltip plugin code here }); }; })( jQuery );
$('div').tooltip({ 'location' : 'left' });
در این مثال، شیء settings با دو خاصیت location و background-color تعریف شده که با شیء options که از ورودی پلاگین دریافت نمودهایم با استفاده از تابع extend ترکیب شده است. خاصیتهای که تعیین نشده باشند با مقادیر پیش فرض آنها تکمیل میگردد.
ادامه دارد...
وبلاگها و سایتهای ایرانی
Visual Studio
امنیت
- رمزنگاری کوانتمی و شبکهای رمزنگاری شده با این روش
ASP. Net
طراحی وب
اسکیوال سرور
به روز رسانیها
ابزارها
- نگارش جدید برنامه RSS Bandit . (برنامهای سورس باز نوشته شده با سی شارپ)
- Visual Round Trip Analyzer (استفاده از NetMon API)
سیشارپ
- Interactive GUI Shell از توسعه دهندگان تیم Mono
عمومی دات نت
- آشنایی با کلاس CommaDelimitedStringCollection در دات نت فریم ورک
- مونو و دات نت گزارشی از PDC2008
CPP
دلفی
- CompilerPlugin برای دلفی 2009. (توسط آن میتوان پروژههای دلفی 2007 را با دلفی 2009 نیز کامپایل کرد)
- نسخه جدید CnPack منتشر شد. (با پشتیبانی دلفی 5 تا 2009)
ویندوز
- آنالیز crash dumps ویندوز . (آیا میدانید صفحات آبی ویندوز را چگونه باید تفسیر کرد؟)
Office
- آشنایی با یک سری از اصطلاحات outlook 2007 برای برنامه نویسها. (اگر قصد داشته باشید یک add-in را برای outlook 2007 با استفاده از امکانات VSTO توسعه دهید، آشنایی با این اصطلاحات بسیار ضروری خواهد بود)
متفرقه
وبلاگهای ایرانی
- گزارشی از PDC 2008 microsoftpdc.com و یک سری ویدیوی مرتبط با دات نت
Visual Studio
- ویژوال استودیو 2010 و دات نت فریم ورک 4، نگارش CTP برای دریافت!
- و تازههای آن (توسط یکی از اعضای اصلی تیم NHibernate)
- دمویی از ایجاد نمودارهای UML با VS.Net 2010 . (خوشبختانه حجم این مورد در مقایسه با دموهای PDC2008 بسیار کمتر است!)
امنیت اطلاعات
ASP. Net
- نگارش CTP2 پروژهVelocity . (این پروژه قرار است راه حل جامع caching مایکروسافت برای ASP.Net باشد)
طراحی وب
اسکیوال سرور
به روز رسانیها
- سرویس پک 3 بتا اس کیوال سرور 2005 هم ارائه شد (نگارش نهایی آن تا پایان سال جاری میلادی ارائه میشود.)
ابزارها
- مجموعه CodeRush یک نگارش express رایگان نیز ارائه داد. (به نظر من نگارش کامل آن بهترین ابزار refactoring برای VS.Net است)
سیشارپ
- ویژگیهای جدید C# 4.0 ، قسمت اول، واژه کلیدی جدیدی به نام dynamic
- ویژگیهای جدید C# 4.0 ، قسمت دوم، پارامترهای پیش فرض (یا آرگومانهای اختیاری). (چیزی شبیه به VB !! بدون نیاز به overloading برای پیاده سازی آن)
دلفی
- محصور کننده جدید SQLite برای دلفی 2009 (رفع مشکلات عدم سازگاری نگارش قبلی با سیستم یونیکد دلفی 2009)
- ویدیویی از Delphi Prism . (نگارشی از دلفی که به شکل افزونهای کاملا یکپارچه در VS.Net قابل دسترسی است)
SharePoint
- سطوح دسترسی کدها در SharePoint . (به شکل زیبایی این مساله را که مشکل اولیه اکثر وب پارت نویسها است توضیح داده است)
ویندوز
متفرقه
اشتراکها