مطالب
آموزش Knockout.Js #1
اگر از برنامه نویسهای پروژههای WPF درباره ویژگیهای مهم الگوی MVVM بپرسید به احتمال زیاد اولین مطلبی که عنوان میشود این است که هنگام کار با الگوی MVVM در WPF باید از مباحث data-binding استفاده شود. به صورت خلاصه، data-binding مکانیزمی است که عناصر موجود در Xaml را به آبجکتهای موجود در ViewModel یا سایر عناصر Xaml مقید میکند به طوری که با تغییر مقدار در آبجکتهای ViewModel، عناصر View نیز خود را به روز میکنند یا با تغییر در مقادیر عناصر Xaml، آبجکتهای متناظر در ViewModel نیز تغییر خواهند کرد(در صورت تنظیم Mode = TwoWay).
Knockout.Js چیست؟در یک جمله Knockout.Js یک فریم ورک جاوا اسکریپ است که امکان پیاده سازی الگوی MVVM و مکانیزم data-binding را در پروژههای تحت وب به راحتی میسر میکند. به عبارت دیگر عناصر DOM را به data-model و آبجکتهای data-model را به عناصر DOM مقید میکند، به طوری که با هر تغییر در مقدار یا وضعیت این عناصر یا آبجکت ها، تغییرات به موارد مقید شده نیز اعمال میگردد. به تصاویر زیر دقت کنید!
به روز رسانی data-model بدون استفاده از KO
به روز رسانی data-model با استفاده از KO
ویژگیهای مهم KO
»ارائه یک راه حل بسیار ساده و واضح برای اتصال بخشهای مختلف UI به data-model
»به روز رسانی خودکار عناصر و بخشهای مختلف UI بر اساس تغییرات صورت گرفته در data-model
»به صورت کامل با کتابخانه و توابع javascript پیاده سازی شده است.
»حجم بسیار کم(سیزده کیلو بایت) بعد از فشرده سازی
»سازگار با تمام مروگرهای جدید(... ,IE 6+, Firefox 2+, Chrome, Safari )
»امکان استفاده راحت بدون اعمال تغییرات اساسی در معماری پروژه هایی که در فاز توسعه هستند و بخشی از مسیر توسعه را طی کرده اند
»و...
آیا KO برای تکمیل JQuery در نظر گرفته شده است یا جایگزین؟
در اینکه JQuery بسیار محبوب است و در اکثر پروژههای تحت وب مورد استفاده است شکی وجود ندارد ولی این بدان معنی نیست که با توجه به وجود JQuery و محبوبیت آن دیگر نیازی به KO احساس نمیشود. به عنوان یک مثال ساده : فرض کنید در یک قسمت از پروژه قصد داریم یک لیست از دادهها را نمایش دهیم. در پایین لیست تعداد آیتمهای موجود در لیست مورد نظر نمایش داده میشود. یک دکمه Add داریم که امکان اضافه شدن آیتم جدید را در اختیار ما قرار میدهد. بعد از اضافه شدن یک مقدار، باید عددی که تعداد آیتمهای لیست را نمایش میدهد به روز کنیم. خب اگر قصد داشته باشیم این کار را با JQuery انجام دهیم راه حلهای زیر پیش رو است :
» به دست آوردن تعداد trهای جدول موجود؛»به دست آوردن تعداد divهای موجود با استفاده از یک کلاس مشخص css؛
» یا حتی به دست آوردن تعداد آیتمهای نمایشی در span هایی مشخص.
و البته سایر راه حل ها...
حال فرض کنید دکمههای دیگر نظیر Delete نیز مد نظر باشد که مراحل بالا تکرار خواهند شد. اما با استفاده از KO به راحتی میتوانیم تعداد آیتمهای موجود در یک آرایه را به یک عنصر مشخص bind کنیم به طور با هر تغییر در این مقدار، عنصر مورد نظر نیز به روز میشود یا به بیانی دیگر همواره تغییرات observe خواهند شد. برای مثال:
Number of items :<span data-bind="text: myList().count"></span>
در پست بعد، شروع به کار با KO آموزش داده خواهد شد.
ادامه دارد...
اشتراکها
دوره توسعه asp.net mvc
در ادامه دوره مقدماتی mvc که قبلا در سایت به اشتراک گذاشته شده بود.دوره جدیدی رو مایکروسافت منتشر کرده با عنوان توسعه برنامههای MVC که سرفصلهای آن را در بخش زیر مشاهده میکنید:
Mod 01: Introduction to MVC 4 Mod 02: Developing ASP.NET MVC 4 Models Mod 03: Developing MVC 4 Controllers Mod 04: Developing ASP.NET MVC 4 Views Mod 05: Integrating JavaScript and MVC 4 Mod 06: Implementing Web APIs Mod 07: Deploying to Windows Azure Mod 08: Visual Studio 2013/MVC 5 Sneak Peek
در مقاله قبلی بحث Assembly Linker را باز کردیم و یاد گرفتیم که چگونه میتوان با استفاده از آن ماژولهای مختلف را به یک اسمبلی اضافه کرد. در این قسمت از این سلسله مقالات قصد داریم فایلهای منابع (Resource) مانند مواد چندرسانهای، چند زبانه و .. را به آن اضافه کنیم. یک اسمبلی حتی میتواند تنها Resource باشد.
برای اضافه کردن یک فایل به عنوان منبع، از سوئیچ [embed[resource استفاده میشود. این سوئیچ محتوای هر نوع فایلی را که به آن پاس شود، به فایل PE اجرایی انتقال داده و جدول ManifestResourceDef را به روز میکند تا سیستم از وجود آن آگاه شود.
سوئیچ [link[Resource هم برای الحاق کردن یک فایل به اسمبلی به کار میرود و دو جدول ManifestResourceDef و FileDef را جهت معرفی منبع جدید و شناسایی فایل اسمبلی که حاوی این منبع است، به روز میکند. در این حالت فایل منبع embed نشده و باید در کنار پروژه منتشر شود.
csc هم قابلیتهای مشابهی را با استفاده از سوئیچهای resource/ و link/ دارد و به روز رسانی و دیگر اطلاعات تکمیلی آن مشابه موارد بالاست.
شما حتی میتوانید منابع یک فایل win32 را خیلی راحت و آسان به اسمبلی معرفی کنید. شما به آسانی میتوانید مسیر یک فایل res. را با استفاده از سوئیچ win32res/ در al یا csc مشخص کنید. یا برای embed کردن آیکن یک برنامه win32 از سوئیچ win32icon/ مسیر یک فایل ICO را مشخص کنید. در ویژوال استودیو اینکار به صورت ویژوالی در پنجره تنظمیات پروژه و برگهی Application امکان پذیر است. دلیل اصلی که آیکن برنامهها به صورت embed ذخیره میشوند این است که این آیکن برای فایل اجرایی یک برنامهی مدیریت شده هم به کار میرود.
فایلهای اسمبلی Win32 شامل یک فایل مانیفست اطلاعاتی هستند که به طور خودکار توسط کمپایلر سی شارپ تولید میگردند. با استفاده از سوئیچ nowin32manifest/ میتوان از ایجاد این نوع فایل جلوگیری کرد. این اطلاعات به طور پیش فرض شبیه زیر است:
موقعیکه AL یا CSC یک فایل نهایی PE را ایجاد میکند، یک منبع نسخه بندی شده با استاندارد win32 نیز به آن Embed میشود که با راست کلیک روی فایل و انتخاب گزینهی Properties و برگهی Details این اطلاعات نمایش مییابد. در کدنویسی این اپلیکیشن هم میتوانید از طریق فضای نام system.Diagnostics.FileVersionInfo و متد ایستای آن GetVersionInfo که پارامتر ورودی آن مسیر فایل اسمبلی است هم به این اطلاعات، در حین اجرای برنامه دست پیدا کنید.
جدول زیر اطلاعاتی در مورد سوئیچهای AL جهت مقداردهی این فیلدهای نسخه بندی دارد (کامپایلر سی شارپ این سوئیچها را ندارد و بهتر است از طریق همان خصوصیات در کدها اقدام کنید). بعضی از اطلاعات زیر با استفاده از سوئیچها قابل تغییر نیستند؛ چرا که این مقادیر یا ثابت هستند یا اینکه طبق شرایطی از بین چند مقدار ثابت، یکی از آنها انتخاب میشود.
موقعیکه شما یک پروژهی سی شارپ را ایجاد میکنید، فایلی به نام AssebmlyInfo.cs در دایرکتوری Properties پروژه ایجاد میشود. این فایل شامل تمامی خصوصیتهای نسخه بندی که در بالا ذکر شد، بهعلاوه یک سری خصوصیات دیگری که در آینده توضیح خواهیم داد، میباشد.
شما برای ویرایش این فایل میتوانید به راحتی آن را باز کرده و اطلاعات داخل آن را تغییر دهید. ویژوال استودیو نیز برای ویرایش این فایل، امکانات GUI را نیز فراهم کرده است. برای استفاده از این امکان، پنجرهی properties را در سطح Solution باز کرده و در تب Application روی Assembly Information کلیک کنید.
برای اضافه کردن یک فایل به عنوان منبع، از سوئیچ [embed[resource استفاده میشود. این سوئیچ محتوای هر نوع فایلی را که به آن پاس شود، به فایل PE اجرایی انتقال داده و جدول ManifestResourceDef را به روز میکند تا سیستم از وجود آن آگاه شود.
سوئیچ [link[Resource هم برای الحاق کردن یک فایل به اسمبلی به کار میرود و دو جدول ManifestResourceDef و FileDef را جهت معرفی منبع جدید و شناسایی فایل اسمبلی که حاوی این منبع است، به روز میکند. در این حالت فایل منبع embed نشده و باید در کنار پروژه منتشر شود.
csc هم قابلیتهای مشابهی را با استفاده از سوئیچهای resource/ و link/ دارد و به روز رسانی و دیگر اطلاعات تکمیلی آن مشابه موارد بالاست.
شما حتی میتوانید منابع یک فایل win32 را خیلی راحت و آسان به اسمبلی معرفی کنید. شما به آسانی میتوانید مسیر یک فایل res. را با استفاده از سوئیچ win32res/ در al یا csc مشخص کنید. یا برای embed کردن آیکن یک برنامه win32 از سوئیچ win32icon/ مسیر یک فایل ICO را مشخص کنید. در ویژوال استودیو اینکار به صورت ویژوالی در پنجره تنظمیات پروژه و برگهی Application امکان پذیر است. دلیل اصلی که آیکن برنامهها به صورت embed ذخیره میشوند این است که این آیکن برای فایل اجرایی یک برنامهی مدیریت شده هم به کار میرود.
فایلهای اسمبلی Win32 شامل یک فایل مانیفست اطلاعاتی هستند که به طور خودکار توسط کمپایلر سی شارپ تولید میگردند. با استفاده از سوئیچ nowin32manifest/ میتوان از ایجاد این نوع فایل جلوگیری کرد. این اطلاعات به طور پیش فرض شبیه زیر است:
<?xml version="1.0" encoding="UTF8" standalone="yes"?> <assembly xmlns="urn:schemasmicrosoftcom:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" name="MyApplication.app" /> <trustInfo xmlns="urn:schemasmicrosoftcom:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemasmicrosoftcom:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> </assembly>
موقعیکه شما یک اسمبلی میسازید باید فیلدهای منبع نسخه بندی را هم ذکر کنید. اینکار توسط خصوصیتها (Attributes) در سطح کد انجام میگیرد. این خصوصیات شامل موارد زیر هستند که در فضای نام Reflection قرار گرفتهاند.
using System.Reflection; // FileDescription version information: [assembly: AssemblyTitle("MultiFileLibrary.dll")] // Comments version information: [assembly: AssemblyDescription("This assembly contains MultiFileLibrary's types")] // CompanyName version information: [assembly: AssemblyCompany("Wintellect")] // ProductName version information: [assembly: AssemblyProduct("Wintellect (R) MultiFileLibrary's Type Library")] // LegalCopyright version information: [assembly: AssemblyCopyright("Copyright (c) Wintellect 2013")] // LegalTrademarks version information: [assembly:AssemblyTrademark("MultiFileLibrary is a registered trademark of Wintellect")] // AssemblyVersion version information: [assembly: AssemblyVersion("3.0.0.0")] // FILEVERSION/FileVersion version information: [assembly: AssemblyFileVersion("1.0.0.0")] // PRODUCTVERSION/ProductVersion version information: [assembly: AssemblyInformationalVersion("2.0.0.0")] // Set the Language field (discussed later in the "Culture" section) [assembly:AssemblyCulture("")]
جدول زیر اطلاعاتی در مورد سوئیچهای AL جهت مقداردهی این فیلدهای نسخه بندی دارد (کامپایلر سی شارپ این سوئیچها را ندارد و بهتر است از طریق همان خصوصیات در کدها اقدام کنید). بعضی از اطلاعات زیر با استفاده از سوئیچها قابل تغییر نیستند؛ چرا که این مقادیر یا ثابت هستند یا اینکه طبق شرایطی از بین چند مقدار ثابت، یکی از آنها انتخاب میشود.
نسخه منبع | سوئیچ AL.exe | توصیف خصوصیت یا سوئیچ مربوطه |
FILEVERSION | fileversion/ | System.Reflection.AssemblyFileVersionAttribute. |
PRODUCTVERSION | productversion/ | System.Reflection. AssemblyInformationalVersionAttribute |
FILEFLAGSMASK | - | Always set to VS_FFI_FILEFLAGSMASK (defined in WinVer.h as 0x0000003F). |
FILEFLAGS | - | همیشه صفر است |
FILEOS | - | در حال حاضر همیشه VOS__WINDOWS32 است |
FILETYPE | target/ | Set to VFT_APP if /target:exe or /target:winexe is specified; set to VFT_DLL if /target:library is specified. |
FILESUBTYPE | - | Always set to VFT2_UNKNOWN. (This field has no meaning for VFT_APP and VFT_DLL.) |
AssemblyVersion | version/ | System.Reflection.AssemblyVersionAttribute |
Comments | description/ | System.Reflection.AssemblyDescriptionAttribute |
CompanyName | company/ | System.Reflection.AssemblyCompanyAttribute |
FileDescription | title/ | System.Reflection.AssemblyTitleAttribute |
FileVersion | version/ | System.Reflection.AssemblyFileVersionAttribute |
InternalName | out/ | ذکر نام فایل خروجی بدون پسوند. |
LegalCopyright | copyright/ | System.Reflection.AssemblyCopyrightAttribute |
LegalTrademarks | trademark/ | System.Reflection.AssemblyTrademarkAttribute |
OriginalFilename | out | ذکر نام فایل خروجی بدون پسوند. |
PrivateBuild | - | همیشه خالی است. |
ProductName | product | System.Reflection.AssemblyProductAttribute |
ProductVersion | productversion | System.Reflection. AssemblyInformationalVersionAttribute |
SpecialBuild | - | همیشه خالی است. |
شما برای ویرایش این فایل میتوانید به راحتی آن را باز کرده و اطلاعات داخل آن را تغییر دهید. ویژوال استودیو نیز برای ویرایش این فایل، امکانات GUI را نیز فراهم کرده است. برای استفاده از این امکان، پنجرهی properties را در سطح Solution باز کرده و در تب Application روی Assembly Information کلیک کنید.
نظرات مطالب
دریافت خلاصهی وبلاگ تا 12 مهرماه 1389
salam
man khastam download konam ba yek hamchin errori movajeh shodam
fekr konam ke file az rapidshare delete shode?!!
File not available
The following file is not available:
http://rapidshare.com/files/423024356/blog03.10.2010.zip7452 KB
The file of the above link no longer exists. This could be for several reasons:
The uploader deleted the file
The file contained illegal contents and was deleted from our Abuse team
The file is incorrect
The file was uploaded through a free Account and reached the maximum number of downloads
RapidPro expired.
man khastam download konam ba yek hamchin errori movajeh shodam
fekr konam ke file az rapidshare delete shode?!!
File not available
The following file is not available:
http://rapidshare.com/files/423024356/blog03.10.2010.zip7452 KB
The file of the above link no longer exists. This could be for several reasons:
The uploader deleted the file
The file contained illegal contents and was deleted from our Abuse team
The file is incorrect
The file was uploaded through a free Account and reached the maximum number of downloads
RapidPro expired.
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 فقط با توابع خالص کار میکند.
توابع خالص: تعدادی آرگومان را دریافت کرده و بر اساس آنها، مقداری را باز میگردانند.
در اینجا یک تابع خالص را مشاهده میکنید که a و b را دریافت کرده و بر این اساس، یک خروجی کاملا مشخص را بازگشت میدهد.
توابع ناخالص: این نوع توابع سبب تغییراتی در متغیرهایی خارج از میدان دید خود میشوند و یا به همراه یک سری اثرات جانبی (side effects) مانند تعامل با دنیای خارج (وجود یک console.log در آن تابع و یا دریافت اطلاعاتی از یک API خارجی) هستند.
تابع تعریف شدهی در اینجا ناخالص است؛ چون با اطلاعاتی خارج از میدان دید خود مانند متغیر b، تعامل دارد. این تعامل با دنیای خارج، حتی در حد نوشتن یک console.log:
یک تابع خالص را تبدیل به یک تابع ناخالص میکند و یا نمونهی دیگر این تعاملات، فراخوانی سرویسهای backend در برنامه هستند که یک تابع را ناخالص میکنند:
روشهایی برای جلوگیری از تغییرات در اشیاء در جاوا اسکریپت
ایجاد تغییرات در آرایهها و اشیاء (Mutating arrays and objects) نیز ناخالصی ایجاد میکند؛ از این جهت که سبب تغییراتی در دنیای خارج (خارج از میدان دید تابع) میشویم. به همین جهت نیاز به روشهایی وجود دارد که از این نوع تغییرات جلوگیری کرد:
برای تغییری در یک شیء، تنها کافی است خاصیتی را به آن اضافه کنیم و یا با استفاده از واژهی کلیدی delete، خاصیتی را از آن حذف کنیم. به همین جهت برای اینکه تغییرات ما بر روی شیء اصلی اثری را باقی نگذارند، یکی از روشها، استفاده از متد Object.assign است. کار آن، یکی کردن اشیایی است که به آن ارسال میشوند. به همین جهت در اینجا با یک شیء خالی، از صفر شروع میکنیم. سپس دومین آرگومان آن را به همان شیء مدنظر، تنظیم میکنیم. به این ترتیب به یک کپی از شیء اصلی میرسیم که دیگر به آن، اتصالی را ندارد. به همین جهت اگر بر روی این شیء کپی تغییراتی را ایجاد کنیم، به شیء اصلی کپی نمیشود و سبب تغییرات در آن (mutation) نخواهد شد.
برای مثال در React، برای انجام رندر نهایی، در پشت صحنه کار مقایسهی اشیاء صورت میگیرد. به همین جهت اگر همان شیءای را که ردیابی میکند تغییر دهیم، دیگر نمیتواند به صورت مؤثری فقط قسمتهای تغییر کردهی آنرا تشخیص داده و کار رندر را فقط بر اساس آنها انجام دهد و مجبور خواهد شد کل یک شیء را بارها و بارها رندر کند که اصلا بهینه نیست. به همین جهت، ایجاد تغییرات مستقیم در شیءای که به state آن انتساب داده میشود، مجاز نیست.
متد Object.assign، چندین شیء را نیز میتواند با هم یکی کند و شیء جدیدی را تشکیل دهد:
روش دیگر ایجاد یک کپی و یا clone از یک شیء را که پیشتر در سری «React 16x» بررسی کردیم، به کمک امکانات ES-6، به صورت زیر است:
در اینجا نیز ابتدا یک شیء خالی را ایجاد میکنیم و سپس توسط spread operator، خواص شیء قبلی را درون آن باز کرده و قرار میدهیم. به این ترتیب به یک clone از شیء اصلی میرسیم. این حالت نیز از ترکیب چندین شیء با هم، پشتیبانی میکند:
روشهایی برای جلوگیری از تغییرات در آرایهها در جاوا اسکریپت
متد slice آرایهها نیز بدون ذکر آرگومانی، یک کپی از آرایهی اصلی را ایجاد میکند:
همچنین معادل همین قطعه کد در ES-6 به همراه spread operator به صورت زیر است:
و یا اگر بخواهیم یک کپی از چندین آرایه را ایجاد کنیم میتوان از متد concat استفاده کرد:
متد Array.push، هرچند سبب افزوده شدن عنصری به یک آرایه میشود، اما یک mutation را نیز ایجاد میکند؛ یعنی تغییرات آن به دنیای خارج اعمال میگردد. اما Array.concat یک آرایهی کاملا جدید را ایجاد میکند و همچنین امکان ترکیب آرایهها را نیز به همراه دارد.
معادل قطعه کد فوق در ES-6 و به همراه spread operator آن به صورت زیر است:
مفاهیم ابتدایی 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 دقت داشت.
چرا به ابزارهای مدیریت حالت نیاز داریم؟
به محض رد شدن از مرز پیاده سازی امکانات اولیهی یک برنامه، نیاز به ابزارهای مدیریت حالت نمایان میشوند؛ خصوصا زمانیکه نیاز است با اطلاعات قابل توجهی سر و کار داشت. مهمترین دلیل استفادهی از یک ابزار مدیریت حالت، مدیریت منطق تجاری برنامه است. منطق نمایشی برنامه مرتبط است به نحوهی نمایش اجزای آن در صفحه؛ مانند نمایش یک صفحهی مودال، تغییر رنگ عناصر با عبور کرسر ماوس از روی آنها و در کل منطقی که مرتبط و یا وابستهی به هدف اصلی برنامه نیست. از سوی دیگر منطق تجاری برنامه مرتبط است با مدیریت، تغییر و ذخیره سازی اشیاء تجاری مورد نیاز آن؛ مانند اطلاعات حساب کاربری شخص و دریافت اطلاعات برنامه از یک 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; }
توابع ناخالص: این نوع توابع سبب تغییراتی در متغیرهایی خارج از میدان دید خود میشوند و یا به همراه یک سری اثرات جانبی (side effects) مانند تعامل با دنیای خارج (وجود یک console.log در آن تابع و یا دریافت اطلاعاتی از یک API خارجی) هستند.
// Impure const b; const add = (a) => { return a + b; }
// Impure const add = (a, b) => { console.log('lolololol'); return a + b; }
// 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);
برای مثال در React، برای انجام رندر نهایی، در پشت صحنه کار مقایسهی اشیاء صورت میگیرد. به همین جهت اگر همان شیءای را که ردیابی میکند تغییر دهیم، دیگر نمیتواند به صورت مؤثری فقط قسمتهای تغییر کردهی آنرا تشخیص داده و کار رندر را فقط بر اساس آنها انجام دهد و مجبور خواهد شد کل یک شیء را بارها و بارها رندر کند که اصلا بهینه نیست. به همین جهت، ایجاد تغییرات مستقیم در شیءای که به state آن انتساب داده میشود، مجاز نیست.
متد Object.assign، چندین شیء را نیز میتواند با هم یکی کند و شیء جدیدی را تشکیل دهد:
// Extend object const original = { a: 1, b: 2 }; const extension = { c: 3 }; const extended = Object.assign({}, original, extension);
// Copy object const original = { a: 1, b: 2 }; const copy = { ...original };
// 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();
// Copy array const original = [1, 2, 3]; const copy = [ ...original ];
// Extend array const original = [1, 2, 3]; const extended = original.concat(4); const moreExtended = original.concat([4, 5]);
معادل قطعه کد فوق در 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 دقت داشت.
مطالب دورهها
اجزای جاوا اسکریپتی بوت استرپ 3
مباحث منوهای پایین افتادنی و برگهها (tabs) در بوت استرپ 2 و 3 یکسان هستند و نکات آنها در بحث «نگاهی به اجزای تعاملی Twitter Bootstrap» عنوان شدهاند. در ادامه به اجزای جدیدی خواهیم پرداخت که مختص بوت استرپ 3 بوده و از تازههای آن بهشمار میروند. همچنین اجزایی را که در بررسی بوت استرپ 2 به آنها پرداخته نشد، مانند Carousels و Scroll spy، نیز بررسی میکنیم.
پنلها و آکاردئونها
پنلهای آکاردئونی، بسیار شبیه به برگهها عمل میکنند. با کلیک بر روی یک سربرگ، محتوای مخفی شده آن نمایش داده میشود. این اعمال نیز توسط اجزای جاوا اسکریپتی بوت استرپ، به کمک jQuery عمل میکنند.
یک مثال:
- ابتدا کل ناحیه مدنظر باید در یک div با کلاس panel-group محصور شود؛ به همراه یک id دلخواه. از این id در ویژگیهای data-parent عنوانهای هر پنل این گروه استفاده میشود. به این ترتیب سیستم جاوا اسکریپتی آن متوجه خواهد شد که باید داخل چه ناحیهای از صفحه عمل کند.
- پس از مشخص سازی آغاز پنل گروهی مدنظر، هر گروه، داخل یک div با کلاس panel panel-default قرار خواهد گرفت. به این ترتیب اولین پنل آکاردئونی مثال، شکل میگیرد.
- سپس داخل هر پنل مجزا، باید توسط panel-heading مشخص کنیم که عنوان این پنل و محتوای خاص این عنوان کجا باید قرار گیرند. همچنین به کمک panel-collapse collapse in، محتوایی را که با کلیک بر روی عنوان هر پنل به صورت خودکار ظاهر خواهد شد را معرفی میکنیم.
در ادامه میتوان پنلهای بیشتری را به این مجموعه و گروه افزود.
- پنلی که قرار است در ابتدای کار باز باشد، دارای کلاس collapse in خواهد بود؛ مابقی فقط collapse دارند.
بررسی کامپوننت Carousels
بوت استرپ به همراه کامپوننت اسلایدشو توکاری است به نام Carousel که بدون نیاز به حتی یک سطر کدنویسی جاوا اسکریپت اضافی، یک اسلاید شو بسیار حرفهای را ارائه میدهد. مثالی را در این مورد در ادامه ملاحظه میکنید:
توضیحات:
- در قسمت carousel-inner این کامپوننت، لیست تک تک تصاویر مورد نیاز قرار خواهند گرفت. تصویر آغازین دارای div ایی محصور کننده با کلاس active item است و مابقی کلاس item دارند.
- مرحله بعد، کار افزودن سیستم راهبری و حرکت بین تصاویر اضافه شده است. این سیستم چیزی نیست جز چند لینک مزین شده با کلاسهای left carousel-control و همچنین right carousel-control. ویژگیهای data-slide این لینکها نیز مشخص کننده اعمالی هستند که کامپوننت جاوا اسکریپتی carousel قرار است انجام دهد. برای مثال حرکت به قبل یا بعد. همچنین باید دقت داشت که href این لینکها به id مرتبط با div اصلی دربرگیرنده این قسمت از صفحه اشاره میکند. از یک سری گلیف آیکن نیز برای نمایش فلش رو به چپ و راست نیز در اینجا استفاده شده است.
- قسمت لیست مرتبط دارای کلاس carousel-indicators، در حقیقت مشخص کننده سه دایره کوچکی است که در تصویر فوق ملاحظه میکنید. به ازای هر تصویر، یک مورد را باید افزود. در اینجا data-target هر آیتم به id مرتبط با div محصور کننده کل اسلایدشو اشاره میکند. data-slide-toها به شماره تصویر متناظر هر آیتم متصل خواهند شد. ایندکس آغازین این آیتمها از صفر شروع میشود.
بررسی کامپوننت Scroll spy
اگر به مستندات رسمی بوت استرپ مراجعه کنید، منوی کنار صفحه با لغزش صفحه به سمت پایین ثابت است؛ اما به ازای هر سرفصل جدیدی در صفحه، آیتم فعال این منو نیز به صورت خودکار تغییر میکند. این قابلیت توسط کامپوننت Scroll spy ایجاد شده است. یک مثال:
توضیحات:
- کار با ایجاد یک section جدید که حاوی منوی ثابت کنار صفحه است، شروع میشود. این section دارای کلاس scrollspy میباشد. داخل این section لیست عناوین منو قرار میگیرند که ul آن دارای کلاس nav nav-list affix خواهد بود. affix سبب میشود تا این لیست در کنار صفحه ثابت نمایش داده شود و همواره نمایان باشد.
- نکته مهم لیست آیتمهای منو، مقادیر href لینکهای آن است. این مقادیر باید به id محتوای متناظر اشاره کنند. این محتواها را در ادامهی کار ملاحظه میکنید.
- به علاوه اگر به تگ body در ابتدای کار دقت کرده باشید، ویژگیهای data-spy و هدفی که قرار است تحت نظر قرار گیرد به آن اضافه شدهاست.
- تا اینجا این سیستم کار میکند؛ اما اگر صفحه را به بالا و پایین حرکت دهید، پس زمینه آیتم فعال، تغییر رنگ نمیدهد. برای این منظور نیاز است، به CSS سفارشی خود، چند سطر ذیل را اضافه کرد:
فایلهای نهایی این قسمت را از اینجا نیز میتوانید دریافت کنید:
bs3-sample04.zip
پنلها و آکاردئونها
پنلهای آکاردئونی، بسیار شبیه به برگهها عمل میکنند. با کلیک بر روی یک سربرگ، محتوای مخفی شده آن نمایش داده میشود. این اعمال نیز توسط اجزای جاوا اسکریپتی بوت استرپ، به کمک jQuery عمل میکنند.
یک مثال:
<div class="container"> <h4 class="alert alert-info"> پنل آکاردئونی</h4> <div class="row"> <div class="panel-group" id="accordion"> <div class="panel panel-default"> <div class="panel-heading"> <h4 class="panel-title"> <a href="#vacc" class="accordion-toggle" data-toggle="collapse" data-parent="#accordion"> <span class="glyphicon glyphicon-pushpin"></span>اطلاعات یک</a> </h4> </div> <div id="vacc" class="panel-collapse collapse in"> <div class="panel-body"> <p> متن متن متن متن .......</p> <p> <a href="#" class="btn btn-info">بیشتر >></a></p> </div> </div> </div> <div class="panel panel-default"> <div class="panel-heading"> <h4 class="panel-title"> <a href="#checkups" class="accordion-toggle" data-toggle="collapse" data-parent="#accordion"> <span class="glyphicon glyphicon-ok"></span>اطلاعات 2</a> </h4> </div> <div id="checkups" class="panel-collapse collapse"> <div class="panel-body"> <p> متن متن متن متن .......</p> <p> <a href="#" class="btn btn-info">بیشتر >></a></p> </div> </div> </div> </div> <!-- end accordion --> </div> <!-- end row --> </div> <!-- /container -->
توضیحات:
- ابتدا کل ناحیه مدنظر باید در یک div با کلاس panel-group محصور شود؛ به همراه یک id دلخواه. از این id در ویژگیهای data-parent عنوانهای هر پنل این گروه استفاده میشود. به این ترتیب سیستم جاوا اسکریپتی آن متوجه خواهد شد که باید داخل چه ناحیهای از صفحه عمل کند.
- پس از مشخص سازی آغاز پنل گروهی مدنظر، هر گروه، داخل یک div با کلاس panel panel-default قرار خواهد گرفت. به این ترتیب اولین پنل آکاردئونی مثال، شکل میگیرد.
- سپس داخل هر پنل مجزا، باید توسط panel-heading مشخص کنیم که عنوان این پنل و محتوای خاص این عنوان کجا باید قرار گیرند. همچنین به کمک panel-collapse collapse in، محتوایی را که با کلیک بر روی عنوان هر پنل به صورت خودکار ظاهر خواهد شد را معرفی میکنیم.
در ادامه میتوان پنلهای بیشتری را به این مجموعه و گروه افزود.
- پنلی که قرار است در ابتدای کار باز باشد، دارای کلاس collapse in خواهد بود؛ مابقی فقط collapse دارند.
بررسی کامپوننت Carousels
بوت استرپ به همراه کامپوننت اسلایدشو توکاری است به نام Carousel که بدون نیاز به حتی یک سطر کدنویسی جاوا اسکریپت اضافی، یک اسلاید شو بسیار حرفهای را ارائه میدهد. مثالی را در این مورد در ادامه ملاحظه میکنید:
<div class="container"> <h4 class="alert alert-info"> اسلاید شو</h4> <div class="row"> <div id="myCarousel" class="carousel slide"> <ol class="carousel-indicators"> <li data-target="#myCarousel" data-slide-to="0" class="active"></li> <li data-target="#myCarousel" data-slide-to="1"></li> <li data-target="#myCarousel" data-slide-to="2"></li> </ol> <!-- carousel-indicators --> <section class="carousel-inner"> <div class="active item"> <img src="images/01.jpg" alt="Photo 1"></div> <div class="item"> <img src="images/02.png" alt="Photo 2"></div> <div class="item"> <img src="images/03.jpg" alt="Photo 3"></div> </section><!-- carousel-inner --> <a href="#myCarousel" class="left carousel-control" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"> </span></a><a href="#myCarousel" class="right carousel-control" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a> </div> <!-- myCarousel --> </div> <!-- end row --> </div>
توضیحات:
- در قسمت carousel-inner این کامپوننت، لیست تک تک تصاویر مورد نیاز قرار خواهند گرفت. تصویر آغازین دارای div ایی محصور کننده با کلاس active item است و مابقی کلاس item دارند.
- مرحله بعد، کار افزودن سیستم راهبری و حرکت بین تصاویر اضافه شده است. این سیستم چیزی نیست جز چند لینک مزین شده با کلاسهای left carousel-control و همچنین right carousel-control. ویژگیهای data-slide این لینکها نیز مشخص کننده اعمالی هستند که کامپوننت جاوا اسکریپتی carousel قرار است انجام دهد. برای مثال حرکت به قبل یا بعد. همچنین باید دقت داشت که href این لینکها به id مرتبط با div اصلی دربرگیرنده این قسمت از صفحه اشاره میکند. از یک سری گلیف آیکن نیز برای نمایش فلش رو به چپ و راست نیز در اینجا استفاده شده است.
- قسمت لیست مرتبط دارای کلاس carousel-indicators، در حقیقت مشخص کننده سه دایره کوچکی است که در تصویر فوق ملاحظه میکنید. به ازای هر تصویر، یک مورد را باید افزود. در اینجا data-target هر آیتم به id مرتبط با div محصور کننده کل اسلایدشو اشاره میکند. data-slide-toها به شماره تصویر متناظر هر آیتم متصل خواهند شد. ایندکس آغازین این آیتمها از صفر شروع میشود.
بررسی کامپوننت Scroll spy
اگر به مستندات رسمی بوت استرپ مراجعه کنید، منوی کنار صفحه با لغزش صفحه به سمت پایین ثابت است؛ اما به ازای هر سرفصل جدیدی در صفحه، آیتم فعال این منو نیز به صورت خودکار تغییر میکند. این قابلیت توسط کامپوننت Scroll spy ایجاد شده است. یک مثال:
<body id="articles" data-spy="scroll" data-target=".scrollspy"> <div class="container"> <h4 class="alert alert-info"> Scroll spy</h4> <div id="articlesindex" class="row"> <section class="scrollspy clearfix col col-lg-3 hidden-sm"> <ul class="nav nav-list affix"> <li><a href="#item1"><span class="glyphicon glyphicon-user"></span>Item 1</a></li> <li><a href="#item2"><span class="glyphicon glyphicon-user"></span>Item 2</a></li> <li><a href="#item3"><span class="glyphicon glyphicon-user"></span>Item 3</a></li> </ul> <!-- nav-list --> </section><!-- scrollspy --> <section class="col col-lg-9"> <article id="item1" class="media"> <h2> item1</h2> <div class="media-body"> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> </div> </article> <article id="item2" class="media"> <h2> item2</h2> <div class="media-body"> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> </div> </article> <article id="item3" class="media"> <h2> item3</h2> <div class="media-body"> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> <p> متن متن متن ............متن متن متن ............متن متن متن .............</p> </div> </article> </section><!-- artistinfo --> </div> <!-- end row --> </div> <!-- /container --> <script type="text/javascript" src="Scripts/jquery-1.10.2.min.js"></script> <script type="text/javascript" src="Scripts/bootstrap-rtl.js"></script> </body>
توضیحات:
- کار با ایجاد یک section جدید که حاوی منوی ثابت کنار صفحه است، شروع میشود. این section دارای کلاس scrollspy میباشد. داخل این section لیست عناوین منو قرار میگیرند که ul آن دارای کلاس nav nav-list affix خواهد بود. affix سبب میشود تا این لیست در کنار صفحه ثابت نمایش داده شود و همواره نمایان باشد.
- نکته مهم لیست آیتمهای منو، مقادیر href لینکهای آن است. این مقادیر باید به id محتوای متناظر اشاره کنند. این محتواها را در ادامهی کار ملاحظه میکنید.
- به علاوه اگر به تگ body در ابتدای کار دقت کرده باشید، ویژگیهای data-spy و هدفی که قرار است تحت نظر قرار گیرد به آن اضافه شدهاست.
- تا اینجا این سیستم کار میکند؛ اما اگر صفحه را به بالا و پایین حرکت دهید، پس زمینه آیتم فعال، تغییر رنگ نمیدهد. برای این منظور نیاز است، به CSS سفارشی خود، چند سطر ذیل را اضافه کرد:
.scrollspy .nav > li.active{ background: lightgray; }
فایلهای نهایی این قسمت را از اینجا نیز میتوانید دریافت کنید:
bs3-sample04.zip
در این قسمت به بررسی بخش Collections (امکان ساخت گروههای شخصی برای انتشار مطالب خود (توسط کاربران) با اعمال دسترسیهای مختلف) ، بخش آگهیها، سیستم لاگ عملیات کاربران و مدلهای سیستمی میپردازیم.
در مدلهای سیستم، یک تغییر کلی به منظور نگهداری آخرین تغییر دهنده و آخرین تاریخ تغییر در رکوردها، ایجاد شده است. کلاس پایهی زیر به منظور کپسوله کردن یکسری خصوصیات تکراری در نظر گرفته شده است.
public abstract class BaseEntity { #region Properties /// <summary> /// gets or sets Identifier of this Entity /// </summary> public virtual long Id { get; set; } /// <summary> /// gets or sets date that this entity was created /// </summary> public virtual DateTime CreatedOn { get; set; } /// <summary> /// gets or sets Date that this entity was updated /// </summary> public virtual DateTime ModifiedOn { get; set; } /// <summary> /// indicate this entity is Locked for Modify /// </summary> public virtual bool ModifyLocked { get; set; } /// <summary> /// gets or sets date that this entity repoted last time /// </summary> public virtual DateTime? ReportedOn { get; set; } /// <summary> /// gets or sets counter for Content's report /// </summary> public virtual int ReportsCount { get; set; } /// <summary> /// gets or sets TimeStamp for prevent concurrency Problems /// </summary> public virtual byte[] RowVersion { get; set; } #endregion #region NavigationProperties /// <summary> /// gets ro sets User that Modify this entity /// </summary> public virtual User ModifiedBy { get; set; } /// <summary> /// gets ro sets Id of User that modify this entity /// </summary> public virtual long? ModifiedById { get; set; } /// <summary> /// gets ro sets User that Create this entity /// </summary> public virtual User CreatedBy { get; set; } /// <summary> /// gets ro sets User that Create this entity /// </summary> public virtual long CreatedById { get; set; } #endregion }
- ReportedOn : نگهداری آخرین تاریخ اخطار
- ModifyLocked : به منظور ممانعت از ویرایش
- CreatedBy,CreatedById : به منظور ایجاد ارتباط یک به چند بین کاربر و سایر موجودیتها (به عنوان ایجاد کننده)
- ModifiedBy , ModifiedById : به منظور ایجاد ارتباط یک به چند بین کاربر و سایر موجودیتها (به عنوان آخرین تغییر دهنده)
مدل کلکسیون (کالکشن ،Collection)
بخش Collections میتواند برای کاربران امکان انتشار مطالب گروهبندی شده را بر اساس موضوعهای مختلف، مهیا کند. کاربران بعد از کسب امتیازات لازم با استفاده از مهیا کردن محتوای وبلاگ یا فعالیتهای آنها در انجمن و ... میتوانند دسترسی لازم را برای ساخت Collectionsهای خود، داشته باشند.
/// <summary> /// Represents the Collection for group posts by topic /// </summary> public class Collection : GuidBaseEntity { #region Properties /// <summary> /// gets or sets name of collection /// </summary> public virtual string Name { get; set; } /// <summary> /// gets or sets Alternative SlugUrl /// </summary> public virtual string SlugUrl { get; set; } /// <summary> /// gets or sets some description of group /// </summary> public virtual string Description { get; set; } /// <summary> /// gets or sets description that indicate how to pay /// </summary> public virtual string HowToPay { get; set; } /// <summary> /// gets or sets Visibility Type /// </summary> public virtual CollectionVisibility Visibility { get; set; } /// <summary> /// gets or sets color of Collection's Cover /// </summary> public virtual string Color { get; set; } /// <summary> /// gets or sets Name of Image that used as Cover /// </summary> public virtual string Photo { get; set; } /// <summary> /// gets or sets name of tags seperated by comma that assosiated with this content fo increase performance /// </summary> public virtual string TagNames { get; set; } /// <summary> /// indicate this collection is active or not /// </summary> public virtual bool IsActive { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set collection of attachments that attached in this group /// </summary> public virtual ICollection<CollectionAttachment> Attachments { get; set; } /// <summary> /// get or set tags of collection /// </summary> public virtual ICollection<Tag> Tags { get; set; } /// <summary> /// get or set List Of Posts that Associated with this Collection /// </summary> public virtual ICollection<CollectionPost> Posts { get; set; } /// <summary> /// get or set Users that they are Member of this collection if visibility is Custom /// </summary> public virtual ICollection<User> Memebers { get; set; } #endregion } public enum CollectionVisibility { Friends, OnlyMe, Public, NotFree, Custom }
- Visibility : برای اعمال محدودیت دسترسی به یک کلکسیون در نظر گرفته شده است. از نوع، نوع دادهی شمارشی CollectionVisibility پیاده سازی شدهی دربالا، میباشد. حالت Custom برای زمانی است که نیاز است صرفا یک سری از کاربران بدون هزینه، دسترسی برای مطالعهی این کلکسیون داشته باشند. حالت NotFree هم برای زمانی است که کاربرانی که هزینهی مورد نظر را پرداخت کرده باشند، به عنوان عضو به این کلکسیون میتوانند دسترسی داشته باشند.
- Members : به منظور اعمال ارتباط چند به چند بین مدل کاربر و مدل کلکسیون، در نظر گرفته شده است و زمانی این لیست اعضا خالی نیست که حالت Visibility با NotFree یا Custom مقدار دهی شده باشد.
- Tags : برای یافتن راحتتر کلکسیونهای مورد نظر کاربران، یک ارتباط چند به چند بین کلکسیونها و مخزن برچسب مطرح شدهی در مقاله اول، در نظر گرفته شده است.
- TagNames : برای افزایش کارآیی سیستم در نظر گرفته شده است و نام برچسبهای در ارتباط با کلکسیون را در خود به صورت جدا شده با (,) نگهداری میکند.
- Posts : لیست پستهایی است که توسط مدیر کلکسیون در آن ارسال میشود. لذا یک ارتباط یک به چند بین کلکسیونها و CollectionPost در نظر گرفته شده است.
- Attachments : لیست فایلهایی است که در کلکسیون بارگذاری شدهاند (در ادامه پیاده سازی خواهد شد).
- Owner , OwnerId : هر کلکسیون نیز یک صاحب خواهد داشت. بدین منظور یک ارتباط یک به چند بین کاربر و کلکسیون برقرار شده است.
- HowToPay : توضیحاتی در مورد نحوهی پرداخت هزینه (در صورتی که Visibility این کلکسیون NotFree باشد).
مدل پستهای کلکسیون ها
public class CollectionPost : GuidBaseEntity { #region Properties /// <summary> /// indicate this post should be pin /// </summary> public virtual bool IsPin { get; set; } /// <summary> /// gets or sets the blog pot body /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets the content title /// </summary> public virtual string Title { get; set; } /// <summary> /// gets or sets value indicating Custom Slug /// </summary> public virtual string SlugAltUrl { get; set; } /// <summary> /// gets or sets a value indicating whether the content comments are allowed /// </summary> public virtual bool AllowComments { get; set; } /// <summary> /// indicate comments should be approved before display /// </summary> public virtual bool ModerateComments { get; set; } /// <summary> /// gets or sets viewed count /// </summary> public virtual long ViewCount { get; set; } /// <summary> /// Gets or sets the total number of approved comments /// <remarks>The same as if we run Item.Comments.Count() /// We use this property for performance optimization (no SQL command executed) /// </remarks> /// </summary> public virtual int ApprovedCommentsCount { get; set; } /// <summary> /// Gets or sets the total number of unapproved comments /// <remarks>The same as if we run Item.Comments.Count() /// We use this property for performance optimization (no SQL command executed) /// </remarks> /// </summary> public virtual int UnApprovedCommentsCount { get; set; } /// <summary> /// gets or sets rating complex instance /// </summary> public virtual Rating Rating { get; set; } /// <summary> /// gets or sets information of User-Agent /// </summary> public virtual string Agent { get; set; } /// <summary> /// indicate users can share this post /// </summary> public virtual bool IsEnableForShare { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set comments of this post /// </summary> public virtual ICollection<CollectionComment> Comments { get; set; } /// <summary> /// gets or sets collection that associated with this post /// </summary> public virtual Collection Collection { get; set; } /// <summary> /// gets or sets id of collection that associated with this post /// </summary> public virtual Guid CollectionId { get; set; } #endregion }
- IsEnableForShare : اگر با مقدار True مقدار دهی شده باشد ، امکان به اشتراک گذاری آن درشبکههای اجتماعی وجود خواهد داشت.
- Comments : اگر مقدار AllowComments مربوط به پست ارسالی true باشد، در آن صورت امکان نظر دهی به پست هم امکان پذیر خواهد بود. برای برقراری ارتباط یک به چند بین مدل پست کلکسیون و نظرات کلکسیون، این لیست در مدل بالا گنجانده شده است.
- ModerateComments : اگر برای پست خاصی، با مقدار true مقدار دهی شده باشد، نظرات آن پست قبل از نمایش باید توسط مدیر آن کلکسیون تأیید شوند.
- Author , AuthorId : در واقع ارسال کنندهی تمام پستها، همان صاحب کلکسیون میباشد. ولی برای راحتی واکشی لیست پستهای ارسالی کاربر مورد نظر یک ارتباط یک به چند بین کاربر و پستهای ارسالی را در کلکسیون اعمال کردهایم.
- Collection , CollectionId : کلید خارجی ما در دیتابیس ایجاد شده خواهد بود که نشان دهندهی ارتباط یک به چند بین کلکسیون و پستها میباشد.
- IsPin : اگر لازم است پستی به عنوان اولین پست در کلکسیون نمایش داده شود، این خصوصیت برای آن true خواهد بود.
- ApprovedCommentsCount , UnApprovedCommentsCount: برای افزایش کارآیی سیستم در نظر گرفته شده است و هنگام درج نظر جدید یا حذف نظر، ویرایش خواهند شد.
مدل نظرات ارسالی در کلکسیون ها
public class CollectionComment { #region Ctor public CollectionComment() { Id = SequentialGuidGenerator.NewSequentialGuid(); CreatedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// get or set identifier of record /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets date of creation /// </summary> public virtual DateTime CreatedOn { get; set; } /// <summary> /// gets or sets body of blog post's comment /// </summary> public virtual string Body { get; set; } /// <summary> /// gets or sets body of blog post's comment /// </summary> public virtual Rating Rating { get; set; } /// <summary> /// gets or sets informations of agent /// </summary> public virtual string UserAgent { get; set; } /// <summary> /// indicate this comment is Approved /// </summary> public virtual bool IsApproved { get; set; } /// <summary> /// gets or sets Ip Address of Creator /// </summary> public virtual string CreatorIp { get; set; } /// <summary> /// gets or sets datetime that is modified /// </summary> public virtual DateTime? ModifiedOn { get; set; } /// <summary> /// gets or sets counter for report this comment /// </summary> public virtual int ReportsCount { get; set; } /// <summary> /// indicate this entity is Locked for Modify /// </summary> public virtual bool ModifyLocked { get; set; } /// <summary> /// gets or sets date that this entity repoted last time /// </summary> public virtual DateTime? ReportedOn { get; set; } #endregion #region NavigationProperties /// <summary> /// gets or sets post that this comment added it /// </summary> public virtual CollectionPost Post { get; set; } /// <summary> /// gets or sets id of post that this comment added it /// </summary> public virtual Guid PostId { get; set; } /// <summary> /// get or set user that create this record /// </summary> public virtual User Creator { get; set; } /// <summary> /// get or set Id of user that create this record /// </summary> public virtual long CreatorId { get; set; } /// <summary> /// gets or sets CollectionComment's identifier for Replying and impelemention self referencing /// </summary> public virtual Guid? ReplyId { get; set; } /// <summary> /// gets or sets Collection's comment for Replying and impelemention self referencing /// </summary> public virtual CollectionComment Reply { get; set; } /// <summary> /// get or set collection of Collection's comment for Replying and impelemention self referencing /// </summary> public virtual ICollection<CollectionComment> Children { get; set; } #endregion }
مدل بالا نشان دهندهی نظرات ارسالی برای پستهای کلکسیونها میباشد. صرفا کاربران عضو سیستم این اجازه را در صورتی خواهند داشت که برای پست مورد نظر خصوصیت AllowComments با مقدار true مقدار دهی شده باشد
حالت درختی آن مشخص است. برای اعمال ارتباط یک به چند بین پستها و نظرات، از CollectionPost و CollectionPostId استفاده خواهد شد.
- IsApproved : برای زمانی استفاده خواهد شد که خصوصیت ModerateComments پست مورد نظر با مقدار true مقدار دهی شده باشد.
- ReportsCount : به مانند بخشهای قبل، تعداد اخطارهای داده شدهی برای یک نظر را نشان خواهد داد.
- Creator,CreatorId : ارسال کنندهی نظر میباشد و برای ایجاد ارتباط یک به چند بین کاربر و نظرات کلکسیونها در نظر گرفته شدهاند.
- ReportedOn : نگه داری آخرین تاریخ اخطار
- ModifyLocked : به منظور ممانعت از ویرایش
مدل فایلهای ضمیمه کلکسیون ها
public class CollectionAttachment : BaseAttachment { #region NavigationProperties /// <summary> /// gets or sets Collection that this file attached /// </summary> public virtual Collection Collection { get; set; } /// <summary> /// gets or sets Id of Collection that this file attached /// </summary> public virtual Guid? CollectionId { get; set; } #endregion }
مدل آگهی ها
/// <summary> /// Represents Announcement For Announcement Section /// </summary> public class Announcement : BaseContent { #region Properties /// <summary> /// gets or sets Date that this Announcement will Expire /// </summary> public virtual DateTime? ExpireOn { get; set; } /// <summary> /// indicate this accouncement is approved by admin if announcementSetting.Moderate==true /// </summary> public virtual bool IsApproved { get; set; } #endregion #region NavigationProperties /// <summary> /// get or set Collection of Comments for this Announcement /// </summary> public virtual ICollection<AnnouncementComment> Comments { get; set; } #endregion }
- ExpireOn : زمان انقضای آگهی
- IsApproved : به منظور اعمال مدیریتی در نظر گرفته شده است
- Comments : اگر امکان ارسال نظرات برای آگهی از بخش تنظیمات فعال باشد، این لیست نظرات ما را نگه داری خواهد کرد. لذا یک رابطهی یک به چند بین نظرات و آگهیها خواهد بود.
مدل نظرات آگهی ها
/// <summary> /// Repersents Comment For Announcement /// </summary> public class AnnouncementComment : BaseComment { #region NavigationProperties /// <summary> /// gets or sets body of announcement's comment /// </summary> public virtual long? ReplyId { get; set; } /// <summary> /// gets or sets body of announcement's comment /// </summary> public virtual AnnouncementComment Reply { get; set; } /// <summary> /// gets or sets body of announcement's comment /// </summary> public virtual ICollection<AnnouncementComment> Children { get; set; } /// <summary> /// gets or sets announcement that this comment sent to it /// </summary> public virtual Announcement Announcement { get; set; } /// <summary> /// gets or sets announcement'Id that this comment sent to it /// </summary> public virtual long AnnouncementId { get; set; } #endregion }
مدل بالا نشان دهندهی نظرات ارسالی برای آگهیها میباشد. از کلاس پایهی مطرح شدهی در مقالهی اول ارث بری کرده و علاوه بر آن ساختار درختی آن مشخص است و همچنین برای برقراری ارتباط یک به چند با مدل آگهیها، خصوصیات Announcement , AnnouncementId در نظر گرفته شدهاند.
مدل سیستم لاگ عملیات کاربران
/// <summary> /// Represent The Operation's log /// </summary> public class AuditLog { #region Ctor /// <summary> /// /// </summary> public AuditLog() { Id = SequentialGuidGenerator.NewSequentialGuid(); OperatedOn = DateTime.Now; } #endregion #region Properties /// <summary> /// sets or gets identifier of AuditLog /// </summary> public virtual Guid Id { get; set; } /// <summary> /// sets or gets description of Log /// </summary> public virtual string Description { get; set; } /// <summary> /// sets or gets when log is operated /// </summary> public virtual DateTime OperatedOn { get; set; } /// <summary> /// sets or gets log's section /// </summary> public virtual AuditSection Section { get; set; } #endregion #region NavigationProperties /// <summary> /// sets or gets log's creator /// </summary> public virtual User OperatedBy { get; set; } /// <summary> /// sets or gets identifier of log's creator /// </summary> public virtual long OperatedById { get; set; } #endregion } public enum AuditSection { Blog, News, Forum, ... }
مدل دامینهای ممنوع
در پنل مدیریت امکانی را خواهیم داشت تا یکسری از دامینهای مد نظر را که نمیخواهیم در محتوای سیستم، آدرس صفحات آنها دیده شوند و همچنین برای عدم ارسال و دریافت پینگ بکها لحاظ شوند، ثبت کنیم.
/// <summary> /// Represents Domain that is banned /// </summary> public class BannedDomain { #region Propertie /// <summary> /// gets or sets identifier of Domain /// </summary> public virtual Guid Id { get; set; } /// <summary> /// gets or sets DomainName /// </summary> public virtual string Name { get; set; } /// <summary> /// gets or sets Date that this record added /// </summary> public virtual DateTime BannedOn { get; set; } #endregion }
مدل کلمات ممنوع
/// <summary> /// Represents the banned words /// </summary> public class BannedWord { #region Ctor /// <summary> /// Create one instance of <see cref="BannedWord"/> /// </summary> public BannedWord() { Id = SequentialGuidGenerator.NewSequentialGuid(); } #endregion #region Properties /// <summary> /// gets or sets identifier of BannedWord /// </summary> public Guid Id { get; set; } /// <summary> /// gets or sets Bad word /// </summary> public string BadWord { get; set; } /// <summary> /// gets or sets Good replaceword /// </summary> public string GoodWord { get; set; } /// <summary> /// indicating that this Word is spam /// </summary> public bool IsStopWord { get; set; } #endregion }
- BadWord : کلمه مورد نظر که قرار است Ban شود.
- IsStopWord : اگر لازم نیست جایگزینی برای کلمه استفاده شود و فقط لازم است حذف گردد، مقدار این خصوصیت true خواهد بود.
- GoodWord : کلمه جایگزین
مدل تنظیمات سیستم
/// <summary> /// Represent The CMS setting /// </summary> public class Setting { #region Properties /// <summary> /// sets or gets name of setting /// </summary> public virtual string Name { get; set; } /// <summary> /// sets or gets value of setting /// </summary> public virtual string Value { get; set; } /// <summary> /// sets or gets Type of setting /// </summary> public virtual string Type { get; set; } #endregion }
پ.ن:در مقالهی بعد با پیاده سازی مدلهای مدیریت کاربران، سیستم پیام رسانی، سیستم ترفیع رتبه و ارتباط دوستی، DomainClasses به اتمام خواهد رسید.
نتیجهی این قسمت
با توجه به این که تعداد مدلها زیاد است و از طرفی حجم تصویر را کاهش دادهایم ، تصویر بدست آماده کمی افت کیفیت دارد؛ بنابراین بهتر است از فایل EDMX زیر استفاده کنید.