مطالب
IIS7 و آپلود فایل‌های حجیم

با استفاده از IIS6 ویندوز سرور 2003 و تنظیمات ویژه در web.config یک برنامه ASP.Net، حداکثر می‌توان یک فایل 2 گیگابایتی را آپلود کرد (جهت مصارف اینترانتی). برای مثال:
<system.web>
<httpRuntime maxRequestLength="2097151" executionTimeout="900" />
</system.web>
2097151 کیلوبایت حداکثر مقداری است که اینجا می‌توان تنظیم کرد و بیش از این با خطای زیر متوقف خواهیم شد:

Parser Error Message: The value for the property 'maxRequestLength' is not valid. The error is: The value must be inside the range 0-2097151.

این محدودیت در IIS7 برطرف شده است که تنظیمات آن در وب کانفیگ به صورت زیر می‌باشد:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="4294967295" />
</requestFiltering>
</security>
</system.webServer>

در اینجا maxAllowedContentLength بر حسب بایت است و نه همانند maxRequestLength برحسب کیلوبایت (که در IIS7 هیچ تاثیری نخواهد داشت).
البته تنظیمات فوق در اینجا به پایان نمی‌رسند زیرا بر اساس تنظیمات امنیتی IIS7، کاربران مجاز به اعمال تنظیمات شخصی خود نیستند و خطای زیر را دریافت خواهند کرد:
The requested page cannot be accessed because the related configuration data for the page is invalid
و یا

The request filtering module is configured to deny a request that exceeds the request content length

برای این منظور باید دستور زیر را با دسترسی مدیریتی در خط فرمان اجرا نمود:
برای یک برنامه خاص:
%windir%\system32\inetsrv\appcmd set config "Default Web Site/<your app>" -section:requestFiltering -requestLimits.maxAllowedContentLength:4294967295

و یا برای تمام برنامه‌ها:
%windir%\system32\inetsrv\appcmd set config -section:requestFiltering -requestLimits.maxAllowedContentLength:4294967295

و یا فایل زیر را یافته:
%windir%\System32\inetsrv\config\applicationHost.config
در آن سطر زیر را
<section name="requestFiltering" overrideModeDefault="Deny" />
ویرایش کرده و مقدار overrideModeDefault آن‌را به Allow‌ تنظیم کرد:
<section name="requestFiltering" overrideModeDefault="Allow" />
مقدار پیش فرض maxRequestLength در IIS6 مساوی 4 مگابایت و مقدار پیش فرض maxAllowedContentLength در IIS7 مساوی 28.6MB‌ می‌باشد. maxAllowedContentLength از نوع UINT32 است یعنی حداکثر تا 4 گیگابایت را توسط آن می‌توان مقدار دهی کرد. maxRequestLength از نوع Int32 است با حداکثر مقدار قابل تنظیم 2 گیگابایت.



مطالب
طبقه بندی Bad Code Smell ها
نقل قول‌های زیادی، در مورد کیفیت کد وجود دارند. دستور العمل‌های فراوانی نیز در این راستا وجود دارند. یکی از ابزارهایی که برای نوشتن کدهایی با کیفیت مطلوب وجود دارد، مجموعه الگوهای بد کد نویسی است که به Code smell یا بوی بد کد مشهور هستند.  
بوی بد کد، نشانه‌هایی در کد هستند که حکایت از مشکلات عمیق‌تری دارند. بوی بد کد مساوی با باگ نیست. ولی خطر افزایش باگ‌ها و یا مشکلاتی را در آینده، به دنبال خواهند داشت. بوی بد کد معمولا حاصل رعایت نکردن یک سری اصول اولیه برنامه نویسی و یا طراحی شیء گرا هستند. 
برای بهبود کیفیت نرم افزار در دراز مدت نیاز است موارد بوی بد کد به دقت بررسی و رفع شوند. رفع شدن آنها ریسک انباشته شدن بوی بد کد را در پروژه کم خواهد کرد. یکی از فواید جلوگیری از انباشته شدن چنین الگوهای بدی در پروژه، بهبود فرآیند نگهداشت آن می‌باشد که موضوعی بسیار مهم برای چابکی یک تیم نرم افزاری است. 
هنگام مشاهده‌ی بوی بد، در بخشی از کدها، معمولا اولین اقدام، رفع آن است (Refactoring). در فرآیند رفع آن ممکن است الگوهای بد دیگری در کد یافت شوند که با آنها نیز به همین صورت برخورد خواهد شد. 
انوع بوهای بد کد به دسته‌های زیر طبقه بندی می‌شوند. 

کدهای متورم (Bloaters) 


این دسته در واقع تکه کدهایی (متد، کلاس و ...) هستند که به دلیل بزرگی بیش از اندازه عملا امکان کار با آن‌ها وجود ندارد. این بخش‌های بزرگ کد معمولا با توسعه تدریجی محصول ایجاد و روی هم انباشته می‌شوند. بوهای بد این دسته بندی به صورت زیر هستند:

1 - متدهای بلند (Long method): در این الگوی بد، متدها تعداد خطهای زیادی از کد را شامل می‌شوند. به طور معمول متدهایی با تعداد خطوط بیشتر از 10 خط، متدهای بلند محسوب می‌شوند. نکته قابل توجه این است که هیچ کس متدی را با تعداد خطوط زیاد طراحی نمی‌کند! معمولا به مرور زمان تعداد خط‌های یک متد افزایش می‌یابند. 
2 - کلاس‌های بزرگ (Large class): کلاسی که تعداد فیلدها، متدها و خطوط کد زیادی دارد. 
3 - وسواس استفاده از متغیرهای داده‌ای اولیه (Primitive obsession): این بوی بد معمولا به سه شکل بروز می‌کند. 
  • استفاده از متغیرهای اولیه بجای ساختارهای کوچک برای کارهای اولیه مانند Currency, DateTime, PhoneNumber 
  • استفاده از constant‌ها برای کد کردن اطلاعات مانند USER_ADMIN_ROLE = 1 
  • استفاده از constant‌های رشته‌ای به عنوان نام فیلدها در آرایه‌های داده 
4 - تعداد پارامترهای زیاد متد (Long parameter list): تعداد پارامترهای بیشتر از سه یا چهار عدد در یک متد. 
5 - توده داده (Data clumps): در بعضی موارد ممکن است از متغیرها به صورت دسته‌ای در مکان‌های مختلف کد استفاده شود. مانند استفاده از دسته‌ای از متغیرها برای نگه داشتن اطلاعات مربوط به اتصال پایگاه داده. این دسته‌ها باید به کلاس‌های حمل کننده داده خود تغییر کنند. 
 

بد استفاده کنندگان از شیء گرایی (Object orientation abusers)  


تکه کدهای این بخش در واقع بد استفاده کنندگاه یا ناقص استفاده کنندگان از اصول شیء گرایی هستند. در این دسته بندی موارد زیر وجود دارند:  

