مطالب
Symbols در ES 6
در مطلب Iterators به بررسی حلقه‌های for of پرداختیم. اما سؤال مهم اینجا است که for of چگونه یک iterator را پیدا می‌کند و چه چیزی سبب می‌شود تا بتواند این پیمایش را انجام دهد؟ پاسخ به این سؤال نیاز به آشنایی با مفهوم جدیدی در ES 6 به نام Symbols دارد.
Symbol یک primitive data type جدید در ES 6 است؛ دقیقا مانند اعداد، Boolean، رشته‌ها و امثال آن‌ها. دو نکته‌ی مهم در مورد Symbols وجود دارد:
الف) منحصربفرد و immutable (غیرقابل تغییر) هستند.
ب) می‌توان از آن‌ها به عنوان کلیدهایی جهت افزودن خواص جدید به اشیاء استفاده کرد.

ایجاد یک Symbol باید بدون استفاده از کلمه‌ی new انجام شود (چون یک primitive data type است):
 let s = Symbol();
همچنین در اینجا یک توضیح را نیز می‌توان ذکر کرد:
 let s1 = Symbol("some description");
سمبل ایجاد شده، منحصربفرد بوده و غیرقابل تغییر است. همین منحصربفرد بودن آن سبب شده‌است که در لایه‌های زیرین ES 6 از آن برای ساخت کلیدهای خواص اشیاء استفاده شود:
let firstName = Symbol();
 
let person = {
    lastName: "Vahid",
    [firstName]: "N",
};
 
// person.lastName = "Vahid"
// person[firstName] = "N"
در این مثال ابتدا یک سمبل جدید ایجاد شده و سپس از این سمبل به عنوان کلیدی منحصربفرد، جهت تعریف یک خاصیت جدید کمک گرفته شده‌است.  
در ES 5 (نگارش فعلی جاوا اسکریپت)، کتابخانه‌های مختلف از time stamp و یا اعداد اتفاقی برای شبیه سازی چنین قابلیتی استفاده می‌کنند اما در ES 6 یک راه حل استاندارد به نام Symbols برای این مساله ارائه شده‌است.

چند نکته
- زمانیکه خاصیتی با کلیدی از نوع Symbol تعریف می‌شود، دیگر در حلقه‌های for in قدیمی ظاهر نخواهد شد.
 let names = [];
for(var p in person) {
   names.push(p);
}
- همچنین این خواص سمبلی، توسط Object.getOwnPropertyNames نیز قابل دسترسی و یافت شدن نیستند. به عبارتی با امکانات ES 5 نمی‌توان آن‌‌ها را مشاهده کرد.


سؤال: ES 6 چگونه از Symbols جهت تعریف Iterators استفاده می‌کند؟

مطابق استاندارد ES 6 اگر متد خاصی با نام iterator@@ در شیءایی ظاهر شود، این شیء قابل پیمایش بوده و به عنوان منبع حلقه‌ی for of قابل استفاده‌است.
خوب، اکنون چگونه می‌توان بررسی کرد که آیا شیءایی دارای متد ویژه‌ی iterator@@ است؟ برای این منظور باید بررسی کرد که آیا این شیء دارای عضو Symbol.iterator هست یا خیر؟ خاصیت iterator متصل به متد Symbol، یکی از سمبل‌های پیش فرض ES 6 است.
برای مثال آرایه‌ی ذیل را درنظر بگیرید:
 var numbers = [1, 2, 3];
برای اینکه بررسی کنیم آیا قابل پیمایش هست یا خیر، می‌توان نوشت:
 numbers[Symbol.iterator];


همانطور که در تصویر مشاهده می‌کنید، آرایه و یا رشته‌ی تعریف شده، دارای Iterator هستند؛ اما عدد تعریف شده، خیر.

و یا اگر بخواهیم همان مثال while دار مطلب بررسی Iterators را با Symbol.iterator بازسازی کنیم، به مثال زیر خواهیم رسید:
 var numbersIterator = numbers[Symbol.iterator]();
numbersIterator.next();
// Result: Object {value: 1, done: false}
numbersIterator.next();
// Result: Object {value: 2, done: false}
numbersIterator.next();
// Result: Object {value: 3, done: false}
numbersIterator.next();
// Result: Object {value: undefined, done: true}
کاری که در اینجا انجام شده، دقیقا عملیاتی است که توسط حلقه‌ی for of در پشت صحنه انجام می‌شود. ابتدا بررسی می‌کند که آیا خاصیت Symbol.iterator در دسترس است یا خیر؟ اگر بله، متد next آن‌را تا زمان true شدن خاصیت done بازگشتی، فراخوانی می‌کند.


ایجاد یک Iterator سفارشی با استفاده از Symbol.iterator

در این مثال قصد داریم یک پیمایشگر سفارشی را بر روی یک رشته‌ی دریافتی، ایجاد کنیم. ابتدا ایجاد سازنده‌ی شیء:
 function Words(str) {
   this._str = str;
}
و سپس بدنه‌ی Iterator:
Words.prototype[Symbol.iterator] = function() {
  var re = /\S+/g;
  var str = this._str;
return {
    next: function() {
      var match = re.exec(str);
      if (match) {
        return {value: match[0], done: false};
      }
      return {value: undefined, done: true};
    }
  }
};
در اینجا شیءایی بازگشت داده می‌شود که دارای متد next است و هر بار {value: nextWordInTheString, done: false} را بازگشت می‌دهد تا دیگر کلمه‌‌ای در رشته باقی نماند. نمونه‌ای از نحوه‌ی استفاده‌ی از آن نیز به صورت زیر است:
 var helloWorld = new Words("Hello world");
for (var word of helloWorld) {
   console.log(word);
}
// Result: "Hello"
// Result: "world"
مطالب
Iterators در ES 6
یکی از اهداف ES 6، استاندارد سازی کار با Iterators و Iterables است. فرض کنید شیءایی را داریم که مجموعه‌ای از عناصر را در بر دارد. این مجموعه می‌تواند آرایه‌ای از عناصر باشد و یا set و map اضافه شده به ES 6 و یا حتی اشیایی که در زمان اجرا ایجاد می‌شوند. اگر این مجموعه Iterable باشد، حرکت بر روی آن یک Iterator را تولید می‌کند که امکان حرکت در این مجموعه را آیتم به آیتم میسر خواهد کرد:
 


هر Iterator شیءایی است که دارای متد next می‌باشد. هر بار که این متد فراخوانی می‌شود، عضو بعدی مجموعه، بازگشت داده خواهد شد. خروجی هر مرحله، درون یک شیء با دو خاصیت value و done قرار داده می‌شود. value‌، مقدار مرحله‌ی بعد است و done مشخص می‌کند که آیا به پایان مجموعه رسیده‌ایم یا خیر (بنابراین در اینجا تعداد اعضای Iterator مشخص نیست).


مثالی از پیمایش یک آرایه با چندین روش مختلف

در مثال زیر، آرایه‌ای از اعداد را داریم که نیاز است جمع اعضای آن محاسبه شود:
 let sum = 0;
let numbers = [1,2,3,4];
این‌کار را می‌توان با استفاده از یک حلقه‌ی for متداول انجام داد. از آنجائیکه آرایه دارای خاصیت length است، می‌توان از آن جهت تعیین حد بالایی آرایه استفاده کرد:
 // for loop
sum = 0;
for(let i =0; i < numbers.length; i++){
     sum += numbers[i];
}
//sum = 10
روش دوم انجام این‌کار، استفاده از حلقه‌ی for in است. این حلقه هربار ایندکس بعدی قابل استفاده‌ی آرایه را بازگشت می‌دهد که از آن می‌توان جهت دسترسی به اعضای آرایه استفاده کرد:
 // for in
sum = 0;
for(let i in numbers) {
    sum += numbers[i];
}
//sum = 10
روش دیگر انجام این‌کار با استفاده از Iterators است:
 // iterator
sum = 0;

let iterator = number.values();
let next = iterator.next();
while(!next.done){
    sum += next.value;
    next = iterator.next();
}
//sum = 10
برای کار با Iterators، ابتدا باید یک شیء Iterator را از مجموعه‌ایی که در اختیار داریم، تولید کنیم. برای مثال آرایه‌ها دارای متدی به نام values هستند که با فراخوانی ()number.values، سبب تولید یک Iterator می‌شوند. این Iterator امکان حرکت بین مقادیر آرایه را در اینجا میسر می‌کند.
مرحله‌ی بعدی، فراخوانی متد next این Iterator است. این عملیات باید در طی یک حلقه، تا پایان کار Iterator انجام شود. همانطور که در ابتدای بحث نیز عنوان شد، خروجی متد next یک شیء است که دارای خواص value و done می‌باشد. اگر done مساوی true شد، یعنی به پایان کار پیمایش رسیده‌ایم.
البته هدف از این مثال، صرفا نمایش سطح پایین کار با Iterators بود. در عمل از حلقه‌ی جدیدی به نام for of برای انجام این پیمایش استفاده می‌شود.


معرفی حلقه‌ی for of

جاوا اسکریپت سال‌ها است که دارای حلقه‌ی for in می‌باشد و نمونه‌ای از کاربرد آن‌را در مثال قبل مشاهده کردید. اگر این حلقه بر روی آرایه‌ها فراخوانی شود، هربار ایندکس پیمایش شده را بازگشت می‌دهد و اگر بر روی یک شیء فراخوانی شود، خواص آن شیء را بازگشت می‌دهد:
 var person = { first: "Vahid", last "N" };
for(let i in person) {
   console.log(person[i]);
}
در این مثال، i بار اول به first و بار دوم به خاصیت last اشاره می‌کند.
چون این حلقه صرفا ایندکس‌ها و کلیدها را بازگشت می‌دهد، جهت کار با Iterators که نیاز است به مقادیر اعضاء دسترسی پیدا کنیم، مناسب نیست. به همین جهت در ES 6، حلقه‌ی جدیدی به نام for of برای کار با Iterators معرفی شده‌است:
 let numbers = [1,2,3,4];
