<md-tab-group> <md-tab label="Tab 1">Content 1</md-tab> <md-tab label="Tab 2">Content 2</md-tab> </md-tab-group>
\/Date(1374222094520)\/
moment("/Date(1198908717056-0700)/"); // December 28 2007 10:11 PM
{{ name | uppercase }}
{{post.date | jalaliDate:'jYYYY/jMM/jDD hh:mm' }}
app.filter('jalaliDate', function () { return function (inputDate, format) { var date = moment(inputDate); return date.fromNow() + " " + date.format(format); } });
- بررسی وضعیت فعلی رمزنگاری SSL | فرهاد جعفری | www.winbeta.net
- دو محدودیت Metro UI پیش روی برنامه نویسان ایرانی | www.persiadevelopers.com
- سامسونگ، اولین تبلت ویندوز ۸ را برای مایکروسافت تولید می کند | علی پارسا | www.winbeta.net
- گذری بر سرویس پشتیبانگیری Volume Shadow Copy (قسمت دوم) | Mohammad Shams Javi | blog.mshams.ir
- ASP.NET MVC 4 و پشتیبانی از مرورگرهای موبایل | geekswithblogs.net
- IDE رایگان و سورس باز SharpDevelop_4.1.0.8000 منتشر شد | sourceforge.net
- Mono برای Android 1.2ارائه شد | blog.xamarin.com
- آشنایی با AutoTest.Net | bloggingabout.net
- برپایی سایت GeekQuiz.Net | www.mehdi-khalili.com
- جستجوی بهبود یافته در نگارش بعدی ویژوال استودیو | blogs.msdn.com
- طراحی کلاسهای fluent | www.realwebdevelopers.com
- مروری بر تازههای نگارش بعدی ASP.NET | weblogs.asp.net
- معرفی SVG | geekswithblogs.net
- نگارش آلفا 3 Code First Migrations مرتبط با EF منتشر شد | blogs.msdn.com
- وضعیت دریافتهای کارکنان آی تی در سال 2011 در آنسوی آبها | feeds.dzone.com
- وقتی یک نفر مصاحبه کننده استخدامی را سرکار میگذارد! | ayende.com
آشنایی با AOP Interceptors
ابزارهایی جهت تولید AOP Interceptors
متداولترین کامپوننتهای خارجی که جهت تولید AOP Interceptors مورد استفاده قرار میگیرند، همان IOC Containers معروف هستند مانند StructureMap، Ninject، MS Unity و غیره.
سایر ابزارهای تولید AOP Interceptors، از روش تولید Dynamic proxies بهره میگیرند. به این ترتیب مزین کنندههایی پویا، در زمان اجرا، کدهای شما را محصور خواهند کرد. (نمونهای از آنرا شاید در حین کار با ORMهای مختلف دیده باشید).
نگاهی به فرآیند Interception
زمانیکه از یک IOC Container در کدهای خود استفاده میکنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست میکند. عموما اینکار با درخواست یک اینترفیس صورت میگیرد؛ هرچند محدودیتی نیز وجود نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
ج) سپس، کد فراخوان، وهله دریافتی را مورد پردازش قرار داده و شروع به استفاده از متدها و خواص آن خواهد نمود.
اکنون با اضافه کردن Interception به این پروسه، چند مرحله دیگر نیز در این بین به آن اضافه خواهند شد:
الف) در اینجا نیز در ابتدا کد فراخوان، درخواست وهلهای را بر اساس اینترفیسی خاص به IOC Container ارائه میدهد.
ب) IOC Container نیز سعی در وهله سازی درخواست رسیده بر اساس تنظیمات اولیه خود میکند.
ج) اما در این حالت IOC Container تشخیص میدهد، نوعی که باید بازگشت دهد، علاوه بر وهله سازی، نیاز به مزین سازی توسط Aspects و پیاده سازی Interceptors را نیز دارد. بنابراین نوع مورد انتظار را در صورت وجود، به یک Dynamic Proxy، بجای بازگشت مستقیم به فراخوان ارائه میدهد.
د) در ادامه Dynamic Proxy، نوع مورد انتظار را توسط Interceptors محصور کرده و به فراخوان بازگشت میدهد.
ه) اکنون فراخوان، در حین استفاده از امکانات شیء وهله سازی شده، به صورت خودکار مراحل مختلف اجرای یک Aspect را که در قسمت قبل بررسی شدند، سبب خواهد شد.
نحوه ایجاد Interceptors
برای ایجاد یک Interceptor دو مرحله باید انجام شود:
الف) پیاده سازی یک اینترفیس
ب) اتصال آن به کدهای اصلی برنامه
در ادامه قصد داریم از یک IOC Container معروف به نام StructureMap در یک برنامه کنسول استفاده کنیم. برای دریافت آن نیاز است دستور پاورشل ذیل را در کنسول نوگت ویژوال استودیو فراخوانی کنید:
PM> Install-Package structuremap
البته باید دقت داشت که برای استفاده از StructureMap نیاز است به خواص پروژه مراجعه و سپس حالت Client profile را به Full profile تغییر داد تا برنامه قابل کامپایل باشد.
using System; using StructureMap; namespace AOP00 { public interface IMyType { void DoSomething(string data, int i); } public class MyType : IMyType { public void DoSomething(string data, int i) { Console.WriteLine("DoSomething({0}, {1});", data, i); } } class Program { static void Main(string[] args) { ObjectFactory.Initialize(x => { x.For<IMyType>().Use<MyType>(); }); var myType = ObjectFactory.GetInstance<IMyType>(); myType.DoSomething("Test", 1); } } }
در اینجا یک اینترفیس نمونه و پیاده سازی آنرا ملاحظه میکنید. همچنین نحوه آغاز تنظیمات StructureMap و نحوه دریافت یک وهله متناظر با IMyType نیز بیان شدهاند.
نکتهی مهمی که در اینجا باید به آن دقت داشت، وضعیت شیء myType حین فراخوانی متد myType.DoSomething است. شیء myType در اینجا، دقیقا یک وهلهی متداول از کلاس myType است و هیچگونه دخل و تصرفی در نحوه اجرای آن صورت نگرفته است.
خوب! تا اینجای کار را احتمالا پیشتر نیز دیده بودید. در ادامه قصد داریم یک Interceptor را طراحی و مراحل چهارگانه اجرای یک Aspect را در اینجا بررسی کنیم.
در ادامه نیاز خواهیم داشت تا یک Dynamic proxy را نیز مورد استفاده قرار دهیم؛ از این جهت که StructureMap تنها دارای Interceptorهای وهله سازی اطلاعات است و نه Method Interceptor. برای دسترسی به Method Interceptors نیاز به یک Dynamic proxy نیز میباشد. در اینجا از Castle.Core استفاده خواهیم کرد:
PM> Install-Package Castle.Core
سپس کلاس ذیل را به پروژه جاری اضافه کنید:
using System; using Castle.DynamicProxy; namespace AOP00 { public class LoggingInterceptor : IInterceptor { public void Intercept(IInvocation invocation) { try { Console.WriteLine("Logging On Start."); invocation.Proceed(); //فراخوانی متد اصلی در اینجا صورت میگیرد Console.WriteLine("Logging On Success."); } catch (Exception ex) { Console.WriteLine("Logging On Error."); throw; } finally { Console.WriteLine("Logging On Exit."); } } } }
اکنون برای معرفی این کلاس به برنامه کافی است سطرهای ذیل را اندکی ویرایش کنیم:
static void Main(string[] args) { ObjectFactory.Initialize(x => { var dynamicProxy = new ProxyGenerator(); x.For<IMyType>().Use<MyType>(); x.For<IMyType>().EnrichAllWith(myTypeInterface => dynamicProxy.CreateInterfaceProxyWithTarget(myTypeInterface, new LoggingInterceptor())); }); var myType = ObjectFactory.GetInstance<IMyType>(); myType.DoSomething("Test", 1); }
برای مثال اکنون با فراخوانی متد myType.DoSomething، ابتدا کنترل برنامه به پروکسی پویای تشکیل شده توسط Castle.Core منتقل میشود. در اینجا هنوز هم متد DoSomething فراخوانی نشده است. ابتدا وارد بدنه متد public void Intercept خواهیم شد. سپس سطر invocation.Proceed، فراخوانی واقعی متد DoSomething اصلی را انجام میدهد. در ادامه باز هم فرصت داریم تا مراحل موفقیت، خطا یا خروج را لاگ کنیم.
تنها زمانیکه کار متد public void Intercept به پایان میرسد، سطر پس از فراخوانی متد myType.DoSomething اجرا خواهد شد.
در این حالت اگر برنامه را اجرا کنیم، چنین خروجی را نمایش میدهد:
Logging On Start. DoSomething(Test, 1); Logging On Success. Logging On Exit.
برای اینکه فراخوانی قسمت On Error را نیز ملاحظه کنید، یک استثنای عمدی را در متد DoSomething قرار داده و مجددا برنامه را اجرا کنید.
توسعه سیستم مدیریت محتوای DNTCms - قسمت سوم
کنترلهای زیر جهت ورود اطلاعات در ویرایشگر پشتیبانی میشوند:
- text
- textarea
- select
- date
- datetime
- dateui
- combodate
- html5types
- checklist
- wysihtml5
- typeahead
- typeaheadjs
- select2
clear | دکمهای جهت حذف محتوای کادر متنی است. مقدار پیش فرض آن true است. |
escape | برای
دفاع در برابر کدهای مخرب html به کار میرود و کاراکترهای مدنظر را در صورت
true بودن غیرفعال میکند. البته اگر از خاصیت display استفاده کنید این
گزینه تاثیرش را از دست خواهد داد. |
inputclass | یک کلاس css را به کادر متنی اعمال میکند. |
placeholder | مقدار داده شده را در صورتی که کادر متنی خالی باشد، نشان میدهد. |
tpl | به معنی یک قالب. شما میتوانید کد html تگ input خود را وارد کنید؛ ولی توصیه نمیشود. |
TextArea
همان خاصیتهای قبلی را دارد بعلاوه rows که نمایانگر مقدار ارتفاع آن است.
select
خاصیتهای escape,input,class و tpl را دارد بهعلاوه خاصیتهای زیر:
prepend | همانند گزینه پایینی است ولی قبل از آن دادههای خود را اضافه میکند. |
source | از
آنجا که یک لیست، لیستی از آیتمها را دارد و کاربر یکی از آنها را
انتخاب میکند، این بخش، منبع آیتمها را معرفی میکند. این خاصیت چهار نوع
داده میپذیرد: آرایه یا شیءایی از مقادیر. تابعی که بعد از انجام هر عملی،
اطلاعات به آن پاس میشوند و یا از نوع رشته که این رشته یک آدرس سمت سرور
است که با درخواست از آن آدرس، اطلاعات را دریافت میکند. |
sourceCache | اگه خاصیت بالا با آدرسی پر شده باشد که از سمت سرور بخواند، در دفعات بعدی مقدار دریافتی را از کش خواهد خواند. |
sourceError | یک پیام خطا هنگام بارگزاری اطلاعات |
sourceOptions | در
صورتیکه قصد اضافه کردن پارامتری را به درخواست ایجکسی دارید. یک شیء از پارامترها را به آن
نسبت میدهیم و برای رونویسی پارامترها از یک تابع استفاده میکنیم که نحوهی تغییرات را قبلا در جدول شماره یک دیدهاید. |
date
خاصیتهای مشترک قبلی : tpl,input,class,escape و clear است.
datepicker | پیکربندی تقویم را بر عهده دارد. برای اطلاعات بیشتر در مورد پیکربندی تقویم به این لینک مراجعه فرمایید.{ weekStart: 0, startView: 0, minViewMode: 0, autoclose: false } |
format | قالب بندی فرمت تاریخ جهت ارسال به سرور\ حالت پیش فرض yyyy-mm-dd مقادیری که میتوان به کار برد: yy yyyy mm m dd d |
viewformat | این فرمت هنگام نمایش به کار میآید و در صورتیکه مقدار عنصر در این قالب نباشد، آن را تبدیل میکند. |
datetime در بوت استراپ
کاملا مشترک با مورد قبلی.
dateUI
مختص JqueryUI است و کاملا مشترک با مورد قبلی.
combodate
موارد مشترک قبلی را دارد ولی به جای خاصیت datepicker از combodate استفاده میشود که پیکربندی آن در این لینک قرار دارد.
نوعهای HTML 5
شامل موارد زیر است:
- password
- url
- tel
- number
- range
- time
خاصیتهای ذکر شده در مورد نوع text، در مورد آنها نیز صدق میکند.
checklist
همانند نوع select است؛ فقط خاصیت separator را دارد که کارش جدا کردن مقادیر است و مقدار پیش فرض آن علامت ',' است.
wysihtml5
سورس و دمو ی این نوع ادیتور که بر پایهی بوت استرپ بنا شده است و زحمت اضافه کردن کتابخانهها به صفحه، بر عهده شماست.
مداخل زیر را به طور دستی به صفحه اضافه کنید:
<link href="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.css" rel="stylesheet" type="text/css"></link> <script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/wysihtml5-0.3.0.min.js"></script> <script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min.js"></script>
<script src="js/inputs-ext/wysihtml5/wysihtml5.js"></script>
typeahead
این گزینه فقط مختص بوت استرپ 2 است و یک کنترل autocomplete به شمار میآید. منبع دادههای آن از طریق خاصیت source به دو صورت آرایه و object تامین میگردد.
['text1', 'text2', 'text3' ...] //or [{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]
typeaheadjs
همانند قبلی است و بر اساس twitterBootstrap است و شامل همان خصوصیات قبلی است. تنها خصوصیت typeahead آن است که باید از این پیکربندی استفاده کنید.
Select2
این المان بر اساس این کتابخانه سورس باز ایجاد میشود. و مستندات آن شامل جزئیات و پیکربندی آن میشود. برای معرفی آن فایلهای زیر را به صفحه معرفی کنید.
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link> <script src="select2/select2.js"></script>
<link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>
نکته: در حال حاضر خاصیت autotext روی این المان جواب نمیدهد و میتوانید از خاصیت data-value به جای آن استفاده کنید.
قالبی نو برای ویرایشگر
ویرایشگر فعلی ما به خصوص در بوت استرپ، ظاهر فوق العادهای دارد. ولی اگر بازهم مایل به تغییر و رونویسی هستید، این امکان فراهم شده است.
fn.editableform.template$
مقدار پیش فرض آن که حتما باید شامل تگ فرم و کلاسهای مدنظر باشد:
<form> <div> <div><div></div><div></div></div> <div></div> </div> </form>
- control-group
- editable-input
- editable-buttons
- editable-error-block
fn.editableform.buttons$
<button type="submit">ok</button> <button type="button">cancel</button>
و نهایتا جهت تغییر loading
fn.editableform.loading$
<div></div>
گاهی اوقات نیاز است که خصوصیات این ویرایشگر را در شرایط متغیر صفحه کنترل کنیم، برای مثال گاهی پیش میآید که بخواهید در یک شرایط خاص ویرایشگر یک المان خاص را غیرفعال کنید. کد زیر مثال این تغییرات است.
$('#favsite').editable('option', 'disabled', false);
متدها و رویدادها
متدهایی که روی آن قابل اجراست:
editable | ویرایشگر را بر اساس مقادیر اولیه روی عنصر مشخص شده فعال میکند. |
() activate | فوکوس را به input ویرایشگر باز میگرداند. |
() destory | حذف ویژگی ویرایش از روی عنصر |
() disable | غیرفعال کردن ویرایشگر |
() enable | فعال سازی آن |
()getvalue | باعث بازگردانی
مقدار جاری همه عناصر توسط شیء جفت کلید مقدار میشود و عناصری که شامل
متن یا مقداری نیستند، از آن حذف میشوند. در صورتیکه قصد دارید مقدار تنها
یک عنصر قابل دریافت باشد، با خاصیت isSingle آن را true کنید. $('#username, #fullname').editable('getValue'); //result: { username: "superuser", fullname: "John" } //isSingle = true $('#username').editable('getValue', true); //result "superuser" |
()hide | مخفی کردن تگ فرم ویرایشگر |
(option(key,value | تغییر خصوصیات یک عنصر که در بالا هم نمونه کد آن را دیدیم. |
(setvalue(value,convertStr | ست کردن مقدار جدید کنترل و پارامتر دوم وضعیت تبدیل این مقدار به فرمت داخلی است که برای آن تعریف شده است مثل date |
() show | نمایش ویرایشگر |
( submit(options | در
صورتی که خاصیت ارسال خودکار به سمت سرور را غیر فعال کرده باشید، با این
گزینه میتوانید همه اطلاعات و تغییرات را ارسال کنید. برای ایجاد فرم بر
اساس ویرایشگرها و ارسال اطلاعات با کلیک بر روی دکمه submit کاربرد دارد. یک مثال
در این زمینه . پارامترهای options به شرح زیر هستند: url data ajaxoptions (error(obj (success(obj,config از نسخه 1.5.1 میتوان این گزینه را به راحتی روی یک المان خاص هم صدا زد: $('#username').editable('submit') |
() toggle | کدی که صدا زده میشود بین دو وضعیت show و hide سوئیچ میکند. |
() toggleDisabled | تغییر وضعیت بین دو حالت enable و disable |
() validate | انجام اعتبارسنجی بر روی همه کنترل ها.$('#username, #fullname').editable('validate'); // possible result: { username: "username is required", fullname: "fullname should be minimum 3 letters length" } |
رویدادها
hidden | این
رویداد زمانی رخ میدهد که ویرایشگر دیگر قابل مشاهده نیست و شامل دو پارامتر
event و reason است. reason دلیل اینکه چرا ویرایشگر از دید خارج شده است
را با یکی از گزینههای زیر مشخص میکند. save cancel onblur nochange manual $('#username').on('hidden', function(e, reason) { if(reason === 'save' || reason === 'cancel') { //auto-open next editable $(this).closest('tr').next().find('.editable').editable('show'); } }); |
init | موقعی صدا زده میشود که متد editable روی عنصر صدا زده میشود و به یاد داشته باشید که این رویداد باید قبل از آن ست شده باشد.$('#username').on('init', function(e, editable) { alert('initialized ' + editable.options.name); }); $('#username').editable(); |
save | موقعی
که مقدار جدید، با موفقیت تایید میشود. دو پارامتر event و params را باز
میگرداند که params شامل دو خصوصیت newValue و response است که به ترتیب
مقدار جدید و اطلاعات برگشت داده شده از درخواست آژاکس است.$('#username').on('save', function(e, params) { alert('Saved value: ' + params.newValue); }); |
shown | موقعیکه ویرایشگر نمایش مییابد و فرم با موفقیت رندر شده است. برای اشیایی چون select باید صبر کنید تا مقادیر آنها بارگذاری شوند.$('#username').on('shown', function(e, editable) { editable.input.$input.val('overwriting value of input..'); }); |
حل مشکل این ابزار در کندو
موقعیکه من این ابزار را بر روی treeview قرار دادم، به این مشکل برخوردم که اطراف پنجره باز شده، توسط حاشیههای treeview محدود شده است و مطابق شکل زیر قسمتهایی از آن دیده نمیشود. به همین علت cssهای کندو را به اندازه یک خط ویرایش کردم.
برای حل این مشکل فایل kendo.common-xxx را باز کنید. xxx بر اساس قالبی که برای کندو انتخاب کردهاید، میتواند متفاوت باشد. در مثالهای کندو عموما این xxx به نام default شناخته میشود یا برای مثال من، bootstrap بود.
بعد از اینکه باز کردید، به دنبال چنین استایلی بگردید:
div.k-treeview{ border-width: 0px; background: transparent none repeat scroll 0px center; overflow: auto; white-space: nowrap; }
overflow: auto;
نکته بعدی اینکه وقتی ویرایشگر در حالت popup قرار میگیرد، مقدار خاصیت title نمایش مییابد که عموما با مضامینی چون "کلمه جدید را وارد نمایید" و ... پر میشود که به طور پیش فرض سمت چپ قرار گرفته است. کد زیر را در صفحه وارد کنید تا متن در سمت راست قرار بگیرد:
.popover-title { text-align: right; }
- Blazor Server، که در آن یک اتصال SignalR، بین مرورگر کاربر و سرور، برقرار شده و سرور حالات مختلف این جلسهی کاری را مدیریت میکند. آغاز این حالت، بسیار سریع است؛ اما وجود اتصال دائم SignalR در آن ضروری است. نیاز به وجود این اتصال دائم، با تعداد بالای کاربر میتواند کارآیی سرور را تحت تاثیر قرار دهد.
- Blazor WASM: در این حالت کل برنامهی Blazor، درون مرورگر کاربر اجرا میشود و برای اینکار الزاما نیازی به سرور ندارد؛ اما آغاز اولیهی آن به علت نیاز به بارگذاری کل برنامه درون مرورگر کاربر، اندکی کند است. اتصال این روش با سرور، از طریق روشهای متداول کار با Web API صورت میگیرد و نیازی به اتصال دائم SignalR را ندارد.
دات نت 8، دو تغییر اساسی را در اینجا ارائه میدهد:
- در اینجا حالت جدیدی به نام SSR یا Static Server Rendering ارائه شدهاست (به آن Server-side rendering هم میگویند). در این حالت نه WASM ای درون مرورگر کاربر اجرا میشود و نه اتصال دائم SignalR ای برای کار با آن نیاز است! در این حالت برنامه تقریبا همانند یک MVC Razor application سنتی کار میکند؛ یعنی سرور، کار رندر نهایی HTML قابل ارائهی به کاربر را انجام داده و آنرا به سمت مرورگر، برای نمایش ارسال میکند و همچنین سرور، هیچ حالتی را هم از برنامه ذخیره نمیکند و بهعلاوه، کلاینت نیز نیازی به دریافت کل برنامه را در ابتدای کار ندارد (هم آغاز و نمایش سریعی را دارد و هم نیاز به منابع کمتری را در سمت سرور برای اجرا دارد).
- تغییر مهم دیگری که در دات نت 8 صورت گرفته، امکان ترکیب کردن حالتهای مختلف رندر صفحات، در برنامههای Blazor است. یعنی میتوان یک صفحهی SSR را داشت که تنها قسمت کوچکی از آن بر اساس معماری Blazor Server کار کند (قسمتهای اصطلاحا interactive یا تعاملی آن). یا حتی در یک برنامه، امکان ترکیب Blazor Server و Blazor WASM نیز وجود دارد.
اینها عناوین موارد جدیدی هستند که در این سری به تفصیل بررسی خواهیم کرد.
تاریخچهی نمایش صفحات وب در مرورگرها
در ابتدای ارائهی وب، سرورها، ابتدا درخواستی را از طرف مرورگر کلاینت دریافت میکردند و در پاسخ به آن، HTML ای را تولید و بازگشت میدادند. حاصل آن، نمایش یک صفحهی استاتیک non-interactive بود (غیرتعاملی). علت تاکید بر روی واژهی interactive (تعاملی)، بکارگیری گستردهی آن در نگارش جدید Blazor است؛ تا حدی که ایجاد قالبهای جدید آغازین برنامههای آن، با تنظیم این گزینه همراه است. برای مشاهدهی آن، پس از نصب SDK جدید دات نت، دستور dotnet new blazor --help را صادر کنید.
سپس JavaScript از راه رسید و هدف آن، افزودن interactivity به صفحات سمت کاربر بود تا بتوان بر اساس تعاملات و ورودیهای کاربر، تغییراتی را بر روی محتوای صفحه اعمال کرد. در ادامه JavaScript این امکان را یافت تا بتواند درخواستهایی را به سمت سرور ارسال کند و بر اساس خروجی دریافتی، قسمتهایی از صفحهی جاری استاتیک را به صورت پویا تغییر دهد.
در ادامه با بالارفتن توانمندیهای سختافزاری و همچنین توسعهی کتابخانههای جاوااسکریپتی، به برنامههای تک صفحهای کاملا پویا و interactive رسیدیم که به آنها SPA گفته میشود (Single-page applications)؛ از این دست کتابخانهها میتوان به Backbone اشاره کرد و پس از آن به React و Angular. برنامههای Blazor نیز اخیرا به این جمع اضافه شدهاند.
اما ... اخیرا توسعه دهندهها به این نتیجه رسیدهاند که SPAها برای تمام امور، مناسب و یا حتی الزامی نیستند. گاهی از اوقات ما فقط نیاز داریم تا محتوایی را خیلی سریع و بهینه تولید و بازگشت دهیم؛ مانند نمایش لیست اخبار، به هزاران دنبال کننده، با حداقل مصرف منابع و در همین حال نیاز به interactivity در بعضی از قسمتهای خاص نیز احساس میشود. این رویهای است که در تعدادی از فریمورکهای جدید و مدرن جاوااسکریپتی مانند Astro در پیش گرفته شدهاست؛ در آنها ترکیبی از رندر سمت سرور، به همراه interactivity سمت کاربر، مشاهده میشود. برای مثال این امکان را فراهم میکنند تا محتوای قسمتی از صفحه را در سمت سرور تهیه و رندر کنید، یا قسمتی از صفحه (یک کامپوننت خاص)، به صورت interactive فعال شود. ترکیب این دو مورد، دقیقا هدف اصلی Blazor، در دات نت 8 است. برای مثال فرض کنید میخواهید برنامه و سایتی را طراحی کنید که چند صفحهی آغازین آن، بدون هیچگونه تعاملی با کاربر هستند و باید سریع و SEO friendly باشند. همچنین تعدادی از صفحات آن هم قرار است فقط یک سری محتوای ثابت را نمایش دهند، اما در قسمتهای خاصی از آن نیاز به تعامل با کاربر است.
معرفی Blazor یکپارچه در دات نت 8
مهمترین تغییر Blazor در دات نت 8، یکپارچه شدن حالتهای مختلف رندر آن در سمت سرور است. تغییرات زیاد رخ دادهاند تا امکان داشتن Server-side rendering یا SSR به همراه قابلیت فعال سازی interactivity به ازای هر کامپوننت دلخواه که به آن حالتهای رندر (Render modes) گفته میشود، میسر شوند. در اساس، این روش جدید، همان Blazor Server بهبود یافتهاست که حالت SSR، حالت پیشفرض آن است. در کنار آن قابلیتهای راهبری (navigation)، نیز بهبود یافتهاند تا برنامههای SSR همانند برنامههای SPA بهنظر برسند.
در دات نت 8، ASP.NET Core و Blazor نیز کاملا یکپارچه شدهاند. در این حالت برنامههای Blazor Server میتوانند همانند برنامههای MVC Razor Pages متداول، با کمک قابلیت SSR، صفحات غیر interactive ای را رندر کنند؛ البته به کمک کامپوننتهای Razor. مزیت آن نسبت به MVC Razor Pages این است که اکنون میتوانید هر کامپوننت مجزایی از صفحه را نیز کاملا interactive کنید.
در نگارشهای قبلی Blazor، برنامههای Blazor Server حتی برای شروع کار نیاز به یک صفحهی Razor Pages آغازین داشتند، اما دیگر نیازی به این مورد با دات نت 8 نیست؛ چون ASP.NET Core 8x میتواند کامپوننتهای Razor را نیز به صورت HTML خالص بازگشت دهد و یا Minimal API آن به همراه خروجی new RazorComponentResult نیز شدهاست. در حالت SSR، حتی سیستم مسیریابی ASP.NET Core نیز با Blazor یکی شدهاست.
البته این تغییرات، حالتهای خالص Blazor WebAssembly و یا MAUI Blazor Hybrid را تحت تاثیر قرار نمیدهند؛ اما بدیهی است تمام آنها از سایر قابلیتهای جدید اضافه شده نیز بهرهمند هستند.
معرفی حالتهای مختلف رندر Blazor در دات نت 8
یک برنامهی جدید 8x Blazor، در اساس بر روی سرور رندر میشود (همان SSR). اما همانطور که عنوان شد، این SSR ارائه شدهی توسط Blazor، یک قابلیت مهم را نسبت به MVC Razor pages دارد و آن هم امکان فعالسازی interactivity، به ازای کامپوننتها و قسمتهای کوچکی از صفحه است که واقعا نیاز است تعاملی باشند. فعالسازی آن هم بسیار ساده، یکدست و یکپارچه است:
@* For being rendered on the server *@ <Counter @rendermode="@InteractiveServer" /> @* For running in WebAssembly *@ <Counter @rendermode="@InteractiveWebAssembly" />
این تعاریف حالت رندر را توسط دایرکتیوها نیز میتوان به ازای هر کامپوننت مجزا، مشخص کرد (یکی از این دو حالت باید بکار گرفته شود):
@rendermode InteractiveServer @rendermode InteractiveWebAssembly
امکان اعمال این ویژگیها به مسیریاب برنامه نیز وجود دارد که در این حالت کل برنامه را interactive میکند. اما در حالت پیشفرض، برنامهای که ایجاد میشود فاقد تنظیمات تعاملی در ریشهی اصلی آن است.
معرفی حالت رندر خودکار در Blazor 8x
یکی دیگر از حالتهای رندر معرفی شدهی در Blazor 8x، حالت Auto است:
<Counter @rendermode="@InteractiveAuto" />
معرفی حالت رندر Streaming در Blazor 8x
در بار اول بارگذاری صفحات، ممکن است دریافت اطلاعات مرتبط با آن کمی کند و با وقفه باشند. در این حالت برای اینکه برنامههای SSR یک صفحهی خالی را نمایش ندهند، میتوان در ابتدا با استفاده از حالت رندر جدید StreamRendering، حداقل قالب صفحه را نمایش داد و سپس اصل اطلاعات را:
@attribute [StreamRendering(prerender: true)]
جزئیات بیشتر نحوهی کار با این حالات را در قسمتهای بعدی بررسی خواهیم کرد.
نتیجه گیری:
روشهای جدید رندر ارائه شدهی در Blazor 8x، برای موارد زیر مفید هستند:
- زمانیکه قسمت عمدهای از برنامهی شما بر روی سرور اجرا میشود.
- زمانیکه خروجی اصلی برنامهی شما بیشتر حاوی محتواهای ثابت است؛ مانند CMSها.
- زمانیکه میخواهید صفحات شما قابل ایندکس شدن توسط موتورهای جستجو باشند و مباحث SEO برای شما مهم است.
- زمانیکه نیاز به مقدار کمی امکانات تعاملی دارید و فقط قسمتهای کوچکی از صفحه قرار است تعاملی باشند. برای مثال فقط قرار است قسمت کوچکی از یک صفحهی نمایش مقالهای از یک بلاگ، به همراه امکان رای دادن به آن مطلب (تنها قسمت «تعاملی» صفحه) باشد.
- و یا زمانیکه میخواهید MVC Razor Pages را با یک فناوری جدید که امکانات بیشتری را در اختیار شما قرار میدهد، جایگزین کنید.
چند مثال:
- پیشتر در این سایت از کلمهی نامفهوم و غریبی به نام «برچسب» استفاده شده بود. عین عنوان را در قسمت برچسب مطلب کپی میکردند (حین ارسال لینک). از زمانیکه به «گروه» تغییر نام یافت، مشکل حل شد.
- پیام میدن، جستجوی سایت صفحه بندی نداره. عنوان میکنم اون مستطیل آبی برجسته مثل تمام قسمتهای دیگر سایت یک دکمه است. قابل کلیک است. در کنارش علامت مراجعه به صفحهی 2 هم قرار داده شد. (تصور کاربری رابطهای تخت و مترو اینجا کلا زیر سؤال هست)
و ...
قدم اول و مهم بحث on time binding هست:
<h1>{{title}}</h1>
اما این روش درست نیست! چرا؟
<h1>{{::title}}</h1>
به زودی مقالههای بیشتری در خصوص Performance در AngularJS خواهم نوشت. امیدوارم لذت برده باشید.
ایجاد یک request bin جدید
برای مشاهدهی محتوای ارسالی توسط postman بدون برپایی وب سرویس خاصی، از سایت requestbin استفاده خواهیم کرد. در اینجا با کلیک بر روی دکمهی create a request bin، یک آدرس موقتی را مانند http://requestbin.fullcontact.com/1gdduy21 برای شما تولید میکند. میتوان انواع و اقسام درخواستها را به این آدرس ارسال کرد و سپس با ریفرش کردن آن در مرورگر، دقیقا محتوای ارسالی به سمت سرور را بررسی نمود.
برای مثال اگر همین آدرس را در postman وارد کرده و سپس بر روی دکمهی send آن کلیک کنیم، پس از ریفرش صفحه، چنین تصویری حاصل میشود:
Encoding کوئری استرینگها در postman
مثال زیر را درنظر بگیرید:
در اینجا با استفاده از گرید ساخت Query Params، دو کوئری استرینگ جدید را ایجاد کردهایم که در دومی، مقدار وارد شده، دارای فاصله است. اگر این درخواست را ارسال کنیم، مشاهده خواهیم کرد که مقدار ارسالی توسط آن encoded نیست:
برای رفع این مشکل میتوان بر روی تکستباکس ورود مقدار یک کلید، کلیک راست کرد و از منوی باز شده، گزینهی encode URI component را انتخاب نمود:
البته برای اینکه این گزینه درست عمل کند، نیاز است یکبار کل متن را انتخاب کرد و سپس بر روی آن کلیک راست نمود، تا انتخاب گزینهی encode URI component، به درستی اعمال شود:
امکان تعریف متغیرها در آدرسهای HTTP
Postman از امکان تعریف path variables پشتیبانی میکند. برای مثال مسیر api.example.com/users/5/contracts/2 را در نظر بگیرید که میتواند سبب نمایش اطلاعات قرارداد دوم کاربر پنجم شود. برای پویا کردن یک چنین آدرسی در postman میتوان از مسیری مانند api.example.com/users/:userid/contracts/:contractid استفاده کرد:
اگر به تصویر فوق دقت کنیم، متغیرهای شروع شدهی با :، در قسمت path variables ذکر شدهاند و به سادگی قابل تغییر و مقدار دهی میباشند (در گریدی همانند گرید کوئری استرینگها) که برای آزمایش دستی، بسیار مفید هستند.
امکان ارسال فایلها به سمت سرور
زمانیکه برای مثال، نوع درخواست به Post تغییر میکند، امکان تنظیم بدنهی آن نیز مسیر میشود. در این حالت اگر گزینهی form-data را انتخاب کنیم، با نزدیک کردن اشارهگر ماوس به هر ردیف جدید (پیش از ورود کلید آن ردیف)، میتوان نوع Text و یا File را انتخاب کرد:
در اینجا پس از انتخاب گزینهی File، میتوان علاوه بر تعیین کلید این ردیف، با استفاده از دکمهی select files، چندین فایل را نیز برای ارسال انتخاب کرد:
روش انتقال درخواستهای پیچیده به Postman
تا اینجا روش ساخت درخواستهای متداولی را بررسی کردیم که آنچنان پیچیده، طولانی و به همراه جزئیات زیادی (مانند کوکیها،انواع و اقسام هدرها و ...) نبودند. فرض کنید میخواهید درخواست ارسال یک امتیاز جدید را به مطلبی در سایت جاری، توسط Postman شبیه سازی کنیم. برای اینکار، توسط مرورگر کروم به سایت وارد شده و پس از لاگین و تنظیم خودکار کوکیهای اعتبارسنجی، برگهی developer tools مرورگر را باز کرده و در آن، قسمت network را انتخاب کنید:
در اینجا گزینهی preserve log را نیز انتخاب کنید تا پس از ارسال درخواستی، سابقهی عملیات، پاک نشود. سپس به صورت معمولی به مطلبی امتیاز دهید. اکنون بر روی مدخل درخواست آن کلیک راست کرده و از منوی ظاهر شده، گزینهی Copy->Copy as cURL را انتخاب کنید تا این عملیات و تمام جزئیات مرتبط با آنرا تبدیل به یک دستور cURL کرده و به حافظه کپی کند.
سپس در postman، از منوی بالای صفحه، سمت چپ آن، گزینهی Import را انتخاب کنید:
در ادامه این دستور کپی شدهی در حافظه را در قسمت Paste Raw Text، قرار دهید و بر روی دکمهی import کلیک کنید:
در این حالت postman این دستور را پردازش کرده و فیلدهای ساخت یک درخواست را به صورت خودکار پر میکند.
یک نکتهی مهم: در حالت انتخاب Copy->Copy as cURL در مرورگر کروم، دو گزینهی cmd و bash موجود هستند. حالت bash را باید انتخاب کنید تا توسط postman به دسترسی parse شود. حالت cmd یک چنین خروجی مشکل داری را در postman تولید میکند که قابل ارسال به سمت سرور نیست:
اما جزئیات حالت bash آن، به درستی parse شدهاست و قابلیت send مجدد را دارد:
کار با کوکیها در Postman
کوکیها نیز در اصل به صورت یک هدر HTTP به صورت خودکار توسط مرورگرها به سمت سرور ارسال میشوند؛ اما Postman روش سادهتری را برای تعریف و کار با آنها ارائه میدهد (و ترجیح میدهد که بین کوکیها و هدرها تفاوت قائل شود؛ هم در زمان ارسال و هم در زمان نمایش response که در کنار قسمت هدرهای دریافتی از سرور، لیست کوکیهای دریافتی نیز به صورت مجزایی نمایش داده میشوند).
با کلیک بر روی لینک Cookies، در ذیل قسمتی که آدرس یک درخواست تنظیم میشود، قسمت مدیریت کوکیها نیز ظاهر خواهد شد که با انتخاب هر نامی در اینجا، میتوان مقدار آنرا ویرایش و یا حتی حذف کرد. در این لیست، تمام کوکیهایی را که تاکنون تنظیم کرده باشید، میتوانید مشاهده کنید (و مختص به برگهی خاصی نیست) و همانند قسمت مدیریت کوکیهای یک مرورگر رفتار میکند.
روش کار با آن نیز به این صورت است: ابتدا باید یک دومین را اضافه کنید. سپس ذیل دومین اضافه شده، دکمهی Add cookie ظاهر میشود و به هر تعدادی که لازم باشد، میتوان برای آن دومین کوکی تعریف کرد:
پس از تعریف کوکیها، در حین کلیک بر روی دکمهی send، کوکیهای متعلق به دومین وارد شدهی در قسمت آدرس درخواست، از قسمت مدیریت کوکیها به صورت خودکار دریافت شده و به سمت سرور ارسال خواهند شد.
به اشتراک گذاری سابقهی درخواستها
در قسمت اول مشاهده کردیم که برای ذخیره سازی درخواستها، باید آنها را در مجموعههای Postman، ذخیره و ساماندهی کرد. برای گرفتن خروجی از این مجموعهها و به اشتراک گذاشتن آنها، اگر اشارهگر ماوس را بر روی نام یک مجموعه حرکت دهیم، یک دکمهی جدید با برچسب ... ظاهر میشود:
با کلیک بر روی آن، یکی از گزینههای آن، export نام دارد که جزئیات تمام درخواستهای یک مجموعه را به صورت یک فایل JSON ذخیره میکند. برای نمونه فایل JSON خروجی دو قسمت قبل این سری را میتوانید از اینجا دریافت کنید: httpbin.postman_collection.json
پس از تولید این فایل JSON، برای بازیابی آن میتوان از دکمهی Import که در منوی سمت چپ، بالای postman قرار دارد، استفاده کرد که نمونهای از آنرا برای Import جزئیات درخواستهای cURL پیشتر مشاهده کردید. در اینجا، همان اولین گزینهی دیالوگ Import که Import file نام دارد، دقیقا برای همین منظور تدارک دیده شدهاست.