1 - گذاره‌های switch: وجود یک گذاره switch پیچیده یا دنباله‌ای از گذاره‌های if  
2 - درخواست رد شده (Refused request): در این حالت یک کلاس مجموعه محدودی از اعضای کلاس پدر خود را پیاده سازی می‌کند و باقی اعضای کلاس پدر یا بدون استفاده می‌مانند یا با استفاده از پرتاب کردن استثناء (Exception throwing) از کار انداخته می‌شوند. 
3 - فیلد موقتی (Temporary field): در این حالت متغیرها مقدار خود را در شرایط خاصی می‌گیرند و در بقیه شرایط خالی هستند. 
4 - کلاس هایی دقیقا مشابه در کارایی ولی متفاوت در مشخصات (Alternative Classes with Different Interfaces): دو کلاس دقیقا یک کار را انجام می‌دهند ولی نام اعضای آنها (متد و ...) متفاوت است. 

جلوگیری کنندگان از تغییر(Change preventers) 


این نشانه‌ها حاکی از این دارند زمانیکه تغییری در یک بخش کد نیاز باشد، در راستای آن حتما باید دیگر بخش‌های کد نیز به مقدار زیادی تغییر کنند. در این حالات اعمال تغییرات و نگهداری کد به شدت سخت خواهد شد. 
مواردی که در این دسته بندی قرار دارند به صورت زیر می‌باشند:  

1 - تغییر واگرا (Divergent change): این حالت زمانی اتفاق می‌افتد که برای اعمال یک تغییر به کلاس نیاز است متدهای زیادی را تغییر دهید. به طور مثال به ازای هر نوع محصولی که به محصولات شما اضافه می‌شود باید متدهای ذخیره، بازیابی، جستجو را تغییر دهید. 
2 - Shotgun Surgery: این حالت شباهت زیادی به تغییر واگرا دارد. تنها تفاوت آن این است که در این حالت شما به ازای هر تغییر نیاز است کلاس‌های زیادی را تغییر دهید. تغییر واگرا در بدنه یک کلاس اتفاق می‌افتد. 
3 - سلسله مراتب موازی ارث بری (Parallel inheritance hierarchy): این مورد یکی کمتر درک شده‌ترین موارد است. در این حالت زمانی که یک زیر کلاس برای یک کلاس ایجاد می‌کنید به ازای آن ناخودآگاه مجبور می‌شوید یک زیر کلاس برای کلاس دیگری ایجاد کنید. 

کدهای غیر ضروری (Dispensables) 


این دسته از کدها معمولا کدهایی هستند بی دلیل و بی استفاده. کدهایی که نبودنشان بهتر از بودنشان است! حذف کردن این کدها به خوانایی و قابلیت نگهداری کد خواهد افزود. بوهای بدی که در این دسته بندی قرار دارند به صورت زیر می‌باشند: 

1 - کامنت: یک متد، با مقادیر فراوانی از کامنت‌های توضیحی پر شده است. 
2 - کد تکراری: در این بوی بد، دو قطعه کد دقیقا مانند یکدیگر هستند. 
3 - کلاس داده (ِData class): کلاس‌هایی که تنها فیلدهای اطلاعاتی در آنها وجود دارند و متدهای خامی که جهت دریافت یا ذخیره اطلاعات در آنها استفاده می‌شوند. این کلاس‌های معمولا هیچ روال منطقی ای در خود ندارند. یکی از قدرت‌های شیء گرایی افزودن رفتار به کلاس‌ها در کنار اقلام اطلاعاتی موجود در آن است.  
4 - کلاس تنبل (Lazy class):  اگر کلاس کار چندانی که درخور نگهداری و توسعه باشد، انجام نمی‌دهد بهتر است از بین برود. 
5 - کد مرده (Dead code): متغیر، پارامتر، متد یا کلاسی که دیگر هیچ استفاده‌ای از آن متصور نیست و هیچ استفاده‌ای در حال حاضر از آن وجود ندارد. 
6 - کلی نگری بیش از اندازه (Speculative Generality): این الگو نیز کدهایی را شامل می‌شود که بلااستفاده هستند. ولی دلیل بلااستفاده بودن آن کلی نگری و دور اندیشی بدون دلیل است. معمولا کدهای تولیدی برای شرایط فعلی و پیش‌بینی آینده تولید می‌شوند. اگر این پیش‌بینی آینده به درستی و بر مبنای واقعیات انجام نشود، معمولا نتیجه کار، طراحی و پیاده سازی ای بی فایده و بلااستفاده خواهد بود. 

کدهایی بیش از اندازه وابسته به هم (Couplers) 


کدهایی که در این دسته قرار می‌گیرند معمولا یا خود درگیر یک وابستگی شدید هستند یا به ایجاد وابستگی بین کلاس‌ها کمک می‌کنند. بوی‌های بدی که در این دسته بندی قرار می‌گیرند به صورت زیر هستند: 

1- متد حسود (Feature envy): متدی که از اعضای یک شیء دیگر بیشتر از اعضای کلاس خود استفاده می‌کند! این اتفاق معمولا زمانی می‌افتد که فیلدهایی به یک "کلاس داده" منتقل می‌شوند. وقتی این اتفاق می‌افتد یکی از راه حل‌ها، انتقال روالهای استفاده کننده از فیلدها به "کلاس داده" مربوطه است.
2 - کلاس‌های بیش از اندازه صمیمی (Inappropriate Intimacy): کلاس‌ها از اعضای internal یکدیگر بیش از اندازه استفاده می‌کنند. کلاس‌های خوب کلاس‌هایی هستند که کمترین اطلاعی را از وضعیت داخلی یکدیگر دارند. 
3 - کتابخانه‌های ناقص (Incomplete Library Class): زمانیکه کتابخانه‌ای آماده می‌شود، بالاخره روزی می‌رسد که این کتابخانه نیازهای پروژه را رفع نمی‌کند و نیاز به توسعه خواهد داشت. ولی از آنجایی که کتابخانه‌ها به صورت فقط خواندنی در اختیار پروژه‌ها قرار می‌گیرند، در صورتیکه توسعه دهنده اصلی آن از توسعه کتابخانه سر باز بزند، مشکلاتی بوجود خواهد آمد. 
4 - زنجیره فراخوانی‌ها (Message chain): زمانیکه یک متد در بدنه خود پیامی به شیء دیگری می‌فرستد که آن شیء نیز به خودی خود پیامی به شیء دیگری می‌فرستد (و الی آخر) یک زنجیره فراخوانی بوجود آمده است. در این روش بیرونی‌ترین استفاده کننده از متد در واقع وابسته به یک زنجیره‌ای از فراخوانی‌ها است که تغییر در هر قدمی از آن باعث خرابی خواهد شد. 
5 - دلال (Middle man): اگر کلاسی تنها کاری که انجام می‌دهد انتقال فراخوانی به کلاس دیگری است، دیگر نیازی به این کلاس وجود نخواهد داشت.