for(let i of numbers) {
   console.log(i);
}
این حلقه برخلاف for in، بر روی values، کار پیمایش را انجام می‌دهد. همچنین به صورت خودکار در پشت صحنه متد next یک Iterator را فراخوانی می‌کند و خاصیت done آن‌را بررسی کرده و زمانیکه این خاصیت مساوی true شد، حلقه را خاتمه می‌دهد. برای نمونه مثال سطح پایین while دار ابتدای بحث را به نحو ساده‌تری با حلقه‌ی for of ذیل می‌توان جایگزین کرد:
let sum = 0;
let numbers = [1,2,3,4];

for(let n of numbers){
   sum += n;
}

for of یکی از روش‌های پیمایش Iterators است. پارامترهای rest و همچنین Array.from نیز چنین قابلیتی را فراهم می‌کنند.

امکان پیاده سازی Iterators سفارشی نیز وجود دارد که پیشنیاز آن، درک مبحث جدید Symbols است که به صورت جداگانه‌ای بررسی خواهد شد.
مطالب
شروع کار با Apache Cordova در ویژوال استودیو #1
Apache Cordova  یک فریمورک سورس باز برای ساخت اپلیکیشن‌های چند سکویی موبایل (cross platform) با استفاده از Html5 می‌باشد.
طی چند مقاله، با استفاده کردن از این فریمورک در VS آشنا خوهیم شد.
هدف خالقان Cordova یافتن یک راه ساده برای تولید اپلیکیشن‌های چند سکویی موبایل بود که برای رسیدن به این هدف تصمیم گرفتند از تکنولوژی‌های بومی (native) و تکنولوژی‌های وب استفاده کنند. به این نوع از اپلیکیشن‌های موبایل، Hybrid Application می‌گویند.
  Cordova دارای  قابلیت‌های بومی بالایی است و مهم‌تر اینکه به طور طبیعی توسط مرورگرها پشتیبانی می‌شود. بعد از تولد Corodva، این فریمورک تبدیل شده است به  بهترین روش تولید اپلیکیشن‌هایی که بر روی چند نوع پلتفرم کار می‌کنند.
پیشتر محدودیتی که وجود داشت شامل این بود که اپلیکیشن‌های موبایل، به چیزهایی بیشتر از HTML و مرورگرهای وب، نیاز داشتند. برخی از این نیاز‌ها عبارتند از ارتباط متقابل وب اپلیکیشن‌ها با دوربین یا لیست شماره‌های تماس گوشی که برطرف کردن آن هم به راحتی امکان پذیر نبود.
Cordova برای مقابله با این محدودیت، مجموعه‌ای از رابط‌های برنامه کاربردی را برای توسعه قابلیت‌های بومی device، مانند لیست مخاطبین، دوربین، تشخیص دهنده‌ی تغییر جهت گوشی (accelerometer) و مانند این موارد، در نظر گرفته است.


Cordova شامل یک سری کامپوننت به شرح زیر است:
  1. سورس کدی برای هر Container و برنامه محلی برای هر یک از سکوهای موبایل که پشتیبانی می‌شوند. container، کد‌های Html5 را بر روی دستگاه (Device) رندر می‌کند. (در مطالب بعدی در مورد این مطلب توضیح خواهم داد)
  2. مجموعه‌ای از رابط‌های برنامه‌ی کاربردی که امکان دسترسی به قابلیت‌های بومی دستگاه را به برنامه‌ی وبی که درون آن در حال اجرا است، می‌دهند.
  3. مجموعه‌ای از ابزارها برای مدیریت فرآیند ایجاد پروژه، مدیریت پلاگین‌ها، ساخت (با استفاده از SDK‌های محلی) برنامه‌های محلی و تست برنامه بر روی دستگاه موبایل یا شبیه ساز  .
 
برای ساخت یک برنامه‌ی Cordova، در واقع شما یک وب اپلیکیشن می‌سازید و آن را داخل Container محلی، بسته بندی می‌کنید. سپس تست کرده و بعد از دیباگ می‌توانید اپلیکیشن را توزیع کنید.

فرآیند بسته بندی :

 داخل اپلیکیشن محلی، رابط کاربری اپلیکیشن شامل یک صفحه‌ی نمایش که خود آن چیزی نیست به غیر از یک Web View که از فضای نمایش دستگاه استفاده می‌کند. زمانی که برنامه آغاز به کار می‌کند، برنامه‌ی وب نوشته شده، درون این web view لود میشود و کنترل‌های موجود، برای تعامل کاربر با برنامه‌ی وب، در اختیار آن قرار می‌گیرند. مانند تعامل کاربر با محتوا، در برنامه‌ها ی تحت وب، لینک‌ها، کدهای نوشته شده‌ی js در فایل‌ها و یا حتی می‌تواند به اینترنت دسترسی داشته باشد و محتوا را از یک وب سرور تغذیه کند.

درباره Web Views
Web View جزء برنامه‌های بومی است که برای رندر کردن محتوای وب (به عنوان نمونه صفحه HTML) درون اپلیکیشن بومی یا صفحه نمایش استفاده می‌شود. در اصل Web View یک Wrapper برنامه نویسی شده قابل دسترس برای نمایش محتوای صفحات وب توکار است.
به عنوان مثال:
در اندروید با استفاده از WebView موجود در (Using andoid.webkit.WebView) , در iOS با UIWebView موجود در (Using System/Library/Framworks/UIKit.framewor)  به این هدف دست پیدا می‌کنند. وب اپلیکیشن ما درون این Container مانند سایر وب اپلیکیشن‌هایی است که هر روز با آنها سرو کار دارید و آنها را در مرورگر موبایل خود اجرا می‌کنید و می‌توانید بین صفحات Navigation داشته باشید. وب اپلیکیشن‌های معمول باید روی یک سرور هاست شوند. در برنامه نویسی چند سکویی با Cordova، این کار می‌تواند درون Cordova Application انجام گیرد.
شاید سؤالی در ذهن شما وجود داشته باشد که مرورگر معمولا به اپلیکیشن‌های موجود در دستگاه، سخت افزار و یا API‌های بومی دستگاه، دسترسی ندارد. برای مثال شاید بگویید که یک وب اپلیکیشن معمولا به لیست مخاطبین با دوربین دستگاه و ... دسترسی ندارد. 
جواب : در واقع امکان دسترسی به این قابلیت‌ها توسط اپلیکیشن بومی (native mobile application) ایجاد می‌شود.
Cordova مجموعه ای از API‌های جاوااسکریپت را به عنوان اهرم اجازه برای دسترسی  برنامه وب درون cordova container به قابلیت‌های بومی دستگاه، در اختیار توسعه دهندگان قرار داده است.

این API‌ها در دو بخش پیاده سازی می‌شوند:
1-کتابخانه‌ی جاوااسکریپت که اجازه‌ی استفاده از قابلیت‌های بومی را به وب اپلیکیشن می‌دهد و کد بومی مشابه در Container اجرا می‌شود که مربوط است به بخش بومی این API ها. در اصل یک کتابخانه‌ی جاوا اسکریپت وجود دارد، اما بخش بومی API‌ها وابسته به سکوی (platform) انتخاب شده پیاده سازی می‌شود.
اگر شما از API‌های موجود استفاده نکنید، می‌توانید آنها را از کتابخانه جاوااسکریپت و native container حذف کنید. این کار به صورت دستی شاید خوشایند نباشد ولی چون در Cordova 3.0 همه‌ی API ها از بیرون وارد می‌شوند، می‌توانید با استفاده از بحث مدیریت پلاگین آن، پلاگین‌ها را اضافه یا حذف کنید. در بخش‌های بعد با مثال‌هایی عملی  این مباحث را کار خواهیم کرد. 
 ادامه دارد.. 

مطالب
نحوه‌ی صحیح کار کردن با بوت استرپ
DOM در حالت عادی بسیار نامرتب است. همچنین با افزودن کلاس‌های CSS، کد HTML به مراتب نامرتب‌تر از قبل می‌شود. بوت استرپ نیز شامل تعداد زیادی از کلاس‌های CSS می‌باشد که برای انجام وظایف خاصی به HTML اضافه می‌شوند.

روش متداول استفاده از بوت‌استرپ 

Embedd کردن کلاس‌های CSS بوت‌استرپ به صورت مستقیم درون HTML

 اغلب فریم‌ورک‌ها، از لحاظ معنایی یا semantic، دارای مشکل هستند. اگر به سورس HTML صفحاتی که با این نوع از فریم‌ورک‌ها ساخته شده باشند نگاهی بیندازید با حجم زیادی از کلاس‌هایی مانند <"div class="row> و یا <"div class="col-sm> مواجه خواهید شد. نوشتن کد‌های HTML به این صورت از لحاظ معنایی اشتباه است. مثلاً اگر بنا به دلایلی سازندگان بوت استرپ تصمیم بگیرند نام کلاس‌های را در نسخه بعدی این فریم‌ورک تغییر دهند (مانند تغییر نام کلاس‌ها در نسخه‌ی 3 بوت استرپ)، و یا اگر در آینده بخواهید از یک فریم‌ورک دیگر در سایت‌تان استفاده کنید. باید این تغییرات را در تمام صفحات سایت‌تان اعمال کنید؛ در نتیجه اینکار زمان زیادی را از شما صرف میکند.

راه‌حل؟

استفاده از CSS preprocessors

