در قسمت قبل، مروری داشتیم بر تفاوتهای دو نوع مختلف فناوریهای ایجاد و مدیریت فرمها در Angular و هچنین ساختار ابتدایی برنامهی این سری را ایجاد کردیم. در ادامه، اولین فرم مبتنی بر قالبها را ایجاد خواهیم کرد.
ایجاد اولین فرم مبتنی بر قالبها
پس از ایجاد کامپوننت employee-register، فایل قالب آن یا src\app\employee\employee-register\employee-register.component.html را گشوده و به نحو ذیل تکمیل میکنیم:
زمانیکه المان فرم به صفحه اضافه میشود، Angular به صورت خودکار دایرکتیو مرتبطی را به فرم اضافه میکند. برای دسترسی به این دایرکتیو نیاز است یک template reference variable را تعریف کرد. برای مثال "form="ngForm# به معنای تعریف متغیر form است که به دایرکتیو توکار ngForm متصل شدهاست و اکنون حاوی وهلهای از این دایرکتیو میباشد. به همین جهت است که امکان دسترسی به اطلاعات این وهله توسط درج form.pristine در همان قالب وجود دارد.
خاصیت pristine مشخص میکند که آیا فرم توسط کاربر تغییر یافتهاست یا خیر؟
مقدار خاصیت pristine در ابتدای کار true است؛ به این معنا که هنوز تغییری در آن اعمال نشدهاست.
یک نکته: ممکن است در حین توسعهی برنامه، خطای ذیل را در کنسول developer tools مرورگرها مشاهده کنید:
این دایرکتیو تنها زمانی قابل دسترسی است که در قسمت imports ماژول جاری که با آن کار میکنید، تعریف FormsModule را به همان نحوی که در انتهای قسمت قبل بررسی کردیم (قسمت «افزودن ماژول فرمها به برنامه»)، افزوده باشید.
در ادامه، در همین فرمی که تعاریف آنرا در بالا مشاهده میکنید، اطلاعاتی را وارد نمائید. هنوز هم مقدار خاصیت pristine مساوی true است. علت اینجا است که هنوز به Angular اعلام نکردهایم که کدام فیلد یا فیلدهای فرم را باید تحت نظر قرار دهد. برای این منظور ابتدا به المان تعریف شده نامی را انتساب داده و سپس دایرکتیو ngModel را نیز به انتهای تعاریف آن اضافه میکنیم:
اکنون اگر مقدار فرم را تغییر دهیم، مشاهده خواهیم کرد که مقدار خاصیتpristine به false تغییر میکند:
یک نکته: زمانیکه دایرکتیو ngModel ذکر میشود، تعریف name المان متناظر با آن، الزامی است؛ در غیراینصورت خطای ذیل را در کنسول developer tools مرورگرها مشاهده خواهید کرد:
خاموش کردن اعتبارسنجی توکار مرورگرها
یکی از کارهایی را که نیاز است در حین کار با فرمها انجام داد، خاموش کردن اعتبارسنجی توکار مرورگرها است. فرض کنید ویژگی معتبر و استاندارد required را به یکی از المانهای ورودی اضافه کردهاید:
در این حالت اگر برنامه را اجرا کنید و بدون تکمیل این فیلد بر روی دکمهی ارسال فرم کلیک نمائید، به ازای مرورگرهای مختلف، پیام انگلیسی «لطفا این فیلد را تکمیل کنید» ظاهر خواهد شد و هر کدام شکل متفاوتی را دارند که جزئیات آنها را نمیتوان تغییر داد و یا سفارشی سازی کرد. به این مورد، browser validation میگویند. به همین جهت برای خاموش کردن این اعتبارسنجی توکار مرورگرها و ارائهی تجربهی کاربری یکنواخت و یکدستی در تمام مرورگرها، نیاز است ویژگی novalidate را به تگ فرم اضافه کرد:
هر دوی ویژگیهای novalidate و یاrequired ، جزو استاندارد HTML هستند و ارتباطی به Angular ندارند.
بهبود ظاهر فرم توسط اعمال شیوهنامههای بوت استرپ
در قسمت قبل، در ابتدای کار تدارک ساختار مثال این سری، بوت استرپ را نیز نصب و تنظیم کردیم. در ادامه میخواهیم اندکی ظاهر این فرم را بر اساس شیوهنامههای بوت استرپ بهبود ببخشیم:
- برای افزودن بوت استرپ نیازی نیست تا شیوهنامهی آنرا به صورت دستی به Index.html برنامه اضافه کرد. همینقدر که ارجاعی از آن در فایل angular-cli.json. در قسمت شیوهنامههای آن وجود داشته باشد، به صورت خودکار در bundle نهایی تولید شدهی توسط سیستم ساخت برنامهی Angular CLI ظاهر خواهد شد.
- در اینجا ابتدا فرم خود را در داخل یک container قرار دادهایم. این مورد سبب میشود تا محتوای آن به میانهی صفحه منتقل شود.
- سپس شیوهنامهی btn به دکمهی ارسال فرم اضافه شدهاست تا شکل دکمههای بوت استرپ را پیدا کند.
- سپس هر فیلد ورودی داخل یک div با کلاس form-group محصور میشود و هر کنترل، کلاس form-control را خواهد یافت.
افزودن سایر المانهای ورودی به فرم
تا اینجا دو text box را به فرم اضافه کردهایم. در ادامه میخواهیم المانهای دیگری را نیز تعریف کنیم:
افزودن Check boxes
چون با بوت استرپ کار میکنیم، نیاز است المان ورودی از نوع checkbox را داخل یک div با کلاس checkbox محصور کنیم. سپس یک label را تعریف کرده و Input را داخل آن قرار دهیم. در اینجا نیز همانند سایر المانها نیاز است نامی را به آن انتساب داده و سپس دایرکتیو ngModel را قید نمود تا Angular این کنترل را تحت نظر قرار دهد.
افزودن Radio buttons
Radio buttons نیز شبیه به Check boxها تعریف میشوند. در اینجا نیز یک div با کلاس radio و سپس label ایی که المان ورودی از نوع radio داخل آن قرار میگیرد، افزوده خواهد شد. فقط در اینجا باید دقت داشت که گروه بندی این المانها بر اساس نام آنها انجام میشود. به همین جهت است که نام این دو المان یکی وارد شدهاست. همچنین باید value آنرا نیز تنظیم کرد. این مقداری است که در نهایت به سرور ارسال خواهد شد.
افزودن Drop downs
در اینجا از المان select برای تشکیل یک drop down استفاده میکنیم و نحوهی تعریف آن بسیار شبیه است به تعریف text boxهایی که داخل form-group محصور شده و همچنین کلاس form-control را پیدا میکنند.
اما قسمت مهم آن، اطلاعاتی است که قرار است در این drop down نمایش داده شوند. این اطلاعات را میتوان از آرایهی languages گرفت و سپس توسط یک ngFor به المان select اضافه کرد. بنابراین باید به فایل employee-register.component.ts مراجعه کرده و آرایهی languages را به آن افزود:
کاری که در اینجا انجام میشود، تکرار المان option توسط ngFor است. برای مثال در اینجا 4 بار المان option توسط عناصر آرایهی زبانها در داخل المان select تکرار خواهد شد. به عبارتی select نهایی رندر شدهی در صفحه، چنین شکلی را پیدا میکند:
تا اینجا فرم تشکیل شدهی ما چنین نمایی را پیدا میکند:
در قسمت بعد این فرم را توسط مباحث data binding و بررسی نحوهی دسترسی به اطلاعات آن در کامپوننت مرتبط، تکمیل خواهیم کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-02.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
ایجاد اولین فرم مبتنی بر قالبها
پس از ایجاد کامپوننت employee-register، فایل قالب آن یا src\app\employee\employee-register\employee-register.component.html را گشوده و به نحو ذیل تکمیل میکنیم:
<h3>Angular Forms</h3> <form #form="ngForm"> <input type="text" placeholder="Name"> <button type="submit">Ok</button> </form> form.pristine: {{ form.pristine }}
خاصیت pristine مشخص میکند که آیا فرم توسط کاربر تغییر یافتهاست یا خیر؟
مقدار خاصیت pristine در ابتدای کار true است؛ به این معنا که هنوز تغییری در آن اعمال نشدهاست.
یک نکته: ممکن است در حین توسعهی برنامه، خطای ذیل را در کنسول developer tools مرورگرها مشاهده کنید:
There is no directive with "exportAs" set to "ngForm"
در ادامه، در همین فرمی که تعاریف آنرا در بالا مشاهده میکنید، اطلاعاتی را وارد نمائید. هنوز هم مقدار خاصیت pristine مساوی true است. علت اینجا است که هنوز به Angular اعلام نکردهایم که کدام فیلد یا فیلدهای فرم را باید تحت نظر قرار دهد. برای این منظور ابتدا به المان تعریف شده نامی را انتساب داده و سپس دایرکتیو ngModel را نیز به انتهای تعاریف آن اضافه میکنیم:
<h3>Angular Forms</h3> <form #form="ngForm"> <input type="text" placeholder="Name" name="name" ngModel> <button type="submit">Ok</button> </form> form.pristine: {{ form.pristine }}
اکنون اگر مقدار فرم را تغییر دهیم، مشاهده خواهیم کرد که مقدار خاصیتpristine به false تغییر میکند:
یک نکته: زمانیکه دایرکتیو ngModel ذکر میشود، تعریف name المان متناظر با آن، الزامی است؛ در غیراینصورت خطای ذیل را در کنسول developer tools مرورگرها مشاهده خواهید کرد:
Error: If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions.
خاموش کردن اعتبارسنجی توکار مرورگرها
یکی از کارهایی را که نیاز است در حین کار با فرمها انجام داد، خاموش کردن اعتبارسنجی توکار مرورگرها است. فرض کنید ویژگی معتبر و استاندارد required را به یکی از المانهای ورودی اضافه کردهاید:
<input type="text" required placeholder="Name" name="name" ngModel>
<form #form="ngForm" novalidate>
بهبود ظاهر فرم توسط اعمال شیوهنامههای بوت استرپ
در قسمت قبل، در ابتدای کار تدارک ساختار مثال این سری، بوت استرپ را نیز نصب و تنظیم کردیم. در ادامه میخواهیم اندکی ظاهر این فرم را بر اساس شیوهنامههای بوت استرپ بهبود ببخشیم:
<div class="container"> <h3>Angular Forms</h3> <form #form="ngForm" novalidate> <div class="form-group"> <label>First Name</label> <input type="text" class="form-control" required name="firstName" ngModel> </div> <div class="form-group"> <label>Last Name</label> <input type="text" class="form-control" required name="lastName" ngModel> </div> <button class="btn btn-primary" type="submit">Ok</button> </form> </div> form.pristine: {{ form.pristine }}
- برای افزودن بوت استرپ نیازی نیست تا شیوهنامهی آنرا به صورت دستی به Index.html برنامه اضافه کرد. همینقدر که ارجاعی از آن در فایل angular-cli.json. در قسمت شیوهنامههای آن وجود داشته باشد، به صورت خودکار در bundle نهایی تولید شدهی توسط سیستم ساخت برنامهی Angular CLI ظاهر خواهد شد.
- در اینجا ابتدا فرم خود را در داخل یک container قرار دادهایم. این مورد سبب میشود تا محتوای آن به میانهی صفحه منتقل شود.
- سپس شیوهنامهی btn به دکمهی ارسال فرم اضافه شدهاست تا شکل دکمههای بوت استرپ را پیدا کند.
- سپس هر فیلد ورودی داخل یک div با کلاس form-group محصور میشود و هر کنترل، کلاس form-control را خواهد یافت.
افزودن سایر المانهای ورودی به فرم
تا اینجا دو text box را به فرم اضافه کردهایم. در ادامه میخواهیم المانهای دیگری را نیز تعریف کنیم:
افزودن Check boxes
<div class="checkbox"> <label> <input type="checkbox" name="is-full-time" ngModel> Full Time Employee </label> </div>
افزودن Radio buttons
<label>Payment Type</label> <div class="radio"> <label> <input type="radio" name="pay-type" value="FullTime" checked> Full Time </label> </div> <div class="radio"> <label> <input type="radio" name="pay-type" value="PartTime"> Part Time </label> </div>
افزودن Drop downs
<div class="form-group"> <label>Primary Language</label> <select class="form-control"> <option *ngFor="let lang of languages"> {{ lang }} </option> </select> </div>
اما قسمت مهم آن، اطلاعاتی است که قرار است در این drop down نمایش داده شوند. این اطلاعات را میتوان از آرایهی languages گرفت و سپس توسط یک ngFor به المان select اضافه کرد. بنابراین باید به فایل employee-register.component.ts مراجعه کرده و آرایهی languages را به آن افزود:
export class EmployeeRegisterComponent implements OnInit { languages = ["Persian", "English", "Spanish", "Other"];
<select class="form-control"> <option>Persian</option> <option>English</option> <option>Spanish</option> <option>Other</option> </select>
تا اینجا فرم تشکیل شدهی ما چنین نمایی را پیدا میکند:
در قسمت بعد این فرم را توسط مباحث data binding و بررسی نحوهی دسترسی به اطلاعات آن در کامپوننت مرتبط، تکمیل خواهیم کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-02.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
مسیرراهها
ASP.NET Core
ASP.NET Core 1.0
- قسمت اول: Net Core. چیست؟
- قسمت دوم: بررسی ساختار جدید Solution
- قسمت سوم: Middleware چیست؟
- قسمت چهارم: فعالسازی پردازش فایلهای استاتیک
- قسمت پنجم: فعالسازی صفحات مخصوص توسعه دهنده ها
- قسمت ششم: سرویسها و تزریق وابستگی ها
- قسمت هفتم: کار با فایلهای Config
- قسمت هشتم: فعالسازی ASP.NET MVC
- قسمت نهم: بررسی تغییرات مسیریابی
- قسمت دهم: بررسی تغییرات Viewها
- قسمت یازدهم: بررسی بهبودهای Razor
- قسمت دوازدهم: معرفی Tag Helpers
- قسمت سیزدهم: معرفی View Components
- قسمت چهاردهم: فعال سازی اعتبارسنجی ورودیهای کاربران
- قسمت پانزدهم: بررسی تغییرات Caching
- قسمت شانزدهم:کار با Sessions
- قسمت هفدهم: بررسی فریم ورک Logging
- قسمت هجدهم: کار با ASP.NET Web API
- قسمت نوزدهم: بومی سازی
- قسمت بیستم: بررسی تغییرات فیلترها
- قسمت بیست و یکم: بررسی تغییرات Bundling و Minification
- قسمت بیست و دوم: توزیع برنامه توسط IIS
- پیاده سازی Unobtrusive Ajax در ASP.NET Core 1.0
ASP.NET Core 2.0
- امکان استفادهی مستقیم از کتابخانههای Full .NET Framework در NET Core 2.0.
- Tag Helper Components در ASP.NET Core 2.0
- کار با Razor در ASP.NET Core 2.0
- فعالسازی Windows Authentication در برنامههای ASP.NET Core 2.0
ASP.NET Core 2.1
روش ارتقاء
ASP.NET Core Identity
کار با Areas در ASP.NET Core
کار با کوکیها در ASP.NET Core
بررسی روش آپلود فایلها در ASP.NET Core
ارسال ایمیل در ASP.NET Core
نوشتن Middleware سفارشی در ASP.NET Core
نوشتن TagHelperهای سفارشی برای ASP.NET Core
بررسی تغییرات Reflection در NET Core.
استفادهی گسترده از DateTimeOffset در NET Core.
بررسی روش دسترسی به HttpContext در ASP.NET Core
توزیع پروژههای ASP.NET Core 1.1 بدون ارائه فایلهای View آن
تغییرات رمزنگاری اطلاعات در NET Core.
ساخت بستههای نیوگت مخصوص NET Core.
تهیه قالب برای ارسال ایمیلها در ASP.NET Core توسط Razor Viewها
روش یافتن لیست تمام کنترلرها و اکشن متدهای یک برنامهی ASP.NET Core
بررسی روش آپلود فایلها از طریق یک برنامهی Angular به یک برنامهی ASP.NET Core
سفارشی سازی صفحهی اول برنامههای Angular CLI توسط ASP.NET Core
تغییرات Encoding در NET Core.
ASP.NET Core Identity
- سفارشی سازی
- امکان ساخت قالب برای پروژههای NET Core.
- تبدیل قالبهای سفارشی پروژههای NET Core. به بستههای NuGet
طراحی یک گرید با Angular و ASP.NET Core
- افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامههای Angular مبتنی بر ASP.NET Core
- تولید هدرهای Content Security Policy توسط ASP.NET Core برای برنامههای Angular
- غیرمعتبر شدن کوکیهای برنامههای ASP.NET Core هاست شدهی در IIS پس از ریاستارت آن
- اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
- اعتبارسنجی مبتنی بر کوکیها در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
- فعالسازی HSTS در ASP.NET Core
- مراحل تنظیم Let's Encrypt در IIS
- IdentityServer 4x
- قسمت اول - نیاز به تامین کنندهی هویت مرکزی
- قسمت دوم - ایجاد ساختار اولیهی مثال این سری
- قسمت سوم - بررسی مفاهیم OpenID Connect
- قسمت چهارم - نصب و راه اندازی IdentityServer
- قسمت پنجم - پیاده سازی ورود و خروج از سیستم
- قسمت ششم - کار با User Claims
- قسمت هفتم- امن سازی Web API
- قسمت هشتم- تعریف سطوح دسترسی پیچیده
- قسمت نهم- مدیریت طول عمر توکنها
- قسمت دهم- ذخیره سازی اطلاعات کاربران IDP در بانک اطلاعاتی
- قسمت یازدهم- استفاده از تامین کنندههای هویت خارجی
- قسمت دوازدهم- یکپارچه سازی با اکانت گوگل
- قسمت سیزدهم- فعالسازی اعتبارسنجی دو مرحلهای
- قسمت چهاردهم- آماده شدن برای انتشار برنامه
- قسمت اول - نیاز به تامین کنندهی هویت مرکزی
کار با Areas در ASP.NET Core
کار با کوکیها در ASP.NET Core
بررسی روش آپلود فایلها در ASP.NET Core
ارسال ایمیل در ASP.NET Core
نوشتن Middleware سفارشی در ASP.NET Core
نوشتن TagHelperهای سفارشی برای ASP.NET Core
بررسی تغییرات Reflection در NET Core.
استفادهی گسترده از DateTimeOffset در NET Core.
بررسی روش دسترسی به HttpContext در ASP.NET Core
توزیع پروژههای ASP.NET Core 1.1 بدون ارائه فایلهای View آن
تغییرات رمزنگاری اطلاعات در NET Core.
ساخت بستههای نیوگت مخصوص NET Core.
تهیه قالب برای ارسال ایمیلها در ASP.NET Core توسط Razor Viewها
روش یافتن لیست تمام کنترلرها و اکشن متدهای یک برنامهی ASP.NET Core
بررسی روش آپلود فایلها از طریق یک برنامهی Angular به یک برنامهی ASP.NET Core
سفارشی سازی صفحهی اول برنامههای Angular CLI توسط ASP.NET Core
تغییرات Encoding در NET Core.
تغییرات متدهای بازگشت فایلها به سمت کلاینت در ASP.NET Core
پیاده سازی برنامههای چند مستاجری در ASP.NET Core
مقدمهای بر تزریق وابستگیها درASP.NET Core
نمایش خطاهای اعتبارسنجی سمت سرور ASP.NET Core در برنامههای Angular
احراز هویت و اعتبارسنجی کاربران در برنامههای Angular - قسمت اول - معرفی و ایجاد ساختار برنامه
روش استفادهی صحیح از HttpClient در برنامههای دات نت
اجرای سرویسهای NodeJS در ASP.NET Core
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامههای ASP.NET Core در IIS
تست کردن متدهای یک Controller به کمک PowerShell
کار با Visual Studio در ASP.NET Core
پیاده سازی برنامههای چند مستاجری در ASP.NET Core
مقدمهای بر تزریق وابستگیها درASP.NET Core
نمایش خطاهای اعتبارسنجی سمت سرور ASP.NET Core در برنامههای Angular
احراز هویت و اعتبارسنجی کاربران در برنامههای Angular - قسمت اول - معرفی و ایجاد ساختار برنامه
روش استفادهی صحیح از HttpClient در برنامههای دات نت
اجرای سرویسهای NodeJS در ASP.NET Core
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامههای ASP.NET Core در IIS
تست کردن متدهای یک Controller به کمک PowerShell
کار با Visual Studio در ASP.NET Core
اشتراکها
Angular 2 نسخهی 2.0.0-rc.0 منتشر شد
- To import various symbols please adjust the paths in the following way:
- angular2/core -> @angular/core
- angular2/compiler -> @angular/compiler
- angular2/common -> @angular/common
- angular2/platform/browser -> @angular/platform-browser (applications with precompiled templates) + @angular/platform-browser-dynamic (applications that compile templates on the fly)
- angular2/platform/server -> @angular/platform-server
- angular2/testing -> @angular/core/testing (it/describe/..) + @angular/compiler/testing (TestComponentBuilder) + @angular/platform-browser/testing
- angular2/upgrade -> @angular/upgrade
- angular2/http -> @angular/http
- angular2/router -> @angular/router-deprecated (snapshot of the component router from beta.17 for backwards compatibility)
- new package: @angular/router - component router with several breaking changes
در این مطلب قصد داریم پیامها و اخطارهای برنامه را توسط کامپوننت Angular2 Toasty نمایش داده و همچنین برای کاهش میزان تکرار قسمتهای نمایش خطا در برنامه، کار مدیریت متمرکز و سراسری آنها را نیز انجام دهیم.
نمایش پیامها و اخطارهای یک برنامهی Angular توسط ng2-toasty
در مطلب «ایجاد Drop Down Listهای آبشاری در Angular» در قسمت دریافت اطلاعات drop down دوم از سرور، اگر کاربر مجددا گروه را بر روی حالت «لطفا گروهی را انتخاب کنید ...» قرار دهد، مقدار categoryId به undefined تغییر میکند:
در اینجا میخواهیم توسط کامپوننت Angular2 Toasty، پیام متناسبی را نمایش دهیم:
پیشنیازهای کار با کامپوننت Angular2 Toasty توسط یک برنامهی Angular CLI
برای کار با کامپوننت Angular2 Toasty، ابتدا از طریق خط فرمان به پوشهی ریشهی برنامه وارد شده و سپس دستور ذیل را صادر میکنیم:
اینکار سبب خواهد شد تا این کامپوننت در پوشهی node_modules\ng2-toasty نصب شده و همچنین فایل package.json نیز جهت درج مدخل آن به روز رسانی شود:
یک نکته: اگر در حین اجرای این دستور به خطای ذیل برخوردید:
چون VSCode پوشهی node_modules را تحت نظر قرار میدهد، ممکن است یک سری اعمال npm مجوز اجرا را پیدا نکنند. بنابراین ابتدا VSCode را بسته و مجددا دستور npm را اجرا کنید.
پس از آن نیاز است یکی از شیوهنامههایی را که در تصویر فوق ملاحظه میکنید، در فایل angular-cli.json. مشخص کنیم:
که برای نمونه در اینجا، شیوهنامهی بوت استرپ آن انتخاب شدهاست.
سپس باید به فایل src\app\app.module.ts مراجعه کرد و ماژول این کامپوننت را معرفی نمود:
همچنین در همین قسمت، به فایل قالب src\app\app.component.html مراجعه کرده و selector tag این کامپوننت را در ابتدای آن تعریف میکنیم:
در اینجا با استفاده از property binding و تعیین مقدار رشتهای top-right، محل نمایش اعلانات برنامه را مشخص میکنیم. مقدارهای ممکن آن شامل bottom-right، bottom-left، top-right، top-left، top-center، bottom-center، center-center هستند. برای مثال اگر میخواهید آنرا در میانهی صفحه نمایش دهید، مقدار center-center را انتخاب کنید. همچنین باید دقت داشت که این مقدار باید درون '' قرار گیرد تا مشخص شود که رشتهای به خاصیت position انتساب داده شدهاست و این مقدار یک خاصیت عمومی تعریف شدهی در کامپوننت متناظر با قالب، نیست.
نمایش یک پیام خطا توسط ToastyService
اکنون که کار برپایی کامپوننت Angular2 Toasty به پایان رسید، کار کردن با آن به سادگی تزریق سرویس آن به سازندهی یک کامپوننت و فراخوانی متدهای info، success ، wait ، error و warning آن است:
- در اینجا در ابتدا ماژولهای مورد نیاز import شدهاند.
- سپس ToastyService به سازندهی کلاس کامپوننت مدنظر تزریق شدهاست تا بتوان از امکانات آن استفاده کرد.
- در ادامه، فراخوانی متد this.toastyService.error سبب نمایش اخطار قرمز رنگی میشود که تصویر آنرا در ابتدای مطلب جاری مشاهده کردید.
- علت ذکر <ToastOptions> در اینجا این است که وجود آن سبب خواهد شد تا intellisense در VSCode فعال شود و پس از آن بتوان تمام گزینههای این متد و تنظیمات را بدون مراجعهی به مستندات آن از طریق intellisense یافت و درج کرد:
مدیریت سراسری خطاهای مدیریت نشده، در یک برنامهی Angular
در برنامههای Angular از این دست کدها بسیار مشاهده میشوند:
تا اینجا قسمت err یا بروز خطا را با console.log مدیریت کردهایم. در این حالت کاربر ممکن است 10 بار بر روی دکمهای کلیک کند یا صفحهای را بارگذاری کند و دست آخر متوجه نشود که مشکل کار چیست. به همین جهت میتوان خطاها را نیز توسط ToastyService نمایش داد تا کاربران دقیقا متوجه بروز مشکل رخ داده شوند. اما ... به این ترتیب تکرار کد زیادی را خواهیم داشت و باید به ازای تمام این موارد، یکبار this.toastyService.error را فراخوانی کنیم. برای مدیریت بهتر یک چنین سناریویی در Angular، کلاس و سرویس توکاری به نام ErrorHandler وجود دارد. در هر قسمتی از برنامهی Angular که استثنایی مدیریت نشده رخ دهد، ابتدا از این کلاس رد شده و سپس به برنامه انتشار پیدا میکند. بنابراین میتوان یک ErrorHandler سفارشی را با ارث بری از آن تهیه کرد و سپس بجای سرویس توکار اصلی، به برنامه معرفی و از آن استفاده نمود. به این ترتیب میتوان یک Global Error Interceptor را طراحی نمود.
به همین منظور کلاس جدیدی را به صورت ذیل در پوشهی src\app اضافه میکنیم:
با این خروجی
سپس این کلاس را به نحو ذیل تکمیل خواهیم کرد:
کلاس جدید AppErrorHandler از کلاس پایه ErrorHandler ارث بری میکند. بنابراین import آنرا در ابتدای کار مشاهده میکنید. سپس باید متد handleError آنرا با امضایی که مشاهده میکنید، پیاده سازی کنیم. فعلا با استفاده از console.log این خطا را در کنسول developer tools نمایش میدهیم.
اکنون نیاز است این ErrorHandler سفارشی را بجای نمونهی اصلی به برنامه معرفی کنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
ابتدا ErrorHandler به لیست imports اضافه شدهاست و همچنین محل تامین AppErrorHandler نیز مشخص گردیدهاست. سپس در قسمت providers ماژول جاری، از تعریف خاصی که ملاحظه میکنید، استفاده خواهد شد. به این ترتیب به Angular اعلام میکنیم، هرگاه نیازی به وهلهای از کلاس توکار ErrorHandler بود، وهلهای از کلاس سفارشی AppErrorHandler را مورد استفاده قرار بده.
اکنون برای آزمایش آن، در کدهای سمت سرور مطلب «ایجاد Drop Down Listهای آبشاری در Angular»، یک استثنای عمدی را قرار میدهیم:
به این ترتیب هر زمانیکه گروهی انتخاب شد، دریافت محصولات آن گروه با خطا مواجه میشود.
برای اینکه AppErrorHandler، مورد استفاده قرار گیرد، قسمت err دریافت لیست محصولات را نیز حذف میکنیم (تا تبدیل به یک استثنای مدیریت نشده شود):
اکنون اگر برنامه را اجرا کنیم، چنین پیامی، در کنسول developer tools ظاهر میشود و مشخص است از فایل AppErrorHandler صادر شدهاست:
افزودن ToastyService به AppErrorHandler
در ادامه میخواهیم بجای console.log از ToastyService برای نمایش خطاهای مدیریت نشدهی برنامه در کلاس AppErrorHandler استفاده کنیم:
به همین منظور سرویس آنرا به سازندهی کلاس AppErrorHandler تزریق کرده و سپس از آن به نحو متداولی در متد handleError استفاده میکنیم. به این ترتیب بجای دهها و یا صدها قسمت مدیریت err=>this.toastyService.error در برنامه، تنها یک مورد مدیریت مرکزی را خواهیم داشت.
مشکل اول! اکنون اگر برنامه را اجرا کنیم، در کنسول developer tools چنین خطایی ظاهر میشود:
به این معنا که Angular قادر نیست وهلهای از AppErrorHandler را ایجاد کند؛ چون نمیداند که چگونه باید پارامتر سازندهی ToastyService را وهله سازی و تزریق نماید. علت اینجا است که کار آغاز کلاس ویژهی ErrorHandler سراسری، پیش از کار بارگذاری ماژول مرتبط با ToastyService انجام میشود. به همین جهت، این مورد جزو معدود مواردی است که باید به صورت دستی تزریق شود:
در اینجا توسط Inject decorator، کار تزریق دستی ToastyService انجام خواهد شد. اکنون اگر برنامه را مجدد اجرا کنیم، خطای قبلی برطرف شده؛ یعنی کلاس AppErrorHandler با موفقیت وهله سازی شدهاست.
مشکل دوم! اینبار برنامه را اجرا کنید. سپس گروهی را انتخاب نمائید. مشاهده میکنید که خطایی نمایش داده نشد؛ هرچند در کنسول developer tools میتوان اثری از آن را مشاهده کرد. مجددا گروه دیگری را انتخاب کنید، در این بار دوم است که خطای ارائه شدهی توسط this.toastyService.error ظاهر میشود. توضیح آن نیاز به بررسی مفهومی به نام Zones در Angular دارد.
مفهوم Zones در Angular
زمانیکه متد this.toastyService.error در یک کامپوننت برنامه مورد استفاده قرار گرفت، به خوبی کار میکرد و در همان بار اول فراخوانی، پیام را نمایش میداد. اما با انتقال آن به کلاسAppErrorHandler ، این قابلیت از کار افتاد. علت اینجا است که زمینهی اجرایی این قطعه کد، اکنون خارج از Zone یا ناحیهی Angular است و به همین دلیل متوجه تغییرات آن نمیشود. Zone زمینهی اجرایی اعمال async است و اگر به فایل package.json یک برنامهی Angular دقت کنید، بستهی zone.js، یکی از وابستگیهای همراه آن است.
تغییرات حالت برنامه، توسط یکی از اعمال ذیل رخ میدهند:
الف) بروز رخدادهایی مانند کلیک، ورود اطلاعات و یا ارسال فرم
ب) اعمال Ajax ایی
ج) استفاده از Timers مانند استفاده از setTimeout و setInterval
هر سه مورد یاد شده از نوع async بوده و زمانیکه رخ میدهند، حالت برنامه را تغییر خواهند داد. Angular نیز تنها به این موارد علاقمند بوده و به آنها در جهت به روز رسانی رابط کاربری برنامه واکنش نشان میدهد.
برای مثال this.toastyService.error دارای خاصیتی است به نام timeout: 5000 که در آن، مورد «ج» فوق رخ میدهد؛ یعنی یک Timer پس از 5 ثانیه سبب بسته شدن آن خواهد شد. به همین جهت است که اگر پیش از پایان این 5 ثانیه مجددا درخواست واکشی لیست محصولات یک گروه را بدهیم، خطای مربوطه مشاهده میشود. چون Angular زمینهی اجرایی لازم را فراهم کرده (یا همان Zone در اینجا) و مجبور به واکنش به عملیات async از نوع Timer است.
برای دسترسی به امکانات کتابخانهی zone.js، میتوان از طریق تزریق سرویس آن به نام NgZone به سازندهی کلاس شروع کرد:
در اینجا فراخوانی this.ngZone.run سبب میشود تا درخواست نمایش خطای رخداده وارد Angular Zone شده و بلافاصله سبب نمایش آن گردد:
چند نکته
1- اگر میخواهید علاوه بر رخدادگردانی سراسری خطاها، این خطاها را به محل اصلی آنها نیز انتشار دهید، نیاز است سطر throw error را در انتهای متد handleError نیز ذکر کنید. در غیر اینصورت، کار در همینجا به پایان خواهد رسید و این خطاها دیگر منتشر نمیشوند.
2- روش دریافت URL جاری صفحه را نیز در اینجا مشاهده میکنید. این اطلاعات میتوانند جهت ارسال به سرور برای ثبت و بررسیهای بعدی مفید باشند.
3- مقدار new Error().stack معادل stack trace جاری است و تقریبا در تمام مرورگرهای جدید پشتیبانی میشود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-07.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
و در دومی دستورات ذیل را اجرا کنید:
اکنون میتوانید برنامه را در آدرس http://localhost:5000 مشاهده و اجرا کنید.
نمایش پیامها و اخطارهای یک برنامهی Angular توسط ng2-toasty
در مطلب «ایجاد Drop Down Listهای آبشاری در Angular» در قسمت دریافت اطلاعات drop down دوم از سرور، اگر کاربر مجددا گروه را بر روی حالت «لطفا گروهی را انتخاب کنید ...» قرار دهد، مقدار categoryId به undefined تغییر میکند:
fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { return; }
پیشنیازهای کار با کامپوننت Angular2 Toasty توسط یک برنامهی Angular CLI
برای کار با کامپوننت Angular2 Toasty، ابتدا از طریق خط فرمان به پوشهی ریشهی برنامه وارد شده و سپس دستور ذیل را صادر میکنیم:
> npm install ng2-toasty --save
یک نکته: اگر در حین اجرای این دستور به خطای ذیل برخوردید:
npm ERR! Error: EPERM: operation not permitted, rename
پس از آن نیاز است یکی از شیوهنامههایی را که در تصویر فوق ملاحظه میکنید، در فایل angular-cli.json. مشخص کنیم:
"styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "../node_modules/ng2-toasty/bundles/style-bootstrap.css", "styles.css" ],
سپس باید به فایل src\app\app.module.ts مراجعه کرد و ماژول این کامپوننت را معرفی نمود:
import { ToastyModule } from "ng2-toasty"; @NgModule({ imports: [ BrowserModule, ToastyModule.forRoot(),
همچنین در همین قسمت، به فایل قالب src\app\app.component.html مراجعه کرده و selector tag این کامپوننت را در ابتدای آن تعریف میکنیم:
<ng2-toasty [position]="'top-right'"></ng2-toasty>
نمایش یک پیام خطا توسط ToastyService
اکنون که کار برپایی کامپوننت Angular2 Toasty به پایان رسید، کار کردن با آن به سادگی تزریق سرویس آن به سازندهی یک کامپوننت و فراخوانی متدهای info، success ، wait ، error و warning آن است:
import { ToastyService, ToastOptions } from "ng2-toasty"; export class ProductGroupComponent implements OnInit { constructor( private productItemsService: ProductItemsService, private toastyService: ToastyService) { } fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { this.toastyService.error(<ToastOptions>{ title: "Error!", msg: "Please select a category.", theme: "bootstrap", showClose: true, timeout: 5000 }); return; }
- سپس ToastyService به سازندهی کلاس کامپوننت مدنظر تزریق شدهاست تا بتوان از امکانات آن استفاده کرد.
- در ادامه، فراخوانی متد this.toastyService.error سبب نمایش اخطار قرمز رنگی میشود که تصویر آنرا در ابتدای مطلب جاری مشاهده کردید.
- علت ذکر <ToastOptions> در اینجا این است که وجود آن سبب خواهد شد تا intellisense در VSCode فعال شود و پس از آن بتوان تمام گزینههای این متد و تنظیمات را بدون مراجعهی به مستندات آن از طریق intellisense یافت و درج کرد:
مدیریت سراسری خطاهای مدیریت نشده، در یک برنامهی Angular
در برنامههای Angular از این دست کدها بسیار مشاهده میشوند:
this.productItemsService.getCategories().subscribe( data => { this.categories = data; }, err => console.log("get error: ", err) );
به همین منظور کلاس جدیدی را به صورت ذیل در پوشهی src\app اضافه میکنیم:
> ng g cl app.error-handler
installing class create src\app\app.error-handler.ts
import { ErrorHandler } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { handleError(error: any): void { console.log("Error:", error); } }
اکنون نیاز است این ErrorHandler سفارشی را بجای نمونهی اصلی به برنامه معرفی کنیم. برای این منظور به فایل src\app\app.module.ts مراجعه کرده و تغییرات ذیل را اعمال میکنیم:
import { NgModule, ErrorHandler } from "@angular/core"; import { AppErrorHandler } from "./app.error-handler"; @NgModule({ providers: [ { provide: ErrorHandler, useClass: AppErrorHandler } ]
اکنون برای آزمایش آن، در کدهای سمت سرور مطلب «ایجاد Drop Down Listهای آبشاری در Angular»، یک استثنای عمدی را قرار میدهیم:
[HttpGet("[action]/{categoryId:int}")] public async Task<IActionResult> GetProducts(int categoryId) { throw new Exception();
برای اینکه AppErrorHandler، مورد استفاده قرار گیرد، قسمت err دریافت لیست محصولات را نیز حذف میکنیم (تا تبدیل به یک استثنای مدیریت نشده شود):
this.productItemsService.getProducts(categoryId).subscribe( data => { this.products = data; this.isLoadingProducts = false; }// , // err => { // console.log("get error: ", err); // this.isLoadingProducts = false; // } );
افزودن ToastyService به AppErrorHandler
در ادامه میخواهیم بجای console.log از ToastyService برای نمایش خطاهای مدیریت نشدهی برنامه در کلاس AppErrorHandler استفاده کنیم:
import { ToastyService, ToastOptions } from "ng2-toasty"; import { ErrorHandler } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { constructor(private toastyService: ToastyService) { } handleError(error: any): void { // console.log("Error:", error); this.toastyService.error(<ToastOptions>{ title: "Error!", msg: "Fatal error!", theme: "bootstrap", showClose: true, timeout: 5000 }); } }
مشکل اول! اکنون اگر برنامه را اجرا کنیم، در کنسول developer tools چنین خطایی ظاهر میشود:
Uncaught Error: Can't resolve all parameters for AppErrorHandler: (?).
import { ErrorHandler, Inject } from "@angular/core"; export class AppErrorHandler implements ErrorHandler { constructor( @Inject(ToastyService) private toastyService: ToastyService ) { }
مشکل دوم! اینبار برنامه را اجرا کنید. سپس گروهی را انتخاب نمائید. مشاهده میکنید که خطایی نمایش داده نشد؛ هرچند در کنسول developer tools میتوان اثری از آن را مشاهده کرد. مجددا گروه دیگری را انتخاب کنید، در این بار دوم است که خطای ارائه شدهی توسط this.toastyService.error ظاهر میشود. توضیح آن نیاز به بررسی مفهومی به نام Zones در Angular دارد.
مفهوم Zones در Angular
زمانیکه متد this.toastyService.error در یک کامپوننت برنامه مورد استفاده قرار گرفت، به خوبی کار میکرد و در همان بار اول فراخوانی، پیام را نمایش میداد. اما با انتقال آن به کلاسAppErrorHandler ، این قابلیت از کار افتاد. علت اینجا است که زمینهی اجرایی این قطعه کد، اکنون خارج از Zone یا ناحیهی Angular است و به همین دلیل متوجه تغییرات آن نمیشود. Zone زمینهی اجرایی اعمال async است و اگر به فایل package.json یک برنامهی Angular دقت کنید، بستهی zone.js، یکی از وابستگیهای همراه آن است.
تغییرات حالت برنامه، توسط یکی از اعمال ذیل رخ میدهند:
الف) بروز رخدادهایی مانند کلیک، ورود اطلاعات و یا ارسال فرم
ب) اعمال Ajax ایی
ج) استفاده از Timers مانند استفاده از setTimeout و setInterval
هر سه مورد یاد شده از نوع async بوده و زمانیکه رخ میدهند، حالت برنامه را تغییر خواهند داد. Angular نیز تنها به این موارد علاقمند بوده و به آنها در جهت به روز رسانی رابط کاربری برنامه واکنش نشان میدهد.
برای مثال this.toastyService.error دارای خاصیتی است به نام timeout: 5000 که در آن، مورد «ج» فوق رخ میدهد؛ یعنی یک Timer پس از 5 ثانیه سبب بسته شدن آن خواهد شد. به همین جهت است که اگر پیش از پایان این 5 ثانیه مجددا درخواست واکشی لیست محصولات یک گروه را بدهیم، خطای مربوطه مشاهده میشود. چون Angular زمینهی اجرایی لازم را فراهم کرده (یا همان Zone در اینجا) و مجبور به واکنش به عملیات async از نوع Timer است.
برای دسترسی به امکانات کتابخانهی zone.js، میتوان از طریق تزریق سرویس آن به نام NgZone به سازندهی کلاس شروع کرد:
import { ToastyService, ToastOptions } from "ng2-toasty"; import { ErrorHandler, Inject, NgZone } from "@angular/core"; import { LocationStrategy, PathLocationStrategy } from "@angular/common"; export class AppErrorHandler implements ErrorHandler { constructor( @Inject(NgZone) private ngZone: NgZone, @Inject(ToastyService) private toastyService: ToastyService, @Inject(LocationStrategy) private locationProvider: LocationStrategy ) { } handleError(error: any): void { // console.log("Error:", error); const url = this.locationProvider instanceof PathLocationStrategy ? this.locationProvider.path() : ""; const message = error.message ? error.message : error.toString(); this.ngZone.run(() => { this.toastyService.error(<ToastOptions>{ title: "Error!", msg: `URL:${url} \n ERROR:${message}`, theme: "bootstrap", showClose: true, timeout: 5000 }); }); // IMPORTANT: Rethrow the error otherwise it gets swallowed // throw error; } }
چند نکته
1- اگر میخواهید علاوه بر رخدادگردانی سراسری خطاها، این خطاها را به محل اصلی آنها نیز انتشار دهید، نیاز است سطر throw error را در انتهای متد handleError نیز ذکر کنید. در غیر اینصورت، کار در همینجا به پایان خواهد رسید و این خطاها دیگر منتشر نمیشوند.
2- روش دریافت URL جاری صفحه را نیز در اینجا مشاهده میکنید. این اطلاعات میتوانند جهت ارسال به سرور برای ثبت و بررسیهای بعدی مفید باشند.
3- مقدار new Error().stack معادل stack trace جاری است و تقریبا در تمام مرورگرهای جدید پشتیبانی میشود.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-07.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
>npm install >ng build --watch
>dotnet restore >dotnet watch run
-
برای دیباگ بهتر برنامههای Angular 2 افزونهی Angular Augury را نصب کنید.
- ngIf، کل المان را از HTML نهایی رندر شده حذف میکند یا برعکس؛ اما سبب حذف آن از قالب مخصوصی که تحت کنترل و کامپایل Angular است نمیشود. در اینحالت نه کامپوننت مجددا رندر میشود و نه حلقهی for مجددا اجرا خواهد شد. فقط قسمتی که Angular آنرا مخفی و از HTML نهایی حذف کرده بوده، مجددا به HTML نهایی اضافه میشود.
- ngIf، کل المان را از HTML نهایی رندر شده حذف میکند یا برعکس؛ اما سبب حذف آن از قالب مخصوصی که تحت کنترل و کامپایل Angular است نمیشود. در اینحالت نه کامپوننت مجددا رندر میشود و نه حلقهی for مجددا اجرا خواهد شد. فقط قسمتی که Angular آنرا مخفی و از HTML نهایی حذف کرده بوده، مجددا به HTML نهایی اضافه میشود.
در پست قبلی با مفاهیم کنترلر و مدل در AngularJs آشنا شدید. قصد دارم روشی را بررسی کنم که یک منبع داده را بین کنترلهای تعریف شده در یک ماژول را به اشتراک بگذاریم.
ابتدا یک فایل جاوااسکریپ به نام module1 ایجاد میکنیم . در این فایل ابتدا ماژول خود را به Angular معرفی کرده و سپس با استفاده از دستور factory سرویس مورد نظر برای به اشتراک گذاری داده را میسازیم:
همان طور که در پست قبلی شرح داده شده برای تعریف ماژول از دستور angular.module استفاده میکنیم. در خط بعدی یک سرویس به نام BookData را با استفاده از دستور factory در ماژول مربوطه ساخته میشود. تابع مورد نظر بک آرایه از کتابها را که هر کدام از آنها شامل کد و نام است برگشت میدهد. قصد داریم کنترلهای تعریف شده در ماژول myApp بتوانند به این لیست این کتابها دسترسی داشته باشند. در این مرحله ابتدا یک کنترلر به نام به controller1 به صورت زیر میسازیم:
تنها نکته قابل ذکر، تزریق مقادیر scope$ و BookData به تابع سازنده کنترلر مربوطه است. از scope$ برای مقید سازی مقادیر مدل به عناصر dom در view استفاده میشود و BookData در این جا دقیقا به مقدار برگشت داده شده از سرویس BookData اشاره میکند(نام سرویس مورد نظر دقیقا باید با مقداری که به عنوان آرگومان اول در تابع factory پاس میدهید یکی باشد). در نتیجه این مقدار را به متغیر books در scope$ نسبت میدهیم. برای کنترلر دوم نیز همین مراحل را تکرار میکنیم:
در View مورد نظر نیز یک ارجاع به فایل ساخته شده بالا خواهیم داشت و سپس کدهای مربوط به نمایش را به صورت زیر مینویسیم(البته ارجاع به فایل اصلی angular.js فراموش نشود):
ابتدا در تگ div اول با استفاده از ng-app محدوده ماژول مورد نظر در صفحه را تعیین کرده سپس با استفاده از تگهای div جداگانه هر کدام از نواحی تحت کنترل مربوط به کنترلرهای تعریف شده را مشخص میکنیم.
با استفاده از ng-repeat به راحتی در بین آرایه کتابها پیمایش کرده و لیست مورد نظر در صفحه نمایش داده میشود. (توضیحات مربوط به ng-repeat و {{}} در پست قبلی شرح داده شده است). خروجی به صورت زیر خواهد بود. واضح است که اطلاعات نمایش داده شده توسط هر دو کنترلر به دلیل استفاده از منبع داده ای یکسان، به یک شکل خواهد بود.
ابتدا یک فایل جاوااسکریپ به نام module1 ایجاد میکنیم . در این فایل ابتدا ماژول خود را به Angular معرفی کرده و سپس با استفاده از دستور factory سرویس مورد نظر برای به اشتراک گذاری داده را میسازیم:
var app = angular.module('myApp', []); app.factory('BookData', function () { var books = [ { code: 1, name: 'book1', }, { code: 2, name: 'book2', }, { code: 3, name: 'book3', }, { code: 4, name: 'book4', }, { code: 5, name: 'book5', } ]; return books; });
app.controller('controller1', function ($scope, BookData) { $scope.books = BookData; });
app.controller('controller2', function ($scope, BookData) { $scope.books = BookData; });
<script type="text/javascript" src="~/scripts/app/controller1.js"></script>
<div ng-app="myApp"> <div ng-controller="controller1"> <p>Data from controller1</p> <table> <tr ng-repeat="book in books"> <td> {{book.code}} </td> <td> {{book.name}} </td> </tr> </table> </div> <div ng-controller="controller2"> <p>Data from controller2</p> <table> <tr ng-repeat="book in books"> <td> {{book.code}} </td> <td> {{book.name}} </td> </tr> </table> </div> </div>
با استفاده از ng-repeat به راحتی در بین آرایه کتابها پیمایش کرده و لیست مورد نظر در صفحه نمایش داده میشود. (توضیحات مربوط به ng-repeat و {{}} در پست قبلی شرح داده شده است). خروجی به صورت زیر خواهد بود. واضح است که اطلاعات نمایش داده شده توسط هر دو کنترلر به دلیل استفاده از منبع داده ای یکسان، به یک شکل خواهد بود.
نظرات اشتراکها
چرا از آنگولار به ری اکت + ری داکس سوئیچ کردم!
- فسلفه React مبتنی بر مخلوط کردن جاوا اسکریپت و HTML با هم هست در فایلهای JSX (نوشتن HTML با کدهای جاوا اسکریپت). به این صورت شما مزیتهای ذاتی HTML و CSS را یکجا از دست میدید؛ چون دیگه نمیتونید HTML جدا یا CSS جدای از جاوا اسکریپت را داشته باشین. در حالیکه در Angular این دو یا این سه (TypeScript، HTML و CSS) از هم جدا هستند که مزیت آن دسترسی به انواع ادیتورهایی هست که بدون اینکه برای Angular نوشته شده باشند، در همان بدو معرفی آن، با آن سازگار هستند که سادگی توسعه را به همراه داره. شاید تولید کامپوننتهای ساده React تولید شده با کدهای جاوا اسکریپتی ساده باشه، اما کمی که حجم آن بیشتر شد، کنترل و مدیریت این مخلوط، سختتر و سختتر میشه و به علاوه مخلوط کردن کدهای یک فریم ورک با HTML و CSS خیلی شبیه به PHP کلاسیک و یا ASP کلاسیک هست و این روزها کسی را پیدا نمیکنید که برای پروژههای واقعی حتی از PHP در حالت کلاسیک آن بدون یک فریم ورک جانبی استفاده کنه. در Angular از همان بدو امر مباحث طراحی ماژولها، کامپوننتها و جدا سازی کدها به صورت ذاتی طراحی شدهاند.
- مزیت کار کردن با TypeScript در مقایسه با ES6 خالص در React، امکان دسترسی به کامپایل آفلاین هست و مباحث پیشرفتهی کامپایلر مانند tree-shaking (حذف کدهای مرده) و AOT (a head of time compilation) که سبب میشن هم حجم نهایی کمتری تولید شود و هم پیش از اجرای برنامه در مرورگر و سپس یافتن باگهای احتمالی در زمان اجرا، پیش از موعد و توسط کامپایلر این باگها گزارش شوند. اگر قصد داشته باشید به یک چنین کیفیت و بررسی کدی در React برسید، باید تعداد آزمونهای واحد قابل توجهی را داشته باشین تا بتونید یافتن مشکلاتی را که کامپایلر TypeScript گوشزد میکند، شبیه سازی کنید. همچنین شما در TypeScript میتونید به تمام امکانات پیشرفتهی زبان جاوا اسکریپت (حتی پس از ES6) دسترسی داشته باشید، اما کد نهایی جاوا اسکریپتی تولید شدهی توسط آنرا برای ES5 که تمام مرورگرها از آن پشتیبانی میکنند، تولید کنید که این هم خودش یک مزیت مهم هست. بنابراین TypeScript فقط یک static type checker ساده نیست.
- اینکه Angular یک فریمورک هست به خودی خودش یک مزیت مهم هست نسبت به React که یک کتابخانه است و اجزای آن باید از منابع مختلفی تهیه شوند. فریم ورک یعنی به روز رسانیهای منظم تمام اجزای آن توسط خود تیم Angular و سازگاری کامل و یکدست هر جزء با نگارش فعلی یا همان آخرین نگارش موجود. اگر با دنیای وابستگیهای ثالث در یک پروژهی واقعی کار کرده باشید به خوبی میدونید که هر چقدر تعداد آنها کمتر باشند، نگهداری طولانی مدت آن پروژه آسانتر میشود؛ چون روزی ممکن است آن کتابخانهی ثالث دیگر توسعه پیدا نکند، یا منسوخ شود یا دیرتر از آخرین نگارش ارائه شده به روز رسانی شود. مزیت داشتن یک فریم ورک یکدست، درگیر نشدن با این مسایل است؛ خصوصا اینکه عموما کتابخانههای ثالث کیفیتشون در حد کتابخانهی اصلی نیست و اینکه مثلا خود تیم Angular ماژول روتر، اعتبارسنجی یا فرمهای اون رو توسعه میده، قطعا کیفیتشون از کتابخانههای ثالث دیگه بهتر هست.
- در مورد سرعت و کارآیی و حتی مصرف حافظه، مطابق یک benchmarck خیلی معتبر، وضعیت Angular اندکی بهتر از React است؛ هرچند در کل از این لحاظ به هم نزدیک هستند.
- این مباحث انحصاری شدن و اینها هم در مورد محصولات سورس باز، زیاد مفهومی ندارند و بیشتر یکسری شعار ایدئولوژیک هست توسط کسانیکه حتی تغییر رفتار این شرکتها را هم دنبال نمیکنند و منابع و ماخذی رو که مطالعه کردن مربوط به یک دهه قبل هست.
- مزیت کار کردن با TypeScript در مقایسه با ES6 خالص در React، امکان دسترسی به کامپایل آفلاین هست و مباحث پیشرفتهی کامپایلر مانند tree-shaking (حذف کدهای مرده) و AOT (a head of time compilation) که سبب میشن هم حجم نهایی کمتری تولید شود و هم پیش از اجرای برنامه در مرورگر و سپس یافتن باگهای احتمالی در زمان اجرا، پیش از موعد و توسط کامپایلر این باگها گزارش شوند. اگر قصد داشته باشید به یک چنین کیفیت و بررسی کدی در React برسید، باید تعداد آزمونهای واحد قابل توجهی را داشته باشین تا بتونید یافتن مشکلاتی را که کامپایلر TypeScript گوشزد میکند، شبیه سازی کنید. همچنین شما در TypeScript میتونید به تمام امکانات پیشرفتهی زبان جاوا اسکریپت (حتی پس از ES6) دسترسی داشته باشید، اما کد نهایی جاوا اسکریپتی تولید شدهی توسط آنرا برای ES5 که تمام مرورگرها از آن پشتیبانی میکنند، تولید کنید که این هم خودش یک مزیت مهم هست. بنابراین TypeScript فقط یک static type checker ساده نیست.
- اینکه Angular یک فریمورک هست به خودی خودش یک مزیت مهم هست نسبت به React که یک کتابخانه است و اجزای آن باید از منابع مختلفی تهیه شوند. فریم ورک یعنی به روز رسانیهای منظم تمام اجزای آن توسط خود تیم Angular و سازگاری کامل و یکدست هر جزء با نگارش فعلی یا همان آخرین نگارش موجود. اگر با دنیای وابستگیهای ثالث در یک پروژهی واقعی کار کرده باشید به خوبی میدونید که هر چقدر تعداد آنها کمتر باشند، نگهداری طولانی مدت آن پروژه آسانتر میشود؛ چون روزی ممکن است آن کتابخانهی ثالث دیگر توسعه پیدا نکند، یا منسوخ شود یا دیرتر از آخرین نگارش ارائه شده به روز رسانی شود. مزیت داشتن یک فریم ورک یکدست، درگیر نشدن با این مسایل است؛ خصوصا اینکه عموما کتابخانههای ثالث کیفیتشون در حد کتابخانهی اصلی نیست و اینکه مثلا خود تیم Angular ماژول روتر، اعتبارسنجی یا فرمهای اون رو توسعه میده، قطعا کیفیتشون از کتابخانههای ثالث دیگه بهتر هست.
- در مورد سرعت و کارآیی و حتی مصرف حافظه، مطابق یک benchmarck خیلی معتبر، وضعیت Angular اندکی بهتر از React است؛ هرچند در کل از این لحاظ به هم نزدیک هستند.
- این مباحث انحصاری شدن و اینها هم در مورد محصولات سورس باز، زیاد مفهومی ندارند و بیشتر یکسری شعار ایدئولوژیک هست توسط کسانیکه حتی تغییر رفتار این شرکتها را هم دنبال نمیکنند و منابع و ماخذی رو که مطالعه کردن مربوط به یک دهه قبل هست.