اطلاع از الگوهای بد کد نویسی به همان اندازه اطلاع از الگوهای خوب کد نویسی در کیفیت محصول تولیدی اثر مثبت خواهند داشت. یادگیری طبقه بندی شده این الگوها کار را برای استفاده روزمره از آنها آسان‌تر خواهد کرد.
اشتراک‌ها
اندازه گیری دما، مختصات جغرافیایی، لرزه یا تکانه و تنظیم نمودن هشدار دهنده توسط NET Micro Framework
اگر شما یک دات نت developer هستید، می‌توانید بدون داشتن دانش زبانهای سطح پایین مانند Assembly روی دستگاههای سازگار با این فریم ورک کد بنویسید و شرایط محیطی پیرامونتان را مانیتور نمایید. این پلت فرم رایگان و با سورس باز است و فرصت طلایی را در اختیار توسعه دهندگان جهت برنامه نویسی روی میکرو پروسسورها "با حداقل تنظیمات پیچیده" قرار می‌دهد. 
شما می‌توانید با syntax (دستورات) دات نتی روی بوردهایی با قیمت زیر 100$ براحتی در محیط دات نت برنامه بنویسید و به همان راحتی باگ گیری نمایید. بعنوان مثال برنامه GPS را روی بورد بنویسید و آن را به شی نصب کرده و از مختصات جغرافیایی در هر لحظه آگاه شوید. و یا دمای پیرامونتان را هر از X ثانیه توسط Thread اندازهگیری نمایید و چنانچه بالاتر و یا پایین‌تر از مقدار مورد انتظارتان بود هشدار دهنده را فعال نمایید.

Measuring temperature, GPS, vibration and set alarms by NET Micro Framework

اندازه گیری دما، مختصات جغرافیایی، لرزه یا تکانه و تنظیم نمودن هشدار دهنده توسط NET Micro Framework
نظرات مطالب
ایجاد یک راهنما برای کنترل تعداد کاراکترهای نوشته شده در یک خط
اگر از Rider استفاده کنید، حتی قابلیت انتقال خودکار قسمت‌های طولانی شده‌ی کد را به سطر بعدی، دارا است. به علاوه می‌شود این قابلیت را به ازای هربار ذخیره سازی تغییرات هم فعال کرد.
مطالب
ذخیره سازی اطلاعات در مرورگر توسط برنامه‌های Angular
تمام برنامه‌های وب، از داده‌ها استفاده می‌کنند و امکان ذخیره سازی، به اشتراک گذاری و بازیابی آن‌ها حتی زمانیکه اتصال به شبکه برقرار نیست، بسیار حائز اهمیت است. به همین جهت مرورگرهای امروزی نیز به همراه قابلیت‌هایی هستند تا این امر را ساده‌تر کنند. این محل ذخیره سازی، درون مرورگر کاربر بوده و دسترسی به آن نیز بسیار سریع است. همچنین امکان دسترسی به آن در حالت آفلاین و بدون اتصال به شبکه نیز میسر است. البته باید دقت داشت که بسته به نوع ذخیره سازی اطلاعات محلی انتخاب شده، حداکثر 10 مگابایت بیشتر در اختیار برنامه قرار نمی‌گیرد. همچنین دسترسی این اطلاعات وابسته‌است به ماشین و وسیله‌ی مورد استفاده. برای مثال اگر کاربر از طریق سیستم و ماشین دیگری برنامه را مرور کند، دیگر دسترسی به اطلاعات محلی قبلی خود نخواهد داشت و یا اگر کاربر کش مرورگر را خالی کند، این اطلاعات نیز حذف می‌شوند.


حالت‌های مختلف ذخیره سازی اطلاعات در مرورگر کاربر

Web Storage و یا Client-side storage در دو حالت کلی session storage و local storage قابل دسترسی است:
الف) session storage
در این حالت اطلاعات ذخیره شده‌ی در session storage، پس از بسته شدن مرورگر، به صورت خودکار حذف خواهند شد.

ب) local storage
اطلاعات ذخیره شده‌ی در local storage پس از بسته شدن مرورگر نیز باقی مانده و قابل دسترسی و بازیابی مجدد هستند. تاریخ انقضای آن‌ها صرفا بر اساس خالی شدن دستی کش مرورگر توسط کاربر و یا حذف دستی اطلاعات آن توسط کدهای برنامه تعیین می‌شود.

هر دو حالت فوق به صورت ایزوله ارائه می‌شوند؛ با محدودیت حجم 10 مگابایت (جمع حجم نهایی هر دو حالت با هم، محدود به 10 مگابایت است). به این معنا که برنامه‌های هر دومین، تنها به محل ذخیره سازی خاص همان دومین دسترسی خواهند داشت.
همچنین API دسترسی به آن‌ها synchronous است و کار کردن با آن‌ها ساده‌است.

البته Client-side storage به دو مورد فوق خلاصه نمی‌شود و شامل File Storage ،WebSQL ،IndexedDB و کوکی‌های مرورگر نیز هست.
- File Storage هنوز مراحل آزمایشی خودش را طی می‌کند و مناسب برنامه‌های دنیای واقعی نیست.
- WebSQL قرار بود بر اساس بانک اطلاعاتی معروف SQLite ارائه شود؛ اما W3C در سال 2010 این استاندارد را منسوخ شده اعلام کرد و با IndexedDB جایگزین شد. دسترسی به آن async است و می‌تواند موضوع بحثی مجزا باشد.
- کوکی‌های مرورگرها نیز یکی دیگر از روش‌های ذخیره سازی اطلاعات در مرورگرها هستند و تنها به ذخیره سازی حداکثر 4096 بایت اطلاعات محدود هستند. کوکی‌ها نیز همانند local storage پس از بسته شدن مرورگر باقی می‌مانند؛ اما برخلاف آن، دارای تاریخ انقضاء و همچنین قابلیت ارسال بین دومین‌ها را نیز دارا می‌باشند. اگر تاریخ انقضای یک کوکی تعیین نشود، همانند session storage، در پایان کار مرورگر و بسته شدن آن، حذف خواهد شد.


تهیه یک سرویس Angular برای کار با Web Storage

جهت کپسوله سازی نحوه‌ی کار با session storage و local storage می‌توان سرویسی را برای این‌کار تهیه کرد:
import { Injectable } from "@angular/core";

@Injectable()
export class BrowserStorageService {

  getSession(key: string): any {
    const data = window.sessionStorage.getItem(key);
    return JSON.parse(data);
  }

  setSession(key: string, value: any): void {
    const data = value === undefined ? null : JSON.stringify(value);
    window.sessionStorage.setItem(key, data);
  }

  removeSession(key: string): void {
    window.sessionStorage.removeItem(key);
  }

  removeAllSessions(): void {
    for (const key in window.sessionStorage) {
      if (window.sessionStorage.hasOwnProperty(key)) {
        this.removeSession(key);
      }
    }
  }