بوت‌استرپ، از Less برای اینکار استفاده می‌کند. Less در واقع یک CSS preprocessor نوشته شده با جاوا اسکریپت است که قابلیت اجرا در مرورگر را دارد. Less امکانات زیادی، از قبیل استفاده از توابع، متغیرها، Mixins و ... را در اختیار شما قرار می‌دهد. در واقع هدف از Less، نگهداری آسان و قابلیت توسعه فایل‌های CSS می‌باشد. در این حالت شما کدهای CSS خود را درون فایل‌هایی با پسوند Less می‌نویسید. در این حالت بجای پیوست کردن کلاس‌های بوت‌استرپ در کد HTML، آن را درون استایل‌شیت پیوست خواهید کرد. همانطور که عنوان شد، بوت‌استرپ با Less نوشته شده است. فایل‌های Less بوت‌استرپ را می‌توانید از مخرن کد گیت‌هاب آن دانلود نمائید یا اینکه از طریق نیوگت می‌توانید آن را نصب کنید: 
PM> Install-Package Twitter.Bootstrap.Less
کار با Less خیلی ساده است. به عنوان مثال در کد زیر یک کلاس با نام loud داریم که استایل‌هایی را به آن اعمال کرده‌ایم. اکنون جهت استفاده مجدد از این استایل‌ها برای کلاسی دیگر، نیاز به نوشتن مجدد آن نیست. کافی همانند یک تابع در هر کلاسی آن را فراخوانی کنیم:
.loud {
  color: red;
}

// Make all H1 elements loud
h1 {
  .loud;
}

نکته: در Visual Studio 2012 Update 2 به بعد به صورت توکار از فایل‌های Less پشتیبانی می‌شود. (توسط پلاگین Web Essentials)

استفاده از Mixins

با استفاده از Mixins می‌توانیم عناصر داخل صفحات‌مان را به صورت Semantic تعریف نمائیم. به عنوان مثال می‌خواهیم با استفاده از سیستم گرید بوت‌استرپ، ساختاری مانند تصویر زیر را داشته باشیم:

در حالت معمول با استفاده از کلاس‌های CSS بوت‌استرپ می‌توانیم اینکار را انجام دهیم:
<div class="container">
    <div class="row">
        <div class="col-md-8">
            Content - Main
        </div>
        <div class="col-md-4">
            Content - Secondary
        </div>
    </div>
</div>
کد فوق را بهتر است به این صورت بنویسیم: 
<div class="wrapper">
    <div class="content-main">
         Content - Main
    </div>
    <div class="content-secondary">
        Content - Secondary
    </div>
</div>

در بوت استرپ از Less Mixins جهت اعمال استایل هایی مانند row و column می‌توانیم استفاده کنیم. به طور مثال برای اعمال استایل به کلاس‌های فوق می‌توانیم به این صورت عمل کنیم:
// Core variables and mixins
@import "variables.less";
@import "mixins.less";
.wrapper {
  .make-row();
}
.content-main {
  .make-lg-column(8);
}
.content-secondary {
  .make-lg-column(3);
  .make-lg-column-offset(1);
}
کد فوق بعد از کامپایل به کد زیر تبدیل خواهد شد:
.wrapper {
  margin-left: -15px;
  margin-right: -15px;
}
.content-main {
  position: relative;
  min-height: 1px;
  padding-left: 15px;
  padding-right: 15px;
}
@media (min-width: 1200px) {
  .content-main {
    float: left;
    width: 66.66666666666666%;
  }
}
.content-secondary {
  position: relative;
  min-height: 1px;
  padding-left: 15px;
  padding-right: 15px;
}
@media (min-width: 1200px) {
  .content-secondary {
    float: left;
    width: 25%;
  }
}
@media (min-width: 1200px) {
  .content-secondary {
    margin-left: 8.333333333333332%;
  }
}
همانطور که قبلاً عنوان شد ویژوال استودیو به راحتی توسط افزونه Web Essentials از فایل‌های Less پشتیبانی می‌کند. در نتیجه کامپایل فایل‌های Less داخل ویژوال استودیو توسط این افزونه به راحتی قابل انجام می‌باشد. 
یک قابلیت جالب دیگر در رابطه با فایل‌هایی Less، تولید نسخه‌های CSS عادی و فشرده نهایی توسط افزونه Web Essentials می‌باشد. به طور مثال شما می‌توانید نسخه minified شده را به Layout تان اضافه کنید. بعد از هربار تغییر در فایل Less این فایل نیز به روز خواهد شد:

 همچنین برای دیگر اجزای بوت‌استرپ نیز می‌توانید به این صورت عمل کنید:  
<!-- Before -->
<a href="#" class="btn danger large">Click me!</a>

<!-- After -->
<a href="#" class="annoying">Click me!</a>

a.annoying {
  .btn;
  .btn-danger;
  .btn-large;
}

خب، با استفاده از این حالت، کد‌های HTML به‌صورت مرتب‌تر، قابل‌انعطاف‌تر و همچنین از لحاظ معنایی(Semantic) استاندارد خواهند بود. بنابراین با آمدن یک فریم‌ورک جدید، به راحتی امکان سوئیچ‌کردن برای ما میسر و آسان‌تر از قبل خواهد شد. 

مطالب
استفاده از Flash Uploader در ASP.NET MVC
چندسال قبل یک کنترل آپلود فایل در برنامه‌های ASP.NET Web forms در سایت Code projects منتشر شد که من در چند پروژه از آن استفاده کردم.
در ادامه نحوه سازگار سازی این مجموعه را با ASP.NET MVC مرور خواهیم کرد:

الف) سورس‌های اصلی Flash کنترل ارسال فایل‌ها
اگر علاقمند به تغییر اطلاعاتی در فایل فلش نهایی هستید به پوشه OriginalFlashSource پروژه پیوست شده مراجعه کنید. در اینجا برای مثال یک سری از برچسب‌های آن فارسی شده‌اند و کامپایل مجدد.


ب) مزیت استفاده از Flash uploader
با استفاده از Flash uploader امکان انتخاب چندین فایل با هم وجود دارد. همچنین در صفحه دیالوگ انتخاب فایل‌ها دقیقا می‌توان پسوند فایل‌های مورد نظر را نیز تعیین کرد. این دو مورد در حالت ارسال معمولی فایل‌ها به سرور و استفاده از امکانات معمولی HTML وجود ندارند. به علاوه امکان نمایش درصد پیشرفت آپلود فایل‌ها و همچنین حذف کلی لیست و حذف یک آیتم از لیست را هم درنظر بگیرید.



ج) معادل کنترل Web forms را در ASP.NET MVC به شکل زیر می‌توان تهیه کرد:

@helper AddFlashUploader(
                string uploadUrl,
                string queryParameters,
                string flashUrl,
                int totalUploadSizeLimit = 0,
                int uploadFileSizeLimit = 0,
                string fileTypes = "",
                string fileTypeDescription = "",
                string onUploadComplete = "")
    {      
        onUploadComplete = string.IsNullOrEmpty(onUploadComplete) ? "" : "completeFunction=" + onUploadComplete;
        queryParameters = Server.UrlEncode(queryParameters);        
        fileTypes = string.IsNullOrEmpty(fileTypes) ? "" : "&fileTypes=" + Server.UrlEncode(fileTypes);
        fileTypeDescription = string.IsNullOrEmpty(fileTypeDescription) ? "" : "&fileTypeDescription=" + Server.UrlEncode(fileTypeDescription);
        var totalUploadSizeLimitData = totalUploadSizeLimit > 0 ? "&totalUploadSize=" + totalUploadSizeLimit : "";
        var uploadFileSizeLimitData = uploadFileSizeLimit > 0 ? "&fileSizeLimit=" + uploadFileSizeLimit : "";
        var flashVars = onUploadComplete + fileTypes + fileTypeDescription + totalUploadSizeLimitData + uploadFileSizeLimitData + "&uploadPage=" + uploadUrl + "?" + queryParameters;
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0"
        width="575" height="375" id="fileUpload" align="middle">
        <param name="allowScriptAccess" value="sameDomain" />
        <param name="movie" value="@flashUrl" />
        <param name="quality" value="high" />
        <param name="wmode" value="transparent">
        <param name=FlashVars value="@flashVars">
        <embed src="@flashUrl"
               FlashVars="@flashVars" 
               quality="high" 
               wmode="transparent" 
               width="575" 
               height="375" 
               name="fileUpload" 
               align="middle" 
               allowScriptAccess="sameDomain" 
               type="application/x-shockwave-flash" 
               pluginspage="http://www.macromedia.com/go/getflashplayer" />
    </object>                        
}
این اطلاعات در فایلی به نام FlashUploadHelper.cshtml در پوشه App_Code قرار خواهند گرفت.


د) نحوه استفاده از HTML helper فوق:

@{
    ViewBag.Title = "Index";
    var uploadUrl = Url.Action("Uploader", "Home");
    var flashUrl = Url.Content("~/Content/FlashUpload/FlashFileUpload.swf");
}
<h2>
    Flash Uploader</h2>
<div style="background: #E0EBEF;">
    @FlashUploadHelper.AddFlashUploader(
                uploadUrl: uploadUrl,
                queryParameters: "User=Vahid&Id=تست",
                flashUrl: flashUrl,
                fileTypeDescription: "Images",
                fileTypes: "*.gif; *.png; *.jpg; *.jpeg",
                uploadFileSizeLimit: 0,
                totalUploadSizeLimit: 0,
                onUploadComplete: "alert('انجام شد');")
</div>
با کدهای کنترلری معادل:
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Mvc;

namespace MvcFlashUpload.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Uploader(string User, string Id, IEnumerable<HttpPostedFileBase> FileData)
        {
            var queryParameter1 = User;
            var queryParameter2 = Id;
            // ...

            foreach (var file in FileData)
            {
                if (file.ContentLength > 0)
                {
                    var fileName = Path.GetFileName(file.FileName);
                    var path = Path.Combine(Server.MapPath("~/App_Data/Uploads"), fileName);
                    file.SaveAs(path);
                }
            }

            return Content(" ");
        }
    }
}