  getLocal(key: string): any {
    const data = window.localStorage.getItem(key);
    return JSON.parse(data);
  }

  setLocal(key: string, value: any): void {
    const data = value === undefined ? null : JSON.stringify(value);
    window.localStorage.setItem(key, data);
  }

  removeLocal(key: string): void {
    window.localStorage.removeItem(key);
  }

  removeAllLocals(): void {
    for (const key in window.localStorage) {
      if (window.localStorage.hasOwnProperty(key)) {
        this.removeLocal(key);
      }
    }
  }
}
دسترسی به local storage از طریق شیء window.localStorage انجام می‌شود و کار با آن در برنامه‌های Angular، نیاز به وابستگی خاص دیگری ندارد. این مورد برای کار با session storage از طریق شیء window.sessionStorage صورت می‌گیرد. هر دو حالت، دارای متدهای setItem برای ذخیره سازی اطلاعات، getItem برای دریافت اطلاعات، بر اساس کلیدی مشخص و removeItem برای حذف اطلاعات کلیدی معلوم، هستند.
در حالت setItem اطلاعاتی را که مرورگرها ذخیره می‌کنند باید رشته‌ای باشد. به همین جهت توسط متد JSON.stringify می‌توان یک شیء را تبدیل به رشته کرد و ذخیره نمود و در حالت getItem توسط متد JSON.parse، می‌توان این رشته را مجددا به همان شیء پیشین خود تبدیل کرد و بازگشت داد.


محل صحیح تعریف BrowserStorageService

همانطور که در مطلب «سازماندهی برنامه‌های Angular توسط ماژول‌ها» بررسی شد، محل صحیح تعریف این سرویس سراسری مشترک در بین کامپوننت‌ها و ماژول‌های برنامه، در CoreModule و پوشه‌ی src\app\core\browser-storage.service.ts است:
import { BrowserStorageService } from "./browser-storage.service";
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { RouterModule } from "@angular/router";

@NgModule({
  imports: [CommonModule, RouterModule],
  exports: [], // components that are used in app.component.ts will be listed here.
  declarations: [], // components that are used in app.component.ts will be listed here.
  providers: [BrowserStorageService] // singleton services of the whole app will be listed here.
})
export class CoreModule { };

و CoreModule نیز به AppModule اضافه می‌شود:
import { CoreModule } from "./core/core.module";

@NgModule({
  imports:      [
//...
    CoreModule,
//...
    RouterModule.forRoot(appRoutes)
  ],
//...
})
export class AppModule { }

بنابراین یکی دیگر از روش‌های به اشتراک گذاری اطلاعات در بین قسمت‌های مختلف برنامه، ذخیره سازی آن‌ها در session/local storage و سپس بازیابی آن‌ها بر اساس کلیدهای مشخص آن‌ها است.


مثالی از نحوه‌ی کاربرد BrowserStorageService

برای آزمایش سرویس تهیه شده، از کامپوننت و قالب ذیل استفاده خواهیم کرد. در اینجا سرویس BrowserStorageService به سازنده‌ی کلاس تزریق شده‌است و سپس دو حالت session storage و local storage مورد بررسی قرار گرفته‌اند:
import { BrowserStorageService } from "./../../core/browser-storage.service";
import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-browser-storage-sample-test",
  templateUrl: "./browser-storage-sample-test.component.html",
  styleUrls: ["./browser-storage-sample-test.component.css"]
})
export class BrowserStorageSampleTestComponent implements OnInit {

  fromSessionStorage = "";
  fromLocalStorage = ""

  sessionStorageKey = "sessionStorageKey1";
  localStorageKey = "localStorageKey1"

  constructor(private browserStorage: BrowserStorageService) { }

  ngOnInit() {
  }

  sessionStorageSetItem() {
    this.browserStorage.setSession(this.sessionStorageKey, "Val1");
  }

  sessionStorageGetItem() {
    this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey);
  }

  localStorageSetItem() {
    this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 });
  }

  localStorageGetItem() {
    this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey));
  }
}
به همراه قالب:
<h1>Browser storage sample</h1>
<div class="panel">
  <button class="btn btn-primary" (click)="sessionStorageSetItem()" type="button">sessionStorage -> Set Item</button>
  <button class="btn btn-success" (click)="sessionStorageGetItem()" type="button">sessionStorage -> Get Item</button>
  <div class="alert alert-info" *ngIf="fromSessionStorage">
    {{fromSessionStorage}}
  </div>
</div>

<div class="panel">
  <button class="btn btn-warning" (click)="localStorageSetItem()" type="button">localStorage -> Set Item</button>
  <button class="btn btn-success" (click)="localStorageGetItem()" type="button">localStorage -> Get Item</button>
  <div class="alert alert-info" *ngIf="fromLocalStorage">
    {{fromLocalStorage}}
  </div>
</div>

در این حالت اگر برنامه را اجرا کنیم، یک چنین خروجی قابل مشاهده خواهد بود:


و اگر به برگه‌ی Application کنسول ابزارهای توسعه دهنده‌های مرورگرها نیز مراجعه کنیم، این مقادیر ثبت شده را در دو حالت استفاده‌ی از session storage و local storage، می‌توان مشاهده کرد:



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
هدف از زیر ساخت بومی سازی در ASP.NET Core، حذف عبارات و رشته‌های درج شده‌ی در کلاس‌ها و ویووهای مختلف برنامه و انتقال آن‌ها به فایل‌های منبع resx است و سپس استفاده‌ی از آن‌ها توسط تزریق وابستگی‌ها. به این ترتیب می‌توان بر اساس نوع فرهنگ درخواستی کاربر جاری، رشته‌های درج شده را به صورت پویا، در زمان اجرای برنامه، بر اساس ترجمه‌های آن‌ها به کاربر نمایش داد.


نحوه‌ی تعیین فرهنگ ترد جاری در ASP.NET Core

در نگارش‌های پیشین ASP.NET، برای تعیین فرهنگ ترد جاری، از یکی از دو روش ذیل استفاده می‌شود:
الف) افزودن مدخل بومی سازی به فایل web.config
<system.web>
    <globalization uiCulture="fa-IR" culture="fa-IR" />
</system.web>
ب) و یا تعیین فرهنگ ترد با کدنویسی مستقیم در فایل global.asax
protected void Application_BeginRequest()
{
   Thread.CurrentThread.CurrentCulture = new CultureInfo("fa-IR");
   Thread.CurrentThread.CurrentUICulture = new CultureInfo("fa-IR");
}
در ASP.NET Core با حذف شدن System.Web و همچنین فایل global.asax، برای تعیین فرهنگ پیش فرض ترد جاری، به همراه فرهنگ‌هایی که برنامه از آن‌ها پشتیبانی می‌کند، به صورت ذیل عمل می‌شود:
public void Configure(IApplicationBuilder app)
{
    app.UseRequestLocalization(new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture(new CultureInfo("fa-IR")),
        SupportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fa-IR")
        },
        SupportedUICultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fa-IR")
        }
    });
در اینجا با مراجعه به کلاس آغازین برنامه و افزودن تنظیمات میان افزار RequestLocalization، می‌توان فرهنگ پیش فرض درخواست جاری و یا فرهنگ‌های پشتیبانی شده را مشخص کرد.
- تنظیمات SupportedCultures بر روی نمایش تاریخ، ساعت و واحد پولی تاثیر دارند. همچنین می‌توانند بر روی نحوه‌ی مقایسه‌ی حروف و مرتب سازی آن‌ها تاثیر داشته باشند.
- تنظیمات SupportedUICultures مشخص می‌کنند که کدامیک از فایل‌های resx برنامه که مداخل ترجمه‌های آن‌را به زبان‌های مختلف مشخص می‌کنند، باید بارگذاری شوند.
- تنظیم DefaultRequestCulture در صورت مشخص نشدن فرهنگ ترد جاری مورد استفاده قرار می‌گیرد.

یک مثال: هر ترد در دات نت دارای اشیاء CurrentCulture و CurrentUICulture است. اگر فرهنگ ترد جاری به en-US تنظیم شده باشد، متد DateTime.Now.ToLongDateString، خروجی نمونه Thursday, February 18, 2016 را نمایش می‌دهد.


زمانیکه میان افزار RequestLocalization فعال می‌شود، سه تامین کننده‌ی پیش فرض (مقدار‌های پیش فرض خاصیت RequestCultureProviders شیء RequestLocalizationOptions فوق)، جهت مشخص ساختن فرهنگ ترد جاری بکار گرفته خواهند شد:
الف) از طریق کوئری استرینگ با فعال سازی QueryStringRequestCultureProvider
http://localhost:5000/?culture=es-MX&ui-culture=es-MX
http://localhost:5000/?culture=es-MX
برای مثال در اینجا QueryStringRequestCultureProvider به دنبال کوئری استرینگ‌های culture و یا ui-culture گشته و با رسیدن به es-MX، فرهنگ جاری را به اسپانیایی مکزیکی تنظیم می‌کند. در این حالت اگر فقط culture ذکر شود، ui-culture نیز به همان مقدار تنظیم خواهد شد.
ب) از طریق نام کوکی با فعال سازی CookieRequestCultureProvider
CookieRequestCultureProvider کوکی ویژه‌ای را با نام پیش فرض AspNetCore.Culture. ایجاد می‌کند. این کوکی برای ردیابی اطلاعات بومی سازی انتخابی کاربر بکار می‌رود. برای مثال اگر به مقدار ذیل تنظیم شود:
 c='en-UK'|uic='en-US'
c آن به معنای culture و uic آن به معنای ui-culture خواهد بود.
ج) از طریق هدر مخصوص Accept-Language با فعال سازی AcceptLanguageHeaderRequestCultureProvider که می‌تواند به همراه درخواست HTTP ارسال شود.

اگر تمام این حالت‌ها تنظیم نشده بودند، آنگاه از مقدارDefaultRequestCulture  استفاده می‌شود. برای مثال اگر مرورگر به صورت پیش فرض هدر Accept-Language را en-US ارسال می‌کند :


دیگر کار به پردازش مقدارDefaultRequestCulture  نخواهد رسید.

اکنون اگر علاقمند بودید تا به کاربر امکان انتخاب زبانی را بدهید، یک چنین اکشن متدی را طراحی کنید:
public IActionResult SetFaLanguage()
{
    Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(new CultureInfo("fa-IR"))),
        new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
    );
 
    return RedirectToAction("GetTitle");
}
این اکشن متد بر اساس تامین کننده‌ی کوکی ردیابی زبان انتخاب شده‌ی توسط کاربر و یا CookieRequestCultureProvider کار می‌کند و توسط آن، فرهنگ جاری برنامه به زبان فارسی تنظیم می‌شود. هرگاه که این اکشن متد فراخوانی شود، کوکی AspNetCore.Culture. به مقدار c=fa-IR|uic=fa-IR تنظیم می‌شود:


از اینجا به بعد است که اگر نام کنترلر شما TestLocalController باشد، فایل منبع متناظر با آن یعنی Controllers.TestLocalController.fa.resx، به صورت خودکار بارگذاری و پردازش خواهد شد. در غیر اینصورت فایل نمونه‌ی ختم شده‌ی به en.resx پردازش می‌شود؛ چون این زبان به صورت پیش فرض در هدر Accept-Language قید شده‌است.


آماده سازی برنامه برای کار با فایل‌های منبع زبان‌های مختلف

ابتدا پوشه‌ی جدیدی را به نام Resources به ریشه‌ی پروژه اضافه کنید. سپس به کلاس آغازین برنامه مراجعه کرده و محل یافت شدن این پوشه را معرفی کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddLocalization(options => options.ResourcesPath = "Resources");
    services.AddMvc()
        .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
        .AddDataAnnotationsLocalization();
در اینجا سرویس جدید Localization، به لیست سرویس‌های ثبت شده‌ی در IoC Container اضافه می‌شود. همچنین توسط خاصیت  ResourcesPath  آن مشخص شده‌است که فایل‌های resx را باید از کجا دریافت کند.
به علاوه به سرویس ASP.NET MVC، تنظیمات بومی سازی Viewها و DataAnnotations نیز اضافه شده‌اند. تنظیم suffix به معنای  view file suffix و یا مثلا fr در نام فایل Index.fr.cshtml است.


نحوه‌ی تعریف و پوشه بندی فایل‌های منبع زبان‌های مختلف

تا اینجا پوشه‌ی جدید Resources را به پروژه اضافه، معرفی و سرویس‌های مرتبط را نیز فعال کردیم. پس از آن نوبت به افزودن فایل‌های resx است. برای این منظور بر روی پوشه‌ی منابع کلیک راست کرده و گزینه‌ی add->new item را انتخاب کنید.


در اینجا با جستجوی resource، می‌توان فایل resx جدیدی را به پروژه اضافه کرد؛ اما ... انتخاب نام آن باید بر اساس نکات ذیل باشد:
الف) برای کنترلرها یکی از دو مسیر / دار و یا نقطه دار جستجو می‌شوند:
Resources/Controllers.HomeController.fr.resx
Resources/Controllers/HomeController.fr.resx

در اینجا fr ذکر شده، همان LanguageViewLocationExpanderFormat.Suffix است که پیشتر بحث شد. قسمت ابتدایی Controllers همیشه ثابت است (یا به صورت نام یک پوشه و یا به عنوان قسمت اول نام فایل). سپس نام کلاس کنترلر به همراه نام فرهنگ مدنظر باید ذکر شوند. قسمت نام پوشه‌ی Resources را نیز به services.AddLocalization معرفی کرده‌ایم.