توضیحات:
در اینجا uploadUrl، مسیر اکشن متدی است که قرار است اطلاعات فایل‌ها را دریافت کند. queryParameters اختیاری است. اگر تعریف شود تعدادی کوئری استرینگ دلخواه را می‌تواند به متد Uploader ارسال کند. برای نمونه در اینجا User و Id ارسال شده‌اند یا هر نوع کوئری استرینگ دیگری که مدنظر است.
flashUrl مسیر فایل SWF را مشخص می‌کند. در اینجا فایل FlashFileUpload.swfدر پوشه Content/FlashUpload قرار گرفته است.
fileTypeDescription برچسبی است که نوع فایل‌های قابل انتخاب را به کاربر نمایش می‌دهد و fileTypes نوع‌های مجاز قابل ارسال را دقیقا مشخص می‌کند.
پارامترهای uploadFileSizeLimit و totalUploadSizeLimit در صورتیکه مساوی صفر وارد شوند، به معنای عدم محدودیت اندازه در فایل‌ها و جمع حجم ارسالی در هر بار است.
استفاده از پارامتر onUploadComplete اختیاری است. در اینجا می‌توان پس از پایان عملیات از طریق جاوا اسکریپت عملیاتی را انجام داد. برای مثال اگر خواستید کاربر را به صفحه خاصی هدایت کنید، window.locationرا مقدار دهی نمائید.
در متد Uploader کنترلر فوق، پارامترهای User و id اختیاری بوده و بر اساس queryParameters متد FlashUploadHelper.AddFlashUploader مشخص می‌شوند. اما نام FileData نباید تغییری کند؛ از این لحاظ که دقیقا همین نام در فایل فلش، مورد استفاده قرار گرفته است.
در اکشن متد دریافت فایل‌ها، لیستی از فایل‌های ارسالی به سرور دریافت شده و سپس بر این اساس می‌توان آن‌ها را در مکانی مشخص ذخیره نمود.


دریافت پروژه
MvcFlashUploader.zip
مطالب
تبدیل html به pdf با کیفیت بالا

کتابخانه iTextSharp دارای کلاسی است به نام HTMLWorker که کار تبدیل عناصر HTML را به عناصر متناظر خودش، انجام می‌دهد. این کلاس در حال حاضر منسوخ شده درنظر گرفته می‌شود (اینطور توسط نویسندگان آن اعلام شده) و دیگر توسعه نخواهد یافت. بنابراین اگر از HTMLWorker استفاده می‌کنید با یک کلاس قدیمی که دارای HTML Parser ایی بسیار بدوی است طرف هستید و در کل برای تبدیل محتوای HTML ایی با ساختار بسیار ساده بد نیست؛ اما انتظار زیادی از آن نداشته باشید.
جایگزین کلاس HTMLWorker در این کتابخانه در حال حاضر کتابخانه itextsharp.xmlworker است، که به صورت یک افزونه در کنار کتابخانه اصلی در حال توسعه می‌باشد. مشکل اصلی این کتابخانه، عدم پشتیبانی از UTF8 و راست به چپ است. بنابراین حداقل به درد کار ما نمی‌خورد.

راه حل بسیار بهتری برای موضوع اصلی بحث ما وجود دارد و آن هم استفاده از موتور WebKit (همان موتوری که برای مثال در Apple Safari استفاده می‌شود) برای HTML parsing و سپس تبدیل نتیجه نهایی به PDF است. پروژه‌ای که این مقصود را میسر کرده، wkhtmltopdf نام دارد.
توسط آن به کمک موتور WebKit، کار HTML Parsing انجام شده و سپس برای تبدیل عناصر نهایی به PDF از امکانات کتابخانه‌ای به نام QT استفاده می‌شود. کیفیت نهایی آن کپی مطابق اصل HTML قابل مشاهده در یک مرورگر است و با یونیکد و زبان فارسی هم مشکلی ندارد.

برای استفاده از این کتابخانه‌ی native در دات نت، شخصی پروژه‌ای را ایجاد کرده است به نام WkHtmlToXSharp که محصور کننده‌ی wkhtmltopdf می‌باشد. در ادامه به نحوه استفاده از آن خواهیم پرداخت:

الف) دریافت پروژه WkHtmlToXSharp
پروژه WkHtmlToXSharp را از آدرس زیر می‌توانید دریافت کنید.

 این پروژه به همراه فایل‌های کامپایل شده نهایی wkhtmltopdf نیز می‌باشد و حجمی حدود 40 مگ دارد. به علاوه فعلا نسخه 32 بیتی آن در دسترس است. بنابراین باید دقت داشت که نباید تنظیمات پروژه دات نت خود را بر روی Any CPU قرار دهیم، زیرا در این حالت برنامه شما در یک سیستم 64 بیتی بلافاصله کرش خواهد کرد. تنظیمات target platform پروژه دات نتی ما حتما باید بر روی X86 تنظیم شود.

ب) پس از دریافت این پروژه و افزودن ارجاعی به اسمبلی WkHtmlToXSharp.dll، استفاده از آن به نحو زیر می‌باشد:

using System.IO;
using WkHtmlToXSharp;
using System;

namespace Test2
{
    public class WkHtmlToXSharpTest
    {
        public static void ConvertHtmlStringToPdfTest()
        {
            using (var wk = new MultiplexingConverter())
            {
                wk.Begin += (s, e) => Console.WriteLine("Conversion begin, phase count: {0}", e.Value);
                wk.Error += (s, e) => Console.WriteLine(e.Value);
                wk.Warning += (s, e) => Console.WriteLine(e.Value);
                wk.PhaseChanged += (s, e) => Console.WriteLine("PhaseChanged: {0} - {1}", e.Value, e.Value2);
                wk.ProgressChanged += (s, e) => Console.WriteLine("ProgressChanged: {0} - {1}", e.Value, e.Value2);
                wk.Finished += (s, e) => Console.WriteLine("Finished: {0}", e.Value ? "success" : "failed!");

                wk.GlobalSettings.Margin.Top = "0cm";
                wk.GlobalSettings.Margin.Bottom = "0cm";
                wk.GlobalSettings.Margin.Left = "0cm";
                wk.GlobalSettings.Margin.Right = "0cm";

                wk.ObjectSettings.Web.EnablePlugins = false;
                wk.ObjectSettings.Web.EnableJavascript = false;
                wk.ObjectSettings.Load.Proxy = "none";

                var htmlString = File.ReadAllText(@"c:\page.xhtml");
                var tmp = wk.Convert(htmlString);

                File.WriteAllBytes(@"tst.pdf", tmp);
            }
        }
    }
}

کار با وهله سازی از کلاس MultiplexingConverter شروع می‌شود. اگر علاقمند باشید که درصد پیشرفت کار به همراه خطاهای احتمالی پردازشی را ملاحظه کنید می‌توان از رخدادگردان‌هایی مانند ProgressChanged و Error استفاده نمائید که نمونه‌ای از آن در کد فوق بکارگرفته شده است.
تبدیل HTML به PDF آنچنان تنظیمات خاصی ندارد زیرا فرض بر این است که قرار است از همان تنظیمات اصلی HTML مورد نظر استفاده گردد. اما اگر نیاز به تنظیمات بیشتری وجود داشت، برای مثال به کمک GlobalSettings آن می‌توان حاشیه‌های صفحات فایل نهایی تولیدی را تنظیم کرد.
موتور WebKit با توجه به اینکه موتور یک مرورگر است، امکان پردازش جاوا اسکریپت را هم دارد. بنابراین اگر قصد استفاده از آن‌را نداشتید می‌توان خاصیت ObjectSettings.Web.EnableJavascript را به false مقدار دهی کرد.
کار اصلی، در متد Convert انجام می‌شود. در اینجا می‌توان یک رشته را که حاوی فایل HTML مورد نظر است به آن ارسال کرد و نتیجه نهایی، آرایه‌ای از بایت‌ها، حاوی فایل باینری PDF تولیدی است.
روش دیگر استفاده از این کتابخانه، مقدار دهی wk.ObjectSettings.Page می‌باشد. در اینجا می‌توان Url یک صفحه اینترنتی را مشخص ساخت. در این حالت دیگر نیازی نیست تا به متد Convert پارامتری را ارسال کرد. می‌توان از overload بدون پارامتر آن استفاده نمود.

یک نکته:
اگر می‌خواهید زبان فارسی را توسط این کتابخانه به درستی پردازش کنید، نیاز است حتما یک سطر زیر را به header فایل html خود اضافه نمائید:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

 
نظرات مطالب
آموزش TypeScript #1
یکی از دلایل محبوبیت زبان JavaScript، راحتی در نوشتن کد با این زبان است. اگر قرار باشد این زبان یک محصول همه منظوره باشد به طور قطع دچار پیچیدگی‌های پیاده سازی شده و این همه محبوبیت به دست نمی‌آورد. هدف اولیه از تولید و توسعه زبان JavaScript، استفاده از آن در پروژه‌های سمت کلاینت بود. اما با مرور زمان و محبوبیت بیش از اندازه، توسعه گران مختلف تصمیم به توسعه این زبان گرفتند که هر محصول برای یک منظور خاص به وجود آمد.  برای مثال Node.Js برای پروژه‌های RealTime استفاه می‌شود و بر مبنای منطق event-driven می‌باشد که خیلی‌ها از آن به عنوان Server side JavaScript یاد می‌کنند یا به عنوان مثال دیگر Dart محصول شرکت گوگل در سال 2011 (طراحی شده بر مبنای Scratch)و TypeScript محصول  شرکت مایکروسافت در سال 2012 (طراحی شده بر مبنای JavaScript)عرضه شدند که هدف اصلی از تولید این زبان‌ها پشتیبانی از مبحث static typing و مباحث OOP برای پیاده سازی پروژه‌های در سطوحی با مقیاس بزرگ بود. JavaScript به عنوان زبان پایه باقی خواهد ماند و نسخه‌های مختلف در شکل سایر زبان‌ها و فریم ورک‌های مختلف عرضه می‌شوند تا هر کدام یک نیاز را برطرف سازند. البته در پایان این نکته را هم متذکر شوم که JavaScript هم روند با توسعه ECMAScript تغییر می‌کند. برای مثال در نسخه ECMASCript 6، امکان تعریف کلاس و ماژول در JavaSCript فراهم شده است.
مطالب
مسیریابی در Angular - قسمت دهم - Lazy loading
می‌خواهیم زمان نمایش اولین قالب برنامه را به حداقل برسانیم تا تاثیر روانی بهتری را بر روی کاربرانی که برنامه را اجرا می‌کنند، بگذاریم. برای این منظور در Angular، از Lazy loading استفاده می‌شود. همچنین این فریم ورک به همراه قابلیت پیش بارگذاری ماژول‌ها نیز هست تا سایر مسیرهای درخواستی را نیز با سرعت هرچه تمام‌تر نمایش دهد.
زمانیکه کاربری برنامه‌ی تک صفحه‌ای وب را در مرورگر باز می‌کند، ابتدا فایل index.html را در پاسخ دریافت خواهد کرد. این فایل تعاریف مداخل مورد نیاز برای رندر آن‌را مانند فایل‌های جاوا اسکریپت و CSS، به همراه دارد. سپس این فایل‌ها توسط مرورگر از سرور دریافت می‌شوند. در این حالت با پردازش این فایل‌ها، کامپوننت ریشه‌ی سایت بارگذاری می‌شود. پس از پایان آن، قالب این کامپوننت به کاربر نمایش داده خواهد شد. بنابر سرعت دریافت فایل‌ها توسط کاربر، این آغاز می‌تواند اندکی کند باشد. البته با رعایت نکات گفته‌ی شده‌ی در مطلب «Angular CLI - قسمت پنجم - ساخت و توزیع برنامه» می‌توان این حجم را توسط AoT و Tree-Shaking به میزان قابل ملاحظه‌ای کاهش داد. به علاوه با فعالسازی Lazy loading می‌توان قسمت‌های مختلف برنامه را تبدیل به یک سری Bundle کرد که در زمان درخواست، بارگذاری می‌شوند. به این ترتیب حجم فایل‌های ابتدایی که باید از سرور دریافت شوند بسیار کمتر شده و به علاوه با کاهش این حجم، مرورگر نیز باید میزان کمتری از کدها را در جهت نمایش اولین کامپوننت، پردازش و اجرا کند. در این حالت زمانیکه کاربری شروع به پیمایش مسیر یک ماژول خاص را می‌کند، آنگاه فایل‌های مرتبط با آن از سرور دریافت و در مرورگر پردازش می‌شوند. بنابراین اگر کاربری به قسمتی دسترسی ندارد، نیازی هم به دریافت فایل‌های آن نخواهد داشت؛ چون کار به فعالسازی مسیریابی آن ماژول نمی‌رسد.


آماده شدن جهت Lazy loading

پیش از Lazy loading یک قسمت از برنامه (که به آن async routing هم می‌گویند)، این قسمت باید دارای شرایطی باشد:
 - این قسمت از برنامه حتما باید در یک ماژول تعریف شده باشد. از این جهت که Lazy loading، لیست کامپوننت‌های قید شده‌ی در تعریف یک ماژول را بارگذاری می‌کند.
 - تمام مسیرهای این ماژول باید در ذیل یک مسیر والد، گروه بندی شده باشند. از این جهت که Lazy loading فقط بر روی مسیر ریشه‌ی والد تنظیم و بارگذاری می‌شود.
 - این ماژول نباید در هیچ ماژول دیگری import شده باشد. اگر این ماژول ارجاعی را در سایر ماژول‌ها داشته باشد، هیچ راهی بجز دریافت و کامپایل کامل آن توسط Angular وجود نخواهد داشت.


در مثال جاری این سری:
 - تمام ویژگی‌های قسمت مدیریت محصولات، داخل ماژول product.module.ts تعریف شده‌اند. بنابراین اولین شرط Lazy loading آن برقرار است.
 - در فایل product-routing.module.ts، کار گروه بندی مسیریابی‌ها ذیل یک والد مشخص انجام شده‌است (همان قسمت ششم این سری). بنابراین شرط دوم lazy loading این ماژول نیز پیشتر پیاده سازی شده‌است.
 - اما اگر به فایل src\app\app.module.ts مراجعه کنیم، ارجاعی به این ماژول در قسمت imports آن وجود دارد. بنابراین باید این ارجاع را حذف کنیم. در غیراینصورت کار دریافت کامل آن به همراه سایر ماژول‌های برنامه، در همان ابتدای کار صورت خواهد گرفت.
بنابراین در فایل src\app\app.module.ts، ابتدا import فایل آن‌را از ابتدای ماژول حذف و سپس ارجاع به نام کلاس کامپوننت ProductModule را نیز حذف می‌کنیم. در این حالت اگر از طریق منوی سایت سعی در دسترسی به این مسیرها کنیم، خطای 404 را دریافت خواهیم کرد؛ چون اکنون برنامه اطلاعاتی را در مورد نحوه‌ی مسیریابی قسمت محصولات برنامه، ندارد.

 
Lazy loading یک ماژول

برای بارگذاری غیرهمزمان یک ماژول و یا همان Lazy loading، می‌توان از خاصیت loadChildren تنظیمات مسیریابی، استفاده کرد:
{
   path: 'products',
   loadChildren:'app/product/product.module#ProductModule'
},
مقدار خاصیت loadChildren به صورت ذکر مسیر ماژول مرتبط به همراه یک # و سپس ذکر نام کلاس ماژول آن انجام می‌شود. مسیری هم که در اینجا ذکر می‌شود بر اساس محل قرارگیری فایل index.html، مقدار دهی شود.
با این تنظیم، زمانیکه مسیر ریشه‌ی produtcs درخواست شد، کار بارگذاری ماژول آن صورت گرفته و تنظیمات مسیریابی آن به سیستم اضافه می‌شود. به علاوه کار فعالسازی و نمایش کامپوننت آن را نیز انجام خواهد داد.

به همین منظور فایل src\app\app-routing.module.ts را گشوده و تنظیم فوق را به آن اضافه می‌کنیم:
const routes: Routes = [
  { path: 'home', component: WelcomeComponent },
  { path: 'welcome', redirectTo: 'home', pathMatch: 'full' },
  { path: 'products', loadChildren: 'app/product/product.module#ProductModule' },
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];
در این حالت اگر دستور ng serve -o را صادر کنید، خروجی آن اندکی متفاوت خواهد بود:
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 165 kB {4} [initial]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 32.7 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 129 kB {4} [initial]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.72 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry]
chunk    {5} 5.chunk.js, 5.chunk.js.map 51.1 kB {1} [rendered]
مورد {5} با فعالسازی lazy loading به لیست فایل‌های موجود اضافه شده‌است. این فایلی است که تنها درصورت درخواست مسیر نمایش لیست محصولات، توسط مرورگر دریافت خواهد شد و هیچ ارجاع مستقیمی به آن در فایل index.html تولیدی نهایی وجود ندارد.

به علاوه اگر در منوی سایت بر روی لینک نمایش لیست محصولات کلیک کنیم، هنوز خروجی نمایش داده نمی‌شود (هرچند خطای 404 را هم دریافت نمی‌کنیم). علت اینجا است که اگر به فایل src\app\product\product-routing.module.ts مراجعه کنیم، تعریف این مسیر ریشه، در این فایل نیز وجود دارد:
const routes: Routes = [
  {
    path: 'products',
    canActivate: [ AuthGuard ],
    children: [   ]
  }
];
بنابراین اکنون برای دسترسی به آن باید مسیر products/products را درخواست داد. به همین جهت، path و canActivate آن‌را حذف کرده و هر دو را به فایل src\app\app-routing.module.ts منتقل می‌کنیم:
import { AuthGuard } from './user/auth.guard';

const routes: Routes = [
  { path: 'home', component: WelcomeComponent },
  { path: 'welcome', redirectTo: 'home', pathMatch: 'full' },
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule',
    canActivate: [AuthGuard]
  },
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

یک نکته: اکنون تنظیمات مسیریابی فایل src\app\product\product-routing.module.ts چنین شکلی را پیدا کرده‌است:
const routes: Routes = [
  {
    path: '',
    component: ProductListComponent
  },
  {
    path: ':id',
    component: ProductDetailComponent,
    resolve: { product: ProductResolverService }
  },
  {
    path: ':id/edit',
    component: ProductEditComponent,
    resolve: { product: ProductResolverService },
    canDeactivate: [ProductEditGuard],
    children: [
      { path: '', redirectTo: 'info', pathMatch: 'full' },
      { path: 'info', component: ProductEditInfoComponent },
      { path: 'tags', component: ProductEditTagsComponent }
    ]
  }
];
تنظیمات مسیر والد به طور کامل حذف شده‌اند. به علاوه دیگر نیازی به ذکر خاصیت children آن نیست و تمام تنظیمات مسیریابی فرزندان، داخل [] اصلی قرار گرفته‌اند. همچنین دیگر نیازی به الحاق AuthGuard در ابتدای importهای این ماژول نیست؛ چون به فایل src\app\app-routing.module.ts منتقل شده‌است.

در این حالت اگر مسیر نمایش لیست محصولات را درخواست دهیم، مشاهده خواهیم کرد فایل 5.chunk.js که حاوی اطلاعات این ماژول است، به صورت مجزایی بارگذاری شده (lazy loading) و سپس با فعال شدن محافظ مسیر آن، صفحه‌ی لاگین نمایش داده می‌شود:


این بارگذاری با تاخیر و در صورت نیاز، به دو علت آغاز برنامه را سریعتر می‌کند:
الف) مرورگر اطلاعی از وجود فایل 5.chunk.js در ابتدای کار نداشته و آن‌را بارگذاری نمی‌کند (دریافت حجم کمتر، در آغاز نمایش برنامه).
ب) چون حجم کمتری از کدهای جاوا اسکریپت توسط مرورگر در آغاز کار دریافت می‌شود، کار پردازش و اجرای آن‌ها نیز بسیار سریعتر خواهد شد.


بررسی محافظ canLoad