ب) برای Viewها نیز همان حالت‌های / دار و یا نقطه دار بررسی می‌شوند:
Resources/Views.Home.About.fr.resx
Resources/Views/Home/About.fr.resx


برای تمام فایل‌ها و کلاس‌ها می‌توان فایل منبع ایجاد کرد

در این نگارش از ASP.NET، در حالت کلی، نام یک فایل منبع، همان نام کامل کلاس آن است؛ منهای فضای نام آن (اگر این فایل منبع در همان اسمبلی قرار گیرد). برای مثال اگر می‌خواهید برای کلاس Startup برنامه، فایل منبعی را درست کنید و نام کامل آن با درنظر گرفتن فضای نام، معادل LocalizationWebsite.Web.Startup است، ابتدای فضای نام آن‌را حذف کنید و سپس آن‌را ختم به fa.resx کنید؛ مثلا Startup.fa.resx
اگر محل واقع شدن فایل‌های resx در همان اسمبلی اصلی پروژه باشند، نیازی به ذکر فضای نام پیش فرض پروژه نیست. برای مثال اگر فضای نام پیش فرض پروژه‌ی وب جاری MyLocalizationWebsite.Web است، بجای نام فایل MyLocalizationWebsite.Web.Controllers.HomeController.fr.resx می‌توانید به صورت خلاصه بنویسید Controllers.HomeController.fr.resx. در غیراینصورت (استفاده از اسمبلی‌های دیگر)، ذکر کامل فضای نام مرتبط هم الزامی است.


چند نکته:
- اگر ResourcesPath را در services.AddLocalization معرفی نکنید، مسیر پیش فرض یافتن فایل‌های resx مربوط به کنترلرها، پوشه‌ی ریشه‌ی پروژه است و برای Viewها، همان پوشه‌ی محل واقع شدن View متناظر خواهد بود.
- اینکه کدام فایل منبع در برنامه بارگذاری می‌شود، دقیقا مرتبط است با فرهنگ ترد جاری و این فرهنگ به صورت پیش فرض en-US است (چون همواره در هدر Accept-Language ارسالی توسط مرورگر وجود دارد). برای تغییر آن، از نکته‌ی اکشن متد public IActionResult SetFaLanguage ابتدای بحث استفاده کنید (در غیراینصورت در آزمایشات خود شاهد بارگذاری فایل‌های منبع دیگری بجز en.resx‌ها نخواهید بود).
- فایل‌های منبع را به صورت کامپایل شده در پوشه‌ی bin برنامه خواهید یافت:



خواندن اطلاعات منابع در کنترلرهای برنامه

فرض کنید کنترلری را به نام TestLocalController ایجاد کرده‌ایم. بنابراین فایل منبع فارسی متناظر با آن Controllers.TestLocalController.fa.resx خواهد بود؛ با این محتوای نمونه:


محتوای این کنترلر نیز به صورت ذیل است:
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.Extensions.Localization;
 
namespace Core1RtmEmptyTest.Controllers
{
    public class TestLocalController : Controller
    {
        private readonly IStringLocalizer<TestLocalController> _stringLocalizer;
        private readonly IHtmlLocalizer<TestLocalController> _htmlLocalizer;
 
        public TestLocalController(
            IStringLocalizer<TestLocalController> stringLocalizer,
            IHtmlLocalizer<TestLocalController> htmlLocalizer)
        {
            _stringLocalizer = stringLocalizer;
            _htmlLocalizer = htmlLocalizer;
        }
 
        public IActionResult Index()
        {
            var name = "DNT";
            var message = _htmlLocalizer["<b>Hello</b><i> {0}</i>", name];
            ViewData["Message"] = message;
            return View();
        }
 
        [HttpGet]
        public string GetTitle()
        {
            var about = _stringLocalizer["About Title"];
            return about;
        }
 
        public IActionResult SetFaLanguage()
        {
            Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(new CultureInfo("fa-IR"))),
                new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
            );
 
            return RedirectToAction("GetTitle");
        }
    }
}
در اینجا نحوه‌ی دسترسی به فایل‌های منبع را در کنترلرها مشاهده می‌کنید. سرویس IStringLocalizer برای خواندن key/valueهای معمولی طراحی شده‌است و سرویس IHtmlLocalizer برای خواندن key/valueهای تگ دار، بکار می‌رود. علت تنظیم شدن پارامتر جنریک آن‌ها به نام کنترلر جاری این است که این سرویس‌ها بدانند دقیقا چه نوعی را قرار است بارگذاری کنند و دقیقا باید به دنبال کدام فایل بگردند. این سرویس‌ها یک کلید را می‌گیرند و یک خروجی و مقدار را باز می‌گردانند.
اگر برنامه را در حالت معمولی اجرا کنید و سپس آدرس http://localhost:7742/testlocal/gettitle را درخواست کنید، عبارت About Title را مشاهده می‌کنید؛ به دو علت:
الف) هنوز فرهنگ پیش فرض ترد جاری همان en-US است که توسط مرورگر ارسال شده‌است.
ب) چون فایل resx متناظر با فرهنگ پیش فرض ترد جاری یافت نشده‌است، مقدار همان کلید درخواستی بازگشت داده می‌شود؛ یعنی همان About Title.

برای رفع این مشکل آدرس http://localhost:7742/testlocal/SetFaLanguage را درخواست کنید. به این صورت با تنظیم کوکی ردیابی فرهنگ ترد جاری به زبان فارسی، خروجی GetTile این‌بار «درباره» خواهد بود.


خواندن اطلاعات منابع در Viewهای برنامه

فرض کنید فایل Views.TestLocal.Index.fa.resx (فایل منبع کنترلر TestLocal و ویوو Index آن به زبان فارسی) دقیقا همان محتوای فایل Controllers.TestLocalController.fa.resx فوق را دارد (اگر نام پوشه‌ی Views را تغییر داده‌اید، قسمت ابتدایی نام فایل Views را هم باید تغییر دهید). برای دسترسی به اطلاعات آن در یک ویوو، می‌توان از سرویس IViewLocalizer  به نحو ذیل استفاده کرد:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
 
@{
}
Message @ViewData["Message"]
<br/>
@Localizer["<b>Hello</b><i> {0}</i>", "DNT"]
<br/>
@Localizer["About Title"]
در اینجا ViewData، از همان اطلاعات اکشن متد Index استفاده می‌کند.
Localizer از طریق تزریق سرویس IViewLocalizer  به View برنامه تامین می‌شود. این سرویس در پشت صحنه از همان IHtmlLocalizer استفاده می‌کند و در حین استفاده‌ی از آن، اطلاعات تگ‌ها انکد (encoded) نخواهند شد (به همین جهت برای کار با کلیدها و مقادیر تگ‌دار توصیه می‌شود).


استفاده از اطلاعات منابع در DataAnnotations

قسمت اول فعال سازی بومی سازی DataAnnotations با ذکر AddDataAnnotationsLocalization در متد ConfigureServices، در ابتدای بحث انجام شد و همانطور که پیشتر نیز عنوان گردید، در این نگارش از ASP.NET، برای تمام کلاس‌های برنامه می‌توان فایل منبع ایجاد کرد. برای مثال اگر کلاس RegisterViewModel در فضای نام ViewModels.Account قرار گرفته‌است، نام فایل منبع آن یکی از دو حالت / دار و یا نقطه دار ذیل می‌تواند باشد:
Resources/ViewModels.Account.RegisterViewModel.fr.resx
Resources/ViewModels/Account/RegisterViewModel.fr.resx

محتوای این کلاس را در ذیل مشاهده می‌کنید:
using System.ComponentModel.DataAnnotations;
 
namespace Core1RtmEmptyTest.ViewModels.Account
{
    public class RegisterViewModel
    {
        [Required(ErrorMessage = "EmailReq")]
        [EmailAddress(ErrorMessage = "EmailType")]
        [Display(Name = "Email")]
        public string Email { get; set; }
    }
}
در این حالت مقداری که برای ErrorMessage ذکر می‌شود، کلیدی است که باید در فایل منبع جستجو شود:


یک نکته: هیچ الزامی ندارد که کلیدها را به این شکل وارد کنید. از این جهت که اگر این کلید در فایل منبع یافت نشد و یا فرهنگ ترد جاری با فایل‌های منبع مهیا تطابقی نداشت، عبارتی را که کاربر مشاهده می‌کند، دقیقا معادل «EmailReq» خواهد بود. بنابراین در اینجا می‌توانید کلید را به صورت کامل، مثلا مساوی «The Email field is required» وارد کنید و همین عبارت را به عنوان کلید در فایل منبع ذکر کرده و مقدار آن‌را مساوی ترجمه‌ی آن قرار دهید. این نکته در تمام حالات کار با کنترلرها و ویووها نیز صادق است.


استفاده از یک منبع اشتراکی

اگر می‌خواهید تعدادی از منابع را در همه‌جا در اختیار داشته باشید، روش کار به این صورت است:
الف) یک کلاس خالی را به نام SharedResource دقیقا با فرمت ذیل در پوشه‌ی Resources ایجاد کنید:
// Dummy class to group shared resources
namespace Core1RtmEmptyTest
{
   public class SharedResource
   {
   }
}
ب) اکنون فایل‌های منبع خود را در پوشه‌ی Resources، دقیقا با این نام‌های خاص ایجاد کنید:
SharedResource.fa.resx
SharedResource.en-US.resx
و امثال آن

ج) برای استفاده‌ی از این منبع اشتراکی در کلاس‌های مختلف برنامه تنها کافی است در حین تزریق وابستگی‌ها، نوع آرگومان جنریک IStringLocalizer را به SharedResource تنظیم کنید:
 IStringLocalizer<SharedResource> sharedLocalizer
و یا حتی در ویووهای برنامه نیز می‌توان از آن استفاده کرد:
 @inject IHtmlLocalizer<SharedResource> SharedLocalizer
مطالب
بررسی مشکلات AngularJS 1.x
از اولین مقاله‌ای که در مورد AngularJS در این سایت منتشر کردم، بیش از دو سال می‌گذرد. در آن زمان فقط از این فریمورک تعریف و تمجید کردم؛ اما بد نیست بعد از چند تجربه‌ی کاری دلایل تنفری را که نسبت به آن پیدا کرده ام، نیز بیان کنم.
اگر عبارت why I hate angularjs را در گوگل جستجو کنید، می‌بینید که فقط من این عقیده را پیدا نکرده‌ام و افراد دیگری نیز هستند که مثل من فکر می‌کنند و حتی از لحاظ فنی AngularJS را به چالش کشیده‌اند. برای مثال سایت I hate angular بیشتر مقالاتی را که ضد AngularJS هستند، گردآوری کرده است و برای بررسی مشکلات Angular می‌تواند شروع خوبی باشد.
البته قصد ندارم که از نظر فنی Angular را نقد کنم؛ فقط قصد به اشتراک گذاری یک سری از مشکلات توسعه‌ی Single Page Application‌ها را با استفاده از فریمورک Angular، دارم و این را در نظر داشته باشید که بعضی از این مشکلات در هنگام توسعه SPA‌ها با فریمورک‌هایی از این دست، گریبان‌گیر شما می‌شوند و الزاما ربطی به AngularJS ندارند.

سازگار نبودن افزونه‌های jQuery با Angular

برنامه‌های واقعی فقط از تعدادی ng-repeat تشکیل نشده‌اند که ما از دیدن آن‌ها ذوق زده شویم. خواسته یا ناخواسته مجبوریم در برنامه‌های وب خودمان از افزونه‌های محبوب جی‌کوئری نیز استفاده کنیم. خوب، خیلی هم خوب! چندین راه حل پیش روی ماست:

روش اول - نادیده گرفتن angular
انگار نه انگار که از angular استفاده می‌کنیم و افزونه مورد نظر را بدون در نظر گرفتن وجود angular، کاملا عادی فراخوانی کنیم.
نتیجه: ممکن است بعضی  وقت‌ها جواب بدهد، ولی اکثر مواقع، نتیجه عجیب غریب است و خطا‌ها قابل فهم نیستند و توانایی اشکال زدایی آن‌ها را نخواهید داشت. دلیلش هم مشخص است؛ چون Angular فازی به نام کامپایل و اصطلاحا context مربوط به خودش را دارد و فراخوانی افزونه مورد نظر، خارج از context انگولار رخ می‌دهد و انگولار از وجود این افزونه بی خبر است. حال ممکن است به طور اتفاقی، فراخوانی افزونه قبل، مابین و یا حتی بعد از فاز کامپایل انگولار رخ دهد. باز هم فرض کنید که بر حسب اتفاق همه چیز خوب پیش رفت، اما اکنون سایر قابلیت‌های خوب انگولار مثل ng- model و model binding آن در دسترس نیستند و در آخر به این نتیجه می‌رسید که پس چرا دارم از انگولار استفاده می‌کنم.

روش دوم - استفاده از directive‌های محصور کننده
راه اصولی برای استفاده از افزونه‌های جی‌کوئری در AngularJS، استفاده از directiveهای تهیه شده برای آن افزونه است. اگر خوش شانس باشید، معمولا برای افرونه‌های معروف، directive انگولاری آن نیز تهیه شده است. اما این همه‌ی داستان نیست؛ فرض کنید که از کتابخانه jQuery file upload، برای آپلود فایل می‌خواهید استفاده کنید. خوشبختانه directive  انگولاری نیز برای آن تهیه شده است و مستندات استفاده از آن هم، تنها مثالی هست که برای آن فراهم شده است. اما فرض کنید که می‌خواهید مانند مثال استفاده از آن در jQuery، یک file input که کاربر تنها  بتواند یک فایل را از طریق کشیدن و رها کردن آپلود کند، با استفاده از Directive انگولاری آن پیاده سازی کنید. اما کار با این directive، به آسانی مثال جی‌کوئری آن نیست. یک‌کم که جلوتر بروید می‌بینید که این directive گنگ طراحی شده است.  البته بیشتر directive هایی که اصطلاحا wrapper برای افزونه‌های جی‌کوئری هستند این مشکل را دارند و کار با آن‌ها چندان لذت بخش نیست و باید ساعت‌ها با آنها کلنجار رفت تا به نتیجه‌ی دلخواه رسید و همه‌ی این‌ها را در نظر بگیرید که اگر با api‌های jQuery آن کار می‌کردید، دیگر این مشکلات را نداشتید. قبلا نیز یک نمونه‌ی دیگر از مشکلات استفاده از این گونه directive‌های محصور کننده را تحت مقاله ای با عنوان استفاده از افزونه isotope در انگولار به اشتراک گذاشتم.

روش سوم - استفاده از directive هایی که به صورت native با انگولار نوشته شده‌اند
اما چرا به هنگام استفاده از directive‌های محصور کننده افرونه‌های جی‌کوئری، با مشکلات زیادی روبرو می‌شویم؟ دلیلش این است که انگولار می‌گوید بهتر است این افزونه‌ها با استفاده از خود angular بازنویسی شوند. برای مثال برای آپلود فایل می‌توان از کتابخانه‌ی با کیفیت ng-file-upload که هیچ وابستگی به jQuery ندارد استفاده کرد. اما آیا واقعا برای تمامی افزونه‌های جی‌کوئری معادلی برای AngularJs آن با همان کیفیت تهیه شده است؟ جواب مطمئنا خیر است. برای مثال در حالی که برای datagrid افزونه‌های بی شماری برای جی‌کوئری تهیه شده است، اما برای angular تنها یکی دو تا directive با کیفیت تهیه شده‌است که نه تنها قابلیت رقابت با معادل‌های jQuery شان را ندارند، آنچنان نیز stable نیستند و در مستندات خودشان هشدار می‌دهند که فلان ویژگی در حال تست هست و هنوز پایدار نیست.

روش چهارم – نوشتن directive توسط خودتان
به عنوان آخرین راه حل باید خودتان دست به کار شده و برای افزونه مورد نظرتان directive بنویسید. اما نوشتن directive برای افزونه‌های پیچیده‌ی جی‌کوئری به سادگی مثال‌های آموزشی AngularJS همانند چگونگی نوشتن directive برای jQueryUI Datepicker  نیست. اگر کدهای directive‌های نوشته شده برای افزونه‌های پیچیده را بررسی کنید، کدهایی را می‌بینید که برای شما منطقی نیست. برای مثال ممکن است با تعداد زیادی setTimeOut مواجه شوید که احتمالا با نحوه‌ی کامپایل HTML توسط انگولار مرتبط است. در کل باید بدانید که نوشتن directive برای تعداد زیادی از افزونه‌ها کار راحتی نیست و احتمالش هست که قید این را کار نیز بزنید.

پس اگر قصد توسعه SPA با هر فریمورکی مثل angular را داشته باشید، این را در نظر داشته باشید که دیر یا زود هنگام استفاده از افزونه‌های جی‌کوئری به مشکل برخواهید خورد. 

 

بیشتر امکانات تو کار ASP.NET MVC را از دست خواهید داد

به هنگام توسعه‌ی برنامه با استفاده از فریم ورک‌های SPA، امکانات توکار ASP.NET MVC مثل اعتبارسنجی یکپارچه و strongly typed view‌ها را از دست خواهید داد. شاید یک سری پروژه در Github پیدا کنید که سعی کرده‌اند این‌ها را با یکدیگر سازگار کنند. اما به محض استفاده متوجه می‌شوید که اگر همه‌ی کارها را خودتان با Angular انجام بدهید راحت‌تر هستید تا استفاده از کتابخانه‌های آزمایشی و ناقص.

البته باز هم نمی‌گویم که این‌ها تقصیر AngularJS است. ذات توسعه‌ی SPA‌ها، این گونه است و در توسعه‌ی SPA با هر فریمورکی به این مشکلات برخواهید خورد.

حال که یکسری مشکلات عمومی را بررسی کردیم، بدنیست نگاهی اختصاصی به خود AngularJS بیندازیم.

ضعف طراحی 

اگر به تعدای از لینک‌های سایت ihateangular مراجعه کنید می‌بینید که هر کسی نظری دارد: یکی می‌گوید به هیچ وجه Directive ننویسید، یکی دیگر می‌گوید کنترلر ننویسید و تمامی کارها را در directive‌های سفارشی نوشته شده توسط خودتان انجام بدهید، کلا همه جا علیه performance این فریمورک صحبت می‌کنند و همگی به پیچیده بودن آن اذعان دارند.

 اما نمی‌شود با چند مقاله‌ی موجود در اینترنت، یک فریمورک با این محبوبیت را زیر سوال برد. اما واقعا فکر می‌کنید که چرا نسخه‌ی 2 انگولار یک بازنویسی کامل است؟ دلیلش واضح است؛ این فریمورک از پایه اشکال دار بوده است و باید از اساس اصلاح شود. پس می‌توان نتیجه گرفت که اشکالات وارد شده به این فریمورک صحیح هستند. 

AngularJs 2 یک بازنویسی کامل است
 
قبلا این موضوع در این نظرسنجی مطرح شده است. بازنویسی کامل یعنی این که خیلی چیزها به کل تغییر کرده‌اند و کدهای قبلی شما با نسخه‌ی جدید سازگار نیستند. بیشتر مطالبی که فراگرفته بودید دیگر کاربردی ندارد و دوباره مطالب جدیدی را باید یاد بگیرید. این را هم در نظر بگیرید که توسعه دهندگانی که در حال نوشتن directive هستند، احتمالا با آمدن نسخه 2 انگولار، مجبورند directive خود را بازنویسی کنند. آیا خودتان بودید، دیگر دل به کار می‌دادید؟!

نتیجه گیری

AngularJS فریمورک خیلی خوبی برای نوشتن برنامه‌های تست پذیر است و کسی منکر قابلیت‌های آن نیست. ولی این را نیز در نظر بگیرید که برای تست پذیر بودن، خیلی چیز‌ها از جمله سادگی کار را از دست می‌دهید. معمولا می‌گویند که AngularJS کارهای مشکل را ساده می‌کند و کارهای ساده را مشکل.

پیشنهاد من این است که اگر هنوز AngularJS را فرا نگرفته‌اید، حداقل یادگیری آن را تا انتشار نسخه‌ی 2 آن به تعویق بیندازید. اگر AngularJS را بلد هستید، دیگر آن را در پروژه‌ای استفاده نکنید؛ چون دیگر کدهای شما در نسخه‌ی 2 کار نخواهد کرد و احتیاج به انجام تغییرات گسترده‌ای در کدهای نوشته شده قبلی پیدا می‌کنید.