تعدادی از محافظ‌های مسیرها را در قسمت قبل بررسی کردیم. هنگامیکه کامپوننت‌ها به صورت lazy loading فعالسازی شده و قالب آن‌ها نمایش داده می‌شوند، می‌توان از محافظ مسیر دیگری به نام canLoad نیز استفاده کرد و هدف از آن، بررسی منطقی، پیش از فعالسازی یک مسیر غیرهمزمان است. بنابراین اگر این محافظ false را برگرداند، حتی فایل‌های اسکریپت این ماژول، بارگذاری اولیه نیز نخواهد شد. به این ترتیب کسانیکه دسترسی به یک مسیر را نداشته باشند، فایل‌های اسکریپت متناظر با آن‌را نیز دریافت نخواهند کرد.

در مثال جاری، اگر به برگه‌ی network ابزار developer مرورگر دقت کنید، با درخواست نمایش مسیر لیست محصولات، ابتدا فایل js آن دریافت می‌شود که حاوی اطلاعات تمام کامپوننت‌ها و قالب‌های مرتبط با این مسیر است و سپس صفحه‌ی login نمایش داده خواهد شد. بنابراین اگر کاربر به این قسمت دسترسی نداشته باشد، فایل js آن بی‌جهت دریافت و بارگذاری شده‌است. برای بهبود این وضعیت می‌توان نمایش لاگین را پیش از بارگذاری فایل js این ماژول فعالسازی کرد و این مورد هدف اصلی محافظ canLoad است.

در ادامه برای تکمیل مثال جاری، می‌توان AuthGuard را طوری تنظیم کرد که علاوه بر پیاده سازی CanActivate، اینترفیس CanLoad را نیز پیاده سازی کند:
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router, CanLoad, Route } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate, CanLoad {

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.checkLoggedIn(state.url);
  }

  canLoad(route: Route): boolean {
    return this.checkLoggedIn(route.path);
  }

 // … the same as before

}
همانطور که ملاحظه می‌کنید، متد canLoad بر خلاف متد canActivate دسترسی به سرویس‌های اطلاعات مسیریابی و وضعیت مسیریابی را ندارد؛ از این جهت که هنوز در این مرحله، ماژول درخواستی حاوی تنظیمات مسیریابی، بارگذاری و فعالسازی نشده‌است.

مرحله‌ی بعد، تغییر فایل src\app\app-routing.module.ts و جایگزین کردن تعریف فعلی canActivate با canLoad است:
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule',
    canLoad: [AuthGuard]
  },
پس از این تغییر، برنامه را مجدا اجرا کرده و صفحه را refresh کنید. سپس برگه‌ی network ابزار developers را نیز باز نگه دارید. اکنون بر روی لینک نمایش لیست محصولات کلیک کنید. مشاهده خواهید کرد که در این حالت صفحه‌ی لاگین، بدون بارگذاری ماژول Js ایی نمایش داده می‌شود. در ادامه اگر لاگین کنیم، آنگاه فایل js این ماژول توسط مرورگر دریافت شده و بارگذاری می‌شود.


پیش بارگذاری ماژول‌ها

با فعالسازی lazy loading، ماژول‌های مورد نیاز کاربر دیگر به همراه فایل‌های js ابتدایی برنامه که در فایل index.html ارجاع مستقیمی به آن‌ها دارند، ارائه نمی‌شوند و تنها در صورت درخواست مشاهده‌ی مسیری، کار بارگذاری آن‌ها توسط برنامه صورت خواهد گرفت. همین مساله می‌تواند در بار اول نمایش این ماژول‌ها تاخیر کوتاهی را سبب شود. به همین جهت قابلیت پیش بارگذاری ماژول‌ها نیز در سیستم مسیریاب Angular پیش بینی شده‌است. به این قابلیت preloading و یا eager lazy loading نیز می‌گویند. در این حالت برنامه در پشت صحنه، کار پیش واکشی ماژول‌ها را انجام می‌دهد و زمانیکه کاربری مسیری را درخواست می‌دهد، آْن مسیر را بدون درنگ مشاهده خواهد کرد.
بدیهی است این قابلیت نباید برای ماژول‌هایی که قرار است توسط کاربرانی خاص مشاهده شوند فعال شود و هدف آن دسترسی سریع به ماژول‌های پرکاربرد برنامه‌است.

در اینجا سه استراتژی پیش بارگذاری ماژول‌ها میسر است:
 - No preloading که حالت پیش فرض است.
 - Preload all سبب پیش بارگذاری تمام قسمت‌های lazy load برنامه می‌شود.
 - Custom که اجازه‌ی تعریف یک استراتژی سفارشی را می‌دهد.

برای مثال برای فعالسازی حالت Preload all، باید به فایل src\app\app-routing.module.ts مراجعه کرده و تغییرات ذیل را اعمال کنیم:
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';

@NgModule({
  imports: [RouterModule.forRoot(
    routes,
    { enableTracing: true, preloadingStrategy: PreloadAllModules  /*, useHash: true*/ }
  )],
در اینجا نحوه‌ی تنظیم preloadingStrategy را به PreloadAllModules مشاهده می‌کنید. در این حالت پس از آغاز ابتدایی برنامه، مسیریاب بلافاصله تمام مسیرهای lazy load را در پشت صحنه بارگذاری می‌کند.

یک نکته: وجود محافظ canLoad، هر نوع استراتژی prealoading را غیرفعال می‌کند. اما prealoading با سایر انواع محافظ‌ها کار می‌کند.
بنابراین برای آزمایش تنظیم  preloadingStrategy: PreloadAllModules، تعریف canLoad را به canActivate تغییر دهید.


تعریف استراتژی‌های سفارشی پیش بارگذاری ماژول‌ها

اگر نیاز به یک استراتژی پیش بارگذاری بهتر از هیچ یا همه باشد، می‌توان یک استراتژی سفارشی را نیز تدارک دید و ایجاد آن سه مرحله‌ی ایجاد سرویس مرتبط، ثبت آن سرویس در ماژول و در آخر تنظیم مسیریابی را به همراه دارد.
برای این منظور ابتدا دستور ذیل را صادر کنید تا قالب ابتدایی سرویس SelectiveStrategy ایجاد شود:
 >ng g s SelectiveStrategy -m app.module
که سبب تولید و به روز رسانی فایل‌های ذیل در پوشه‌ی src\app خواهد شد (چون مرتبط است به کل برنامه):
 installing service
  create src\app\selective-strategy.service.spec.ts
  create src\app\selective-strategy.service.ts
  update src\app\app.module.ts
در این حالت لیست providers فایل app.module.ts نیز به صورت خودکار تکمیل می‌گردد.

سپس کدهای SelectiveStrategyService را به نحو ذیل تغییر دهید:
import { Injectable } from '@angular/core';
import { Route, PreloadingStrategy } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';

@Injectable()
export class SelectiveStrategyService implements PreloadingStrategy {

  preload(route: Route, load: Function): Observable<any> {
    if (route.data && route.data['preload']) {
      return load();
    }
    return Observable.of(null);
  }
}
- این سرویس ویژه باید اینترفیس PreloadingStrategy را پیاده سازی کند. سپس باید متد اجباری preload آن‌را افزود و تکمیل نمود.
- پارامتر اول این متد، اطلاعاتی را در مورد مسیر جاری در اختیار ما قرار می‌دهد و دومین پارامتر آن متدی است که کار preloading را انجام می‌دهد.
- در اینجا است که تصمیم می‌گیریم ماژولی را preload کنیم یا خیر. برای نمونه در اینجا از خاصیت data مسیریابی استفاده شده‌است. این خاصیت نیز به یک مقدار ثابت اشاره می‌کند (قسمت «ارسال اطلاعات ثابت به مسیرهای مختلف برنامه» قسمت چهارم). برای مثال نام دلخواه آن‌را preload گذاشته‌ایم و اگر مقدار آن به true تنظیم شده بود، آنگاه این مسیر preload خواهد شد. فراخوانی متد load در اینجا به معنای preloading این مسیر است. در غیراینصورت null را بازگشت می‌دهیم.


در ادامه نیاز است در فایل src\app\app-routing.module.ts، بجای معرفی PreloadAllModules، این استراتژی سفارشی خود را معرفی کرد:
import { SelectiveStrategyService } from './selective-strategy.service';

@NgModule({
  imports: [RouterModule.forRoot(
    routes,
    { enableTracing: true, preloadingStrategy: SelectiveStrategyService
     /*, preloadingStrategy: PreloadAllModules*/  /*, useHash: true*/ }
  )],
و همچنین تعریف مسیریابی برنامه به این صورت تغییر می‌کند:
  {
    path: 'products',
    loadChildren: 'app/product/product.module#ProductModule',
    //canLoad: [AuthGuard] 
    canActivate: [AuthGuard],
    data: { preload: true }
  },
در اینجا نحوه‌ی مقدار دهی خاصیت data را به اطلاعات ثابت preload: true مشاهده می‌کنید. این اطلاعاتی است که در سرویس SelectiveStrategy سفارشی ما بررسی شده و بر اساس آن در مورد پیش بارگذاری این مسیر تصمیم‌گیری می‌شود.

برای آزمایش آن، برنامه را مجدا اجرا کرده و صفحه را refresh کنید. سپس برگه‌ی network ابزار developers را نیز باز نگه دارید. مشاهده خواهید کرد که علاوه بر فایل‌های js اصلی برنامه که در فایل index.html ارجاعی را دارند، فایل 5.chunk.js نیز پیش بارگذاری شده‌است.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-routing-lab-09.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng s -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت اول - Redux چیست؟
Redux و Mobx، کتابخانه‌های کمکی هستند برای مدیریت حالت برنامه‌های پیچیده‌ی React. هرچند React به صورت توکار به همراه امکانات مدیریت حالت است، اما این کتابخانه‌ها مزایای ویژه‌ای را به آن اضافه می‌کنند. در این سری ابتدا کتابخانه‌ی Redux را به صورت خالص و مجزای از React بررسی می‌کنیم. از این کتابخانه در برنامه‌های Angular و Ember هم می‌توان استفاده کرد و به صورت اختصاصی برای React طراحی نشده‌است. سپس آن‌را به برنامه‌های React متصل می‌کنیم. در آخر کتابخانه‌ی محبوب دیگری را به نام Mobx بررسی می‌کنیم که برای مدیریت حالت، اصول برنامه نویسی شیءگرا و همچنین Reactive را با هم ترکیب می‌کند و این روزها در برنامه‌های React، بیشتر از Redux مورد استفاده قرار می‌گیرد.


چرا به ابزارهای مدیریت حالت نیاز داریم؟

به محض رد شدن از مرز پیاده سازی امکانات اولیه‌ی یک برنامه، نیاز به ابزارهای مدیریت حالت نمایان می‌شوند؛ خصوصا زمانیکه نیاز است با اطلاعات قابل توجهی سر و کار داشت. مهم‌ترین دلیل استفاده‌ی از یک ابزار مدیریت حالت، مدیریت منطق تجاری برنامه است. منطق نمایشی برنامه مرتبط است به نحوه‌ی نمایش اجزای آن در صفحه؛ مانند نمایش یک صفحه‌ی مودال، تغییر رنگ عناصر با عبور کرسر ماوس از روی آن‌ها و در کل منطقی که مرتبط و یا وابسته‌ی به هدف اصلی برنامه نیست. از سوی دیگر منطق تجاری برنامه مرتبط است با مدیریت، تغییر و ذخیره سازی اشیاء تجاری مورد نیاز آن؛ مانند اطلاعات حساب کاربری شخص و دریافت اطلاعات برنامه از یک API که مختص به برنامه‌ی خاص ما است و به همین دلیل نیاز به ابزاری برای مدیریت بهینه‌ی آن وجود دارد. برای مثال اینکه در کجا باید منطق تجاری و نمایشی را به هم متصل کرد، می‌تواند چالش بر انگیر باشد. چگونه باید اطلاعات کاربر را ذخیره کرد؟ چگونه React باید متوجه شود که اطلاعات ما تغییر کرده‌است و در نتیجه‌ی آن کامپوننتی را مجددا رندر کند؟ یک ابزار مدیریت حالت، تمام این مسایل را به نحو یک‌دستی در سراسر برنامه، مدیریت می‌کند.
اگر از یک ابزار مدیریت حالت استفاده نکنیم، مجبور خواهیم شد تمام اطلاعات منطق تجاری را در داخل state کامپوننت‌ها ذخیره کنیم که توصیه نمی‌شود؛ چون مقیاس پذیر نیست. برای مثال فرض کنید قرار است تمام اطلاعات state را داخل یک کامپوننت ذخیره کنیم. هر زمانیکه بخواهیم این state را از طریق یک کامپوننت فرزند تغییر دهیم، نیاز خواهد بود این اطلاعات را به والد آن کامپوننت ارسال کنیم که اگر از تعداد زیادی کامپوننت تو در تو تشکیل شده باشد، زمانبر و به همراه کدهای تکراری زیادی خواهد بود. همچنین اینکار سبب رندر مجدد کل برنامه با هر تغییری در state آن می‌شود که غیرضروری بوده و کارآیی برنامه را کاهش می‌دهد. به علاوه در این بین مشخص نیست هر قسمت از state، از کدام کامپوننت تامین شده‌است. به همین جهت نیاز به روشی برای مدیریت حالت در بین کامپوننت‌های برنامه وجود دارد.


داشتن تنها یک محل برای ذخیره سازی state در برنامه

همانطور که در قسمت 8 ترکیب کامپوننت‌ها در سری React 16x بررسی کردیم، هر کامپوننت در React، دارای state خاص خودش است و این state از سایر کامپوننت‌ها کاملا مستقل و ایزوله‌است. این مورد با بزرگ‌تر شدن برنامه و برقراری ارتباط بین کامپوننت‌ها، مشکل ایجاد می‌کند. برای مثال اگر بخواهیم دکمه‌ای را در صفحه قرار داده و توسط این دکمه درخواست صفر شدن مقدار هر کدام از شمارشگرها را صادر کنیم، با صفر کردن value هر کدام از این کامپوننت‌ها، اتفاقی رخ نمی‌دهد. چون state محلی این کامپوننت‌ها، با سایر اجزای صفحه به اشتراک گذاشته نمی‌شود و باید آن‌را تبدیل به یک controlled component کرد، بطوریکه دارای local state خاص خودش نیست و تمام داده‌های دریافتی را از طریق this.props دریافت می‌کند و هر زمانیکه قرار است داده‌ای تغییر کند، رخ‌دادی را به والد خود صادر می‌کند. بنابراین این کامپوننت به طور کامل توسط والد آن کنترل می‌شود. تازه این روش در مورد کامپوننت‌هایی صدق می‌کند که رابطه‌ی والد و فرزندی بین آن‌ها وجود دارد. اگر چنین رابطه‌ای وجود نداشت، باید state را به یک سطح بالاتر انتقال داد. برای مثال باید state کامپوننت Counters را به والد آن که کامپوننت App است، منتقل کرد. پس از آن چون کامپوننت‌های ما، از کامپوننت App مشتق می‌شوند، اکنون می‌توان این state را به تمام فرزندان App توسط props منتقل کرد و به اشتراک گذاشت. این مورد هم مانند مثال انتقال اطلاعات کاربر لاگین شده‌ی به سیستم، به تمام زیر قسمت‌های برنامه، نیاز به ارسال اطلاعات از طریق props یک کامپوننت، به کامپوننت بعدی را دارد و به همین ترتیب برای مابقی که به props drilling مشهور است و روش پسندیده‌ای نیست.


Redux چیست؟ ذخیره سازی کل درخت state یک برنامه، در یک محل. به این ترتیب به یک شیء جاوا اسکریپتی بزرگ خواهیم رسید که در برگیرنده‌ی تمام state برنامه‌است. یکی از مزایای آن امکان serialize و deserialize کل این شیء، به سادگی است. برای مثال توسط متد JSON.stringify می‌توان آن‌را در جائی ذخیره کرد و سپس آن‌را به صورت یک شیء جاو اسکریپتی در زمانی دیگر بازیابی کرد. یکی از مزایای آن، امکان بازیابی دقیق شرایط کاربری است که دچار مشکل شده‌است و سپس دیباگ و رفع مشکل او، در زمانی دیگر.


تاریخچه‌ای از سیستم‌های مدیریت حالت

همه چیز با AngularJS 1x شروع شد که از data binding دو طرفه پشتیبانی می‌کرد. هرچند این روش برای همگام نگه داشتن View و مدل برنامه، مفید است، اما در Viewهای پیچیده، برنامه را کند می‌کند. در همین زمان فیس‌بوک، روش مدیریت حالتی را به نام Flux ارائه داد که از data binding یک طرفه پشتیبانی می‌کرد. به این معنا که در این روش، همواره اطلاعات از View به مدل، جریان پیدا می‌کند. کار کردن با آن ساده‌است؛ چون نیازی نیست حدس زده شود که اکنون جریان اطلاعات از کدام سمت است. اما مشکل آن عدم هماهنگی model و view، در بعضی از حالات است. Flux از این جهت به وجود آمد که مدیریت حالت در برنامه‌های React آن زمان، پیچیده بود و مقیاس پذیری کمی داشت (پیش از ارائه‌ی Context و Hooks). در کل Flux صرفا یکسری الگوی مدیریت حالت را بیان می‌کند و یک کتابخانه‌ی مجزا نیست. بر مبنای این الگوها و قراردادها، می‌توان کتابخانه‌های مختلفی را ایجاد کرد. از این رو در سال 2015، کتابخانه‌های زیادی مانند Reflux, Flummox, MartyJS, Alt, Redux و غیره برای پیاده سازی آن پدید آمدند. در این بین، کتابخانه‌ی Redux ماندگار شد و پیروز این نبرد بود!


توابع خالص و ناخالص (Pure & Impure Functions)

پیش از شروع بحث، نیاز است با یک‌سری از واژه‌ها مانند توابع خالص و ناخالص آشنا شد. این نکات از این جهت مهم هستند که Redux فقط با توابع خالص کار می‌کند.
توابع خالص: تعدادی آرگومان را دریافت کرده و بر اساس آن‌ها، مقداری را باز می‌گردانند.
// Pure
const add = (a, b) => {
  return a + b;
}
در اینجا یک تابع خالص را مشاهده می‌کنید که a و b را دریافت کرده و بر این اساس، یک خروجی کاملا مشخص را بازگشت می‌دهد.

توابع ناخالص: این نوع توابع سبب تغییراتی در متغیرهایی خارج از میدان دید خود می‌شوند و یا به همراه یک سری اثرات جانبی (side effects) مانند تعامل با دنیای خارج (وجود یک console.log در آن تابع و یا دریافت اطلاعاتی از یک API خارجی) هستند.
// Impure
const b;

const add = (a) => {
  return a + b;
}
تابع تعریف شده‌ی در اینجا ناخالص است؛ چون با اطلاعاتی خارج از میدان دید خود مانند متغیر b، تعامل دارد. این تعامل با دنیای خارج، حتی در حد نوشتن یک console.log:
// Impure
const add = (a, b) => {
  console.log('lolololol');
  return a + b;
}
یک تابع خالص را تبدیل به یک تابع ناخالص می‌کند و یا نمونه‌ی دیگر این تعاملات، فراخوانی سرویس‌های backend در برنامه هستند که یک تابع را ناخالص می‌کنند:
// Impure
const add = (a, b) => {
   Api.post('/add', { a, b }, (response) => {
    // Do something.
   });
};


روش‌هایی برای جلوگیری از تغییرات در اشیاء در جاوا اسکریپت

ایجاد تغییرات در آرایه‌ها و اشیاء (Mutating arrays and objects) نیز ناخالصی ایجاد می‌کند؛ از این جهت که سبب تغییراتی در دنیای خارج (خارج از میدان دید تابع) می‌شویم. به همین جهت نیاز به روش‌هایی وجود دارد که از این نوع تغییرات جلوگیری کرد:
// Copy object
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original);
برای تغییری در یک شیء، تنها کافی است خاصیتی را به آن اضافه کنیم و یا با استفاده از واژه‌ی کلیدی delete، خاصیتی را از آن حذف کنیم. به همین جهت برای اینکه تغییرات ما بر روی شیء اصلی اثری را باقی نگذارند، یکی از روش‌ها، استفاده از متد Object.assign است. کار آن، یکی کردن اشیایی است که به آن ارسال می‌شوند. به همین جهت در اینجا با یک شیء خالی، از صفر شروع می‌کنیم. سپس دومین آرگومان آن را به همان شیء مدنظر، تنظیم می‌کنیم. به این ترتیب به یک کپی از شیء اصلی می‌رسیم که دیگر به آن، اتصالی را ندارد. به همین جهت اگر بر روی این شیء کپی تغییراتی را ایجاد کنیم، به شیء اصلی کپی نمی‌شود و سبب تغییرات در آن (mutation) نخواهد شد.
برای مثال در React، برای انجام رندر نهایی، در پشت صحنه کار مقایسه‌ی اشیاء صورت می‌گیرد. به همین جهت اگر همان شیءای را که ردیابی می‌کند تغییر دهیم، دیگر نمی‌تواند به صورت مؤثری فقط قسمت‌های تغییر کرده‌ی آن‌را تشخیص داده و کار رندر را فقط بر اساس آن‌ها انجام دهد و مجبور خواهد شد کل یک شیء را بارها و بارها رندر کند که اصلا بهینه نیست. به همین جهت، ایجاد تغییرات مستقیم در شیءای که به state آن انتساب داده می‌شود، مجاز نیست.

متد Object.assign، چندین شیء را نیز می‌تواند با هم یکی کند و شیء جدیدی را تشکیل دهد:
// Extend object
const original = { a: 1, b: 2 };
const extension = { c: 3 };
const extended = Object.assign({}, original, extension);
روش دیگر ایجاد یک کپی و یا clone از یک شیء را که پیشتر در سری «React 16x» بررسی کردیم، به کمک امکانات ES-6، به صورت زیر است:
// Copy object
const original = { a: 1, b: 2 };
const copy = { ...original };
در اینجا نیز ابتدا یک شیء خالی را ایجاد می‌کنیم و سپس توسط spread operator، خواص شیء قبلی را درون آن باز کرده و قرار می‌دهیم. به این ترتیب به یک clone از شیء اصلی می‌رسیم. این حالت نیز از ترکیب چندین شیء با هم، پشتیبانی می‌کند:
// Extend object
const original = { a: 1, b: 2 };
const extension = { c: 3 };
const extended = { ...original, ...extension };


روش‌هایی برای جلوگیری از تغییرات در آرایه‌ها در جاوا اسکریپت

متد slice آرایه‌ها نیز بدون ذکر آرگومانی، یک کپی از آرایه‌ی اصلی را ایجاد می‌کند:
// Copy array
const original = [1, 2, 3];
const copy = [1, 2, 3].slice();
همچنین معادل همین قطعه کد در ES-6 به همراه spread operator به صورت زیر است:
// Copy array
const original = [1, 2, 3];
const copy = [ ...original ];
و یا اگر بخواهیم یک کپی از چندین آرایه را ایجاد کنیم می‌توان از متد concat استفاده کرد:
// Extend array
const original = [1, 2, 3];
const extended = original.concat(4);
const moreExtended = original.concat([4, 5]);
متد Array.push، هرچند سبب افزوده شدن عنصری به یک آرایه می‌شود، اما یک mutation را نیز ایجاد می‌کند؛ یعنی تغییرات آن به دنیای خارج اعمال می‌گردد. اما Array.concat یک آرایه‌ی کاملا جدید را ایجاد می‌کند و همچنین امکان ترکیب آرایه‌ها را نیز به همراه دارد.
معادل قطعه کد فوق در ES-6 و به همراه spread operator آن به صورت زیر است:
// Extend array
const original = [1, 2, 3];
const extended = [ ...original, 4 ];
const moreExtended = [ ...original, ...extended, 5 ];


مفاهیم ابتدایی Redux


در Redux برای ایجاد تغییرات در شیء کلی state، از مفهومی به نام dispatch actions استفاده می‌شود. action در اینجا به معنای رخ‌دادن چیزی است؛ مانند کلیک بر روی یک دکمه و یا دریافت اطلاعاتی از یک API. در این حالت مقایسه‌ای بین وضعیت قبلی state و وضعیت فعلی آن صورت می‌گیرد و تغییرات مورد نیاز جهت اعمال به UI، محاسبه خواهند شد.
اصلی‌ترین جزء Redux، تابعی است به نام Reducer. این تابع، یک تابع خالص است و دو آرگومان را دریافت می‌کند:


تابع Reducer، بر اساس action و یا رخ‌دادی، ابتدا کل state برنامه را دریافت می‌کند و سپس خروجی آن بر اساس منطق این تابع، یک state جدید خواهد بود. اکنون که این state جدید را داریم، برنامه‌ی React ما می‌تواند به تغییرات آن گوش فرا داده و بر اساس آن، UI را به روز رسانی کند. به این ترتیب کار اصلی مدیریت state، به خارج از برنامه‌ی React منتقل می‌شود.

در این تصویر، تابع action creator را هم ملاحظه می‌کند که کاملا اختیاری است. یک action می‌تواند یک رشته و یا یک عدد باشد. با پیچیده شدن برنامه، نیاز به ارسال یک‌سری متادیتا و یا اطلاعات بیشتری از اکشن رسیده‌است. کار action creator، ایجاد شیء action، به صورت یک دست و یکنواخت است تا دیگر نیازی به ایجاد دستی آن نباشد.


مزایای کار با Redux

- داشتن یک مکان مرکزی برای ذخیره سازی کلی حالت برنامه (به آن «source of truth» و یا store هم گفته می‌شود): به این ترتیب مشکل ارسال خواص در بین کامپوننت‌های عمیق و چند سطحی، برطرف شده و هر زمانیکه نیاز بود، از آن اطلاعاتی را دریافت و یا با قالب خاصی، آن‌را به روز رسانی می‌کنند.
- رسیدن به به‌روز رسانی‌های قابل پیش بینی state: هرچند در حالت کار با Redux، یک شیء بزرگ جاوا اسکریپتی، کل state برنامه را تشکیل می‌دهد، اما امکان کار مستقیم با آن و تغییرش وجود ندارد. به همین جهت است که برای کار با آن، باید رویدادی را از طریق actionها به تابع Reducer آن تحویل داد. چون Reducer یک تابع خالص است، با دریافت یک سری ورودی مشخص، همواره یک خروجی مشخص را نیز تولید می‌کند. به همین جهت قابلیت ضبط و تکرار را پیدا می‌کند؛ همان بحث serialize و deseriliaze، توسط ابزاری مانند: logrocket. به علاوه قابلیت undo و redo را نیز می‌توان به این ترتیب پیاده سازی کرد (state جدید محاسبه شده، مشخص است، کل state قبلی را نیز داریم یا می‌توان ذخیره کرد و سپس برای undo، آن‌را جایگزین state جدید نمود). افزونه‌ی redux dev tools نیز قابلیت import و export کل state را به همراه دارد.
- چون تابع Reducer، یک تابع خالص است و همواره خروجی‌های مشخصی را به ازای ورودی‌های مشخصی، تولید می‌کند، آزمایش کردن، پیاده سازی و حتی logging آن نیز ساده‌تر است. در این بین حتی یک افزونه‌ی مخصوص نیز برای دیباگ آن تهیه شده‌است: redux-devtools-extension. تابع خالص، تابعی است که به همراه اثرات جانبی نیست (side effects)؛ به همین جهت عملکرد آن کاملا قابل پیش بینی بوده و آزمون پذیری آن به دلیل نداشتن وابستگی‌های خارجی، بسیار بالا است.


Context API خود React چطور؟

در قسمت 33 سری React 16x، مفهوم React Context را بررسی کردیم. پس از معرفی آن با React 16.3، مقالات زیادی منتشر شدند که ... Redux مرده‌است (!) و یا بجای Redux از React context استفاده کنید. اما واقعیت این است که React Redux در پشت صحنه از React context استفاده می‌کند و تابع connect آن دقیقا به همین زیر ساخت متصل می‌شود.
کار با Redux مزایایی مانند کارآیی بالاتر، با کاهش رندر‌های مجدد کامپوننت‌ها، دیباگ ساده‌تر با افزونه‌های اختصاصی و همچنین سفارشی سازی، مانند نوشتن میان‌افزارها را به همراه دارد. اما شاید واقعا نیازی به تمام این امکانات را هم نداشته باشید؛ اگر هدف، صرفا انتقال ساده‌تر اطلاعات بوده و برنامه‌ی مدنظر نیز کوچک است. React Context برخلاف Redux، نگهدارنده‌ی state نیست و بیشتر هدفش محلی برای ذخیره سازی اطلاعات مورد استفاده‌ی در چندین و چند کامپوننت تو در تو است. هرچند شبیه به Redux می‌توان اشاره‌گرهایی از متدها را به استفاده کنندگان از آن ارسال کرد تا سبب بروز رویدادها و اکشن‌هایی در کامپوننت تامین کننده‌ی Contrext شوند (یا یک کتابخانه‌ی ابتدایی شبیه به Redux را توسط آن تهیه کرد). بنابراین برای انتخاب بین React Context و Redux باید به اندازه‌ی برنامه، تعداد نفرات تیم، آشنایی آن‌ها با مفاهیم Redux دقت داشت.
بازخوردهای دوره
نگاهی به SignalR Hubs
«عیب یابی و دیباگ برنامه‌های SignalR» خصوصا قسمت «بهترین راه برای مشاهده ریز جرئیات خطاها، ذکر سطر ذیل در کدهای سمت کلاینت جاوا اسکریپتی برنامه است:
 $.connection.hub.logging = true;
و سپس مراجعه به کنسول developers مرورگر برای بررسی خطاهای لاگ شده.»