آموزش انگولار رایگان (Angular) | از مقدماتی تا پیشرفته

۵۴۵۲ بازدید
آخرین به‌روزرسانی: ۲۲ اسفند ۱۴۰۲
زمان مطالعه: ۹۸ دقیقه
آموزش انگولار رایگان (Angular) | از مقدماتی تا پیشرفته

انگولار یک فریمورک مبتنی بر جاوا اسکریپت متن-باز و سمت کلاینت است که به منظور توسعه وب‌اپلیکیشن‌ها مورد استفاده قرار می‌گیرد. در واقع انگولار یکی از بهترین فریمورک‌ها برای توسعه اپلیکیشن‌های تک‌صفحه‌ای (SPA) محسوب می‌شود. در این مقاله به آموزش انگولار از طریق معرفی مباحث نظری و بررسی مثال‌های عملی به صورت هم‌زمان خواهیم پرداخت.

فهرست مطالب این نوشته

ویژگی‌های انگولار

  • انگولار یک فریمورک ساختاری بر مبنای الگوی MVC است.
  • انگولار فریمورکی برای توسعه اپلیکیشن‌های تک‌صفحه‌ای (SPA) است.
  • این کتابخانه از امکان قالب‌سازی سمت کلاینت پشتیبانی می‌کند.
  • انگولار امکان اجرای تست unit را فراهم ساخته و از این رو کد انگولار می‌تواند پیش از توزیع مورد تست قرار گیرد.

پیش از ادامه این مبحث لازم است یادآور شویم که می‌توانید انگولار را با استفاده از مجموعه آموزش انگولار، مقدماتی تا پیشرفته
فرادرس یاد بگیرید.

مزیت‌های انگولار

چنان که اشاره کردیم انگولار یک فریمورک متن-باز است که به وسیله گوگل نگهداری می‌شود. این کتابخانه قادر به خوانش بسیار سریع صفحه HTML است که موجب می‌شود بتوانیم خصوصیت‌های سفارشی تگ اضافی را در صفحه جاسازی کنیم. این خصوصیت‌ها به صورت دایرکتیوهایی تفسیر می‌شوند که به فریمورک انگولار دستور می‌دهند تا بخش‌های ورودی و خروجی صفحه را به یک مدل اتصال دهد که به صورت متغیرهای استاندارد جاوا اسکریپت بازنمایی می‌شود. مقادیر این متغیرهای جاوا اسکریپت را می‌توان به صورت دستی درون کد تعیین کرد یا از منابع استاتیک یا دینامیک JSON شامل داده‌های سمت سرور از قبیل REST API یا غیره به دست آورد.

مزایای عمده استفاده از انگولار در وب‌اپلیکیشن‌ها به شرح زیر است:

  • انگولار به جای افزودن کد HTML داخلی خاص خود، به صورت مستقیم DOM صفحه را دست‌کاری می‌کند که به مراتب سریع‌تر است.
  • «اتصال داده» (Data Binding) در زمان هر تغییر در کنترل یا مقدار رخ نمی‌دهد. بدین ترتیب چیزی به نام «شنونده تغییر» (Change Listener) وجود ندارد، بلکه در نقاط خاصی از اجرای جاوا اسکریپت این رصد تغییرات انجام می‌یابند. این موضوع موجب بهبود چشمگیری در عملکرد می‌شود، چون یک به‌روزرسانی منفرد دسته‌ای Model/View جایگزین صدها رویداد تغییر داده آبشاری می‌شود.
  • هیچ نیازی به استفاده از تابع‌های observable وجود ندارد. انگولار DOM صفحه را تحلیل می‌کند و اتصال‌ها را بر مبنای خصوصیت‌های عنصر خاص انگولار می‌سازد. این امر نیازمند کدنویسی کمتری است یعنی کد منسجم‌تر است، درک آن آسان‌تر است و خطاهای کمتری دارد.
  • امکان بسط قابلیت‌هایی از قبیل تزریق وابستگی، مسیریابی، انیمیشن، کپسوله‌سازی نما و موارد دیگر وجود دارد.
  • از سوی IDE-های IntelliJ IDEA و Visual Studio.NET پشتیبانی می‌شود.
  • از سوی گوگل و جامعه توسعه‌دهندگان عالی آن پشتیبانی می‌شود.
  • انگولار راه‌حلی عالی برای توسعه سریع در سمت فرانت است. این کتابخانه به هیچ پلاگین یا فریمورک دیگری نیاز ندارد.
  • انگولار آمادگی تست unit را دارد و این یکی از بهترین مزیت‌های این فریمورک است.

چرا انگولار را فریمورک می‌نامیم؟

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

تاریخچه انگولار

نخستین نسخه انگولار از سوی شخصی به نام «میشکو هیوِری» (Miško Hevery) که یک توسعه‌دهنده در گوگل بود، ‌در سال‌های 2008 و 2009 ایجاد شد. او یک فریمورک برای تسهیل ساخت وب‌اپلیکیشن‌ها طراحی کرده بود. این فریمورک نه برای توسعه‌دهندگان وب، بلکه برای استفاده طراحان وب ارائه شده بود که اطلاعات فنی کمی از دانش برنامه‌نویسی داشتند. به این ترتیب اگر یک وب‌سرور استاتیک داشتید، می‌توانستید یک وب‌اپلیکیشن ساده بسازید. «آدام آبرونز» (Adam abrons) یکی از همکاران میشکو پیشنهاد کرد نام این پروژه انگولار یعنی زاویه‌دار باشد، زیرا HTML شامل «براکت‌های زاویه‌دار» (Angular) است.

در این زمان مدیر میشکو در شرکت گوگل به نام «براد گرین» (Brad Green) از وی خواست که روی یکی از ابزارهای داخلی گوگل به نام «Google Feedback» کار کند. گوگل یک فریمورک به نام GWT داشت که با جاوا نوشته شده بود و به منظور توسعه ابزارهای داخلی این شرکت مورد استفاده قرار می‌گرفت. دو توسعه‌دهنده دیگر به همراه میشکو 17000 خط کد را در طی 6 ماه نوشتند. دلیل این زمان طولانی آن بود که تست کردن این کد کار دشواری بود. برای افزودن یک برچسب در HTML باید کد آن در جاوا نوشته و کامپایل شده به جاوا اسکریپت HTML انتقال می‌یافت تا در مرورگر وب نمایش یابد. میشکو ادعا کرد که می‌تواند کل این پروژه را در طی 2 هفته با استفاده از ابزاری که خود به نام انگولار توسعه داده است، بنویسد.

با این که وی نتوانست ادعای خود را اجرا کند و این کار را در طی 3 هفته انجام داد، اما حجم کد به 1500 خط کاهش یافت. با این حال این پروژه از سوی مدیران ارشد گوگل حمایت نشد و لذا به صورت یک کتابخانه متن-باز منتشر شد و کار از سوی توسعه‌دهندگان خارج از گوگل ادامه یافت.

در ادامه گوگل شرکت DoubleClick را خریداری کرد. این شرکت توسعه‌دهندگان زیاد جاوا نداشت و بنابراین کار با GWT برای آن‌ها دشوار بود، از این رو به دنبال فناوری‌های جایگزینی می‌گشتند و با میشکو و AngularJs مواجه شدند. بدین ترتیب ‌آن‌ها موفق شدند صفحه‌های فرود خود را با بهره‌گیری از انگولارجی‌اس‌ با حجمی ده بار کمتر و سرعتی ده بار بیشتر بنویسند. در ابتدا مخالفت‌های زیادی وجود داشت، چون همه افراد در گوگل از GWT استفاده می‌کردند، اما در نهایت افراد زیادی به این فریمورک جدید روی آوردند. نسخه رسمی یعنی AngularJs v1.0 در مه 2011 منتشر شد و در ادامه نسخه‌های بعدی به ترتیب زیر از سوی گوگل انتشار یافته‌اند.

نسخه انگولارتاریخ
Angular 22016/09/14
Angular 42017/03/23
Angular 52017/11/11
Angular 62018/05/03
Angular 72018/10/18
Angular 82019/08/25
Angular 92020/02/06
Angular 102020/05/04

نکته مهم: این راهنما بر پایه انگولار نسخه 8 نوشته شده است.

پیش‌نیازهای آموزش انگولار

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

  • جدیدترین نسخه Node.js
  • نصب جدیدترین نسخه تایپ اسکریپت
  • نصب یک IDE مانند VSCode یا ویژوال استودیو
  • همچنین باید Angular CLI را نصب کنیم تا بتوانیم پروژه انگولار را اجرا کنیم.

برای توسعه اپلیکیشن‌های انگولار از زبان برنامه‌نویسی تایپ اسکریپت استفاده می‌شود. تایپ اسکریپت یک زبان برنامه‌نویسی رایگان و متن-باز است که از سوی مایکروسافت توسعه یافته و نگهداری می‌شد. تایپ اسکریپت یک سوپرست نحوی صریح از جاوا اسکریپت محسوب می‌شود که قابلیت نوع‌بندی استاتیک اختیاری را به این زبان افزوده است. «آندرس هیلزبرگ» (Anders Hejlsberg) که رهبر معماری زبان #C و خالق زبان‌های دلفی، توربوپاسکال بوده است، روی توسعه تایپ اسکریپت کار کرده است.

شما برای کار روی اپلیکیشن‌های انگولار باید با زبان تایپ اسکریپت آشنا باشید و از آن بهره بگیرید.

ایجاد پروژه جدید با Angular CLI

اگر بخواهیم یک پروژه جدید در انگولار ایجاد کنیم، می‌توانیم از دستورهای CLI بهره بگیریم. به این منظور باید گام‌های زیر را طی کنیم:

  1. برنامه Command Prompt را باز کرد‌ه و یک پوشه ایجاد کنید.
  2. دستور  ng new AngularDemo- را اجرا کنید.
  3. در زمانی که از شما سؤال شود آیا می‌خواهید مسیریابی انگولار به این پروژه اضافه شود، مخالفت خود را با وارد کردن حرف N اعلام کنید.
  4. نوع استایل‌شیت را به صورت CSS انتخاب کرده و دکمه اینتر را بزنید.

آموزش انگولار

بدین ترتیب Angular CLI فیلد‌های لازم برای اجرای پروژه‌های انگولار را همراه با پکیج‌های مرتبط که در پوشه node_modules دانلود می‌شوند ایجاد می‌کند.

ساختار پروژه‌های انگولار

Angular CLI در زمان ایجاد پروژه انگولار، پوشه جدیدی با نام پروژه ایجاد می‌کند. اینک می‌توانید پروژه را در هر ادیتور کدی مانند Visual Studio Code (+) یا Microsoft Visual Studio باز کنید. این پوشه پروژه شامل ساختار زیر است:

آموزش انگولار

پروژه ایجاد شده شامل پوشه‌های زیر است:

  1. e2e – این پوشه به منظور تست «سربه‌سر» (end to end) استفاده می‌شود و شامل فایل‌های پیکربندی مرتبط با اجرای تست unit پروژه‌ها است.
  2. node_modules – این پوشه شامل پکیج‌های دانلود شده بسته به پیکربندی است.
  3. Src - این پوشه شامل سورس کد اصلی پروژه است و سه زیرپوشه دارد:
    1. app – پوشه app شامل فایل‌های مرتبط با پروژه انگولار مانند کامپوننت‌ها، فایل‌های HTML و غیره است.
    2. assets - پوشه assets شامل هر نوع فایل استاتیک از قبیل تصاویر، استایل‌شیت، فایل‌های کتابخانه‌های خاص جاوا اسکریپت و غیره است.
    3. environments - پوشه environments شامل فایل‌های مرتبط با محیط است که در زمان توسعه یا بیلد کردن پروژه‌ها مورد نیاز هستند.

فایل‌های متفاوت پیکربندی

هرزمان که یک پروژه مبتنی بر انگولار را با استفاده از Angular CLI می‌سازیم، 3 فایل مختلف پیکربندی ایجاد می‌شوند که به پیکربندی پروژه‌ها و وابستگی‌ها کمک می‌کنند. این فایل‌ها به شرح زیر هستند.

فایل tsconfig.json

فایل‌های tsconfig.json درون پوشه root پوشه قرار دارند و به این معنی هستند که این پروژه مبتنی بر تایپ اسکریپت است. فایل tsconfig.json فایل‌های root و گزینه‌های کامپایلر مورد نیاز برای کامپایل پروژه را تعیین می‌کند. یک فایل نمونه tsconfig.json مانند زیر است:

1{  
2  "compileOnSave": true,  
3  "compilerOptions": {  
4    "baseUrl": "./",  
5    "outDir": "./dist/out-tsc",  
6    "sourceMap": true,  
7    "declaration": false,  
8    "module": "esnext",  
9    "moduleResolution": "node",  
10    "emitDecoratorMetadata": true,  
11    "experimentalDecorators": true,  
12    "importHelpers": true,  
13    "target": "es2015",  
14    "typeRoots": [  
15      "node_modules/@types"  
16    ],  
17    "lib": [  
18      "es2018",  
19      "dom"  
20    ]  
21  }  
22}

فایل package.json

فایل package.json اساساً یک فایل JSON است که شامل همه اطلاعات مرتبط با پکیج‌های مورد نیاز برای پروژه است. ضمناً به کمک این فایل‌های پیکربندی، می‌توانیم نام پروژه و نسخه آن را با استفاده از مشخصه‌های name و version تعیین کنیم. همچنین می‌توانیم تعریف build پروژه را با استفاده از این فایل ارائه کنیم.

1{  
2  "name": "angular8-demo",  
3  "version": "0.0.0",  
4  "scripts": {  
5    "ng": "ng",  
6    "start": "ng serve",  
7    "build": "ng build",  
8    "test": "ng test",  
9    "lint": "ng lint",  
10    "e2e": "ng e2e"  
11  },  
12  "private": true,  
13  "dependencies": {  
14    "@angular/animations": "~8.0.0",  
15    "@angular/common": "~8.0.0",  
16    "@angular/compiler": "~8.0.0",  
17    "@angular/core": "~8.0.0",  
18    "@angular/forms": "~8.0.0",  
19    "@angular/platform-browser": "~8.0.0",  
20    "@angular/platform-browser-dynamic": "~8.0.0",  
21    "@angular/router": "~8.0.0",  
22    "rxjs": "~6.4.0",  
23    "tslib": "^1.9.0",  
24    "zone.js": "~0.9.1"  
25  },  
26  "devDependencies": {  
27    "@angular-devkit/build-angular": "~0.800.0",  
28    "@angular/cli": "~8.0.2",  
29    "@angular/compiler-cli": "~8.0.0",  
30    "@angular/language-service": "~8.0.0",  
31    "@types/node": "~8.9.4",  
32    "@types/jasmine": "~3.3.8",  
33    "@types/jasminewd2": "~2.0.3",  
34    "codelyzer": "^5.0.0",  
35    "jasmine-core": "~3.4.0",  
36    "jasmine-spec-reporter": "~4.2.1",  
37    "karma": "~4.1.0",  
38    "karma-chrome-launcher": "~2.2.0",  
39    "karma-coverage-istanbul-reporter": "~2.0.1",  
40    "karma-jasmine": "~2.0.1",  
41    "karma-jasmine-html-reporter": "^1.4.0",  
42    "protractor": "~5.4.0",  
43    "ts-node": "~7.0.0",  
44    "tslint": "~5.15.0",  
45    "typescript": "~3.4.3"  
46  }  
47}

فایل angular.json

این فایل به پیکربندی محیط توسعه اپلیکیشن انگولار مربوط است و با داشتن ساختاری مبتنی بر json همه اطلاعات مرتبط با بیلد و توزیع پروژه را در خود جای داده است. این فایل به سیستم اعلام می‌کند که در زمان استفاده از دستورهای ng build یا ng serve کدام فایل‌ها باید تغییر یابند.

فایل main.ts

این فایل به عنوان نقطه ورودی اپلیکیشن انگولار عمل می‌کند. این فایل مسئول عملیات بوت‌استرپ ماژول‌های انگولار است. فایل main.ts شامل برخی گزاره‌های ایمپورت مرتبط با ماژول‌ها و برخی پیکربندی‌های راه‌اندازی اولیه از قبیل موارد زیر است:

  • enableProdMode – این گزینه برای غیر فعال ساختن محیط توسعه انگولار و فعال‌سازی حالت پروداکشن استفاده می‌شود. غیر فعال‌سازی حالت توسعه موجب خاموش شدن assertion-ها و دیگر برسی ‌های مرتبط با مدل درون فریمورک می‌شود.
  • platformBrowserDynamic – این گزینه برای بوت‌استرپ کردن اپلیکیشن انگولار در مرورگر مورد نیاز است.
  • AppModule – این گزینه نشان می‌دهد که ماژول به صورت یک ماژول root در اپلیکیشن‌ها عمل می‌کند.
  • environment – این گزینه ثابت‌های محیط متفاوت را ذخیره می‌کند.
1import { enableProdMode } from '@angular/core';  
2import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';  
3  
4import { AppModule } from './app/app.module';  
5import { environment } from './environments/environment';  
6  
7if (environment.production) {  
8  enableProdMode();  
9}  
10  
11platformBrowserDynamic().bootstrapModule(AppModule)  
12  .catch(err => console.error(err));

متادیتای ngModule@

در هر اپلیکیشن انگولار دست کم یک فایل ماژول انگولار مورد نیاز است. یک اپلیکیشن انگولار ممکن است شامل بیش از یک ماژول انگولار باشد. ماژول‌های انگولار یک فرایند یا سیستم برای مونتاژ عناصر چندگانه انگولار مانند کامپوننت‌ها، دایرکتیوها، pipe-ها، سرویس‌ها و غیره است. این عناصر انگولار می‌توانند به ترتیبی ترکیب شوند که همه عناصر بتوانند با همدیگر ارتباط داشته باشند و در نهایت اپلیکیشن را تشکیل دهند.

دکوراتور NgModule@ در انگولار به منظور تعریف کردن کلاس ماژول انگولار استفاده می‌شود. برخی اوقات، این کلاس به نام کلاس NgModule نامیده می‌شود. NgModule@ همواره یک شیء metadata می‌گیرد که به انگولار اعلام می‌کند چگونه اپلیکیشن را کامپایل کرده و در مرورگر اجرا کند. به این ترتیب برای تعریف ماژول انگولار باید برخی مراحل را به صورت زیر تعریف کنیم:

  1. ابتدا باید BrowserModule انگولار را در فایل ماژول انگولار ایمپورت کنیم. این کلاس BrowserModule مسئول اجرا کردن اپلیکیشن در مرورگر است.
  2. در گام بعدی، باید عناصر انگولار مانند کامپوننت‌ها درون ماژول انگولار اعلان کنیم، به طوری که این کامپوننت‌ها یا عناصر می‌توانند با ماژول انگولار ارتباط بگیرند.

در گام آخر باید یک کامپوننت انگولار را به صورت کامپوننت root برای ماژول انگولار تعیین کنیم. این کامپوننت همواره به نام کامپوننت بوت‌استرپ نیز شناخته می‌شوند. بنابراین یک ماژول انگولار می‌تواند شامل صدها کامپوننت باشد. اما از میان این کامپوننت‌ها، یک کامپوننت همواره به عنوان کامپوننت بوت‌استرپ شناخته می‌شود که وقتی ماژول انگولار در مرورگر بوت‌استرپ می‌شود، اجرا خواهد شد.

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3  
4import { AppComponent } from './app.component';  
5  
6@NgModule({  
7  declarations: [  
8    AppComponent  
9  ],  
10  imports: [  
11    BrowserModule  
12  ],  
13  providers: [],  
14  bootstrap: [AppComponent]  
15})  
16export class AppModule { }

مثال اول: نخستین برنامه با انگولار

چنان که اشاره کردیم وقتی یک پروژه انگولار را با استفاده از Angular CLI ایجاد می‌کنیم، پروژه‌های جدیدی همراه با یک ماژول و فایل کامپوننت پیش‌فرض ایجاد می‌شود.

این فایل‌ها به طور معمول درون پوشه app قرار دارند. از این رو ابتدا باید پروژه انگولار را با استفاده از دستور ng serve اجرا کنیم تا خروجی زیر در مرورگر دیده شود:

آموزش انگولار

اکنون برخی تغییرها را در فایل app.component.ts و app.component.ts به صورت زیر ایجاد می‌کنیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls: ['./app.component.css']  
7})  
8export class AppComponent {  
9  title = 'Welcome to Angular 8 Learning Series...';  
10}

فایل app.component.html

1<!--The content below is only a placeholder and can be replaced.-->  
2<div style="text-align:center">  
3  <h1>  
4    Welcome to {{ title }}!  
5  </h1>  
6  <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdC
7b3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEu
8OSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaW
9xsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHo
10iIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5
11LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">  
12</div>

اگر اکنون مرورگر را رفرش کنیم، خروجی زیر را مشاهده خواهیم کرد:

آموزش انگولار

کامپوننت چیست؟

کامپوننت اساساً کلاسی است که برای هر عنصر یا کنترل قابل دیدن روی صفحه تعریف می‌شود.

هر کلاس کامپوننت برخی مشخصه‌ها دارد و با استفاده از آن‌ها می‌توانیم رفتار یا ظاهر عنصر را روی صفحه دست‌کاری کنیم. به این ترتیب می‌توانیم کامپوننت‌های خود را بسته به الزامات در هر مرحله از اپلیکیشن، ایجاد، به‌روزرسانی یا تخریب کنیم. اما در تایپ اسکریپت، کامپوننت اساساً یک کلاس تایپ اسکریپت است که یک دکوراتور ()Component@ دارد. یک کامپوننت از دیدگاه HTML تگ سفارشی HTML تعریف شده از سوی کاربر است که می‌تواند در مرورگر برای نمایش هر نوع عنصر UI همراه با نوعی منطق تجاری رندر شود.

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls: ['./app.component.css']  
7})  
8export class AppComponent {  
9  title = 'Welcome to Angular 8 Learning Series...';  
10}

دکوراتورها به طور عمده تابع‌های جاوا اسکریپت هستند که به ترتیبی موجب بهبود کلاس دارای دکوراتور می‌شوند. کامپوننت یک بلوک مستقل و کامل است که منطق، نما و داده‌های مورد نیاز به صورت واحد منفردی درآورده است. شما به عنوان یک توسعه‌دهنده برای ساخت یک کامپوننت صرفاً باید برخی اشیای پیکربندی مرتبط با تابع دکوراتور را به صورت آرگومان یا پارامتر به آن ارسال کنید. از نظر منطقی هر کامپوننت انگولار خود به صورت یک مفهوم MVC عمل می‌کند. از آنجا که هر کلاس کامپوننت واحد مستقلی است، قابلیت بالایی برای استفاده مجدد دارد و می‌تواند بدون ایجاد مشکل در کامپوننت‌های دیگر نگهداری شود.

مثالی از یک کامپوننت ابتدایی

اکنون فرض کنید لازم است یک کامپوننت جدید در پروژه انگولار خود ایجاد کنیم. به این منظور هم می‌توانیم یک پروژه جدید ایجاد کنیم و هم از همان پروژه بخش قبلی مقاله استفاده کنیم. در پوشه پروژه یک فایل کامپوننت به نام app.component.ts داریم. ابتدا یک کامپوننت با HTML درون‌خطی ایجاد می‌کنیم. به این منظور باید تغییرهای زیر را در فایل موجود app.component.ts ایجاد کنیم:

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  template: '<h1>Component is the main Building Block in Angular</h1>'  
6})  
7export class AppComponent {  
8    
9}

اگر اینک اپلیکیشن را در مرورگر اجرا کنید، با تصویری مانند زیر مواجه می‌شوید:

آموزش انگولار

چه لزومی به استفاده از معماری کامپوننت-محور وجود دارد؟

با توجه به روندهای موجود در توسعه اپلیکیشن‌ها، معماری کامپوننت-محور به عنوان یک معماری با بیشترین قابلیت استفاده در آینده توسعه وب مطرح شده است. با کمک گرفتن از این معماری، می‌توانیم زمان و هزینه توسعه در حجم بالا را برای هر پروژه توسعه وب بزرگ-مقیاس کاهش دهیم. به همین جهت است که کارشناسان فنی امروزه پیاده‌سازی این معماری را در توسعه اپلیکیشن‌های مبتنی بر وب توصیه می‌کنند. بنابراین پیش از این که به بررسی تفصیلی کامپوننت‌ها بپردازیم، ابتدا باید دلیل لزوم استفاده از معماری کامپوننت-محور را در توسعه وب‌اپلیکیشن‌ها توضیح دهیم.

  • قابلیت استفاده مجدد: فریمورک‌های کامپوننت-محور فایده زیادی دارند، زیرا امکان ساخت فیچرهای با قابلیت استفاده مجدد را فراهم می‌سازند. در این چارچوب، کامپوننت‌ها واحدهای منفردی در فرایند توسعه هستند. این توسعه با استفاده از کامپوننت موجب می‌شود که یک پیش‌بینی در خصوص قابلیت استفاده مجدد در چرخه‌های آتی توسعه داشته باشیم. با توجه به این که فناوری امروزه با سرعت زیادی در حال تغییر است، اگر اپلیکیشنی را با معماری کامپوننت-محور توسعه دهیم در آینده قادر خواهیم بود اجزای مختلف را که به شکل کامپوننت هستند، وارد پروژه کنیم یا آن‌ها را کنار بگذاریم. رویکرد معماری کامپوننت-محور به ما امکان می‌دهد که اپلیکیشن را در طی زمان به‌روز نگهداریم و دیگر نیاز نیست همه چیز را از صفر دوباره بسازیم.
  • افزایش سرعت توسعه: توسعه کامپوننت-محور از توسعه مبتنی بر روش agile پشتیبانی می‌کند. کامپوننت‌ها می‌توانند در یک کتابخانه نگهداری شوند و تیم می‌تواند در طی فرایند توسعه به آن‌ها دسترسی داشته، آن‌ها را مورد استفاده قرار دهید یا تغییر دهد. از سوی دیگر باید توجه داشته باشیم که هر توسعه‌دهنده‌ای مهارت‌های تخصصی معینی دارد. برای نمونه یک فرد می‌تواند در زمینه جاوا اسکریپت تخصص داشته باشد. فرد دیگر متخصص CSS باشد و یا تخصص‌های دیگری داشته باشند. در این چارچوب هر توسعه‌دهنده متخصص می‌تواند روی کامپوننت معینی که در حیطه تخصصی وی است کار کند.
  • ادغام آسان: در چارچوب کامپوننت-محور می‌توان یک ریپازیتوری کتابخانه مرتبط با کامپوننت ایجاد کرد. این ریپازیتوری کامپوننت می‌تواند به عنوان یک ریپازیتوری کد مرکزی برای توسعه کنونی و همچنین توسعه‌های آتی مورد استفاده قرار گیرد. همانند ریپازیتوری‌های مرکزی کدهای دیگر این کتابخانه را می‌توان در هر سیستم کنترل نسخه‌ای نگهداری کرد. به این ترتیب توسعه‌دهنده می‌تواند به این ریپازیتوری دسترسی داشته باشد و فیچرها و کارکردهای جدید را بسته به الزامات جدید به‌روزرسانی کند و جهت تأیید شدن تحویل دهد.
  • بهینه‌سازی الزام و طراحی: از کتابخانه کامپوننت می‌توان به عنوان یک منبع ارجاع کامپوننت UI استفاده کرد. به این ترتیب با بهره‌گیری از این منبع، تحلیل اعضای تیم از قبیل مدیر محصول، تحلیلگر کسب‌وکار و یا رهبران فنی نیازمند صرف وقت کمتری است و نهایی‌سازی طراحی UI برای الزامات جدید سریع‌تر انجام می‌یابد، زیرا از قبل یک دسته کامپوننت‌های کاملاً تست‌شده و با کارکرد صحیح در اختیار داریم. در این صورت تنها کافی است در مورد فرایند بهینه‌سازی شامل صرفاً منطق تجاری جدید تصمیم‌گیری شود. به این ترتیب این چارچوب کامپوننت-محور موجب افزایش سرعت چرخه عمر فرایند توسعه می‌شود.
  • کاهش هزینه نگهداری: از آنجا که چارچوب کامپوننت-محور موجب ایجاد قابلیت استفاده مجدد می‌شود، چنین فریمورکی نیاز به وجود توسعه‌دهندگان زیاد را در زمان ساخت یک اپلیکیشن جدید از میان برمی‌دارد. کامپوننت‌های مبتنی بر منطق به طور معمول «فاقد زمینه» (context-free) هستند و کامپوننت‌های مبتنی بر UI نیز به همراه UI و UX مناسبی عرضه می‌شوند. از این رو توسعه‌دهنده می‌تواند روی ادغام این کامپوننت‌ها در اپلیکیشن و شیوه ایجاد اتصال‌ها بین این نوع کامپوننت‌ها تمرکز کند. ضمناً خصوصیت‌های دیگر سیستم از قبیل امنیت، عملکرد، قابلیت نگهداری، پایداری و مقیاس‌پذیری نیز می‌توانند تست شوند.

متادیتای Component@

زمانی که بخواهیم یک کامپوننت جدید در انگولار بسازیم، باید از دکوراتور Component@ استفاده کنیم. دکوراتور Component@ اساساً یک کلاس تایپ اسکریپت به صورت یک شیء Component تعریف می‌کند. در واقع دکوراتور Component@ یک تابع است که انواع مختلفی از پارامترها را می‌گیرد. در دکوراتور Component@ می‌توانیم مقادیر مشخصه‌های مختلف را برای نهایی‌سازی تغییر رفتار کامپوننت تعیین کنیم. رایج‌ترین مشخصه‌های دکوراتور Component@ به شرح زیر هستند:

  1. selector – یک کامپوننت می‌تواند از سوی عبارت سلکتور مورد استفاده قرار گیرد. افراد زیادی با کامپوننت‌ها به عنوان یک تگ سفارشی HTML رفتار می‌کنند، زیرا در نهایت زمانی که بخواهند از کامپوننت در فایل HTML استفاده کنند، باید یک سلکتور درست مانند تگ HTML داشته باشند.
  2. template - قالب یا template بخشی از کامپوننت است که در مرورگر رندر می‌شود. در این مشخصه می‌توانیم تگ‌های HTML یا کد را به صورت مستقیم به نام کد inline ارسال کنیم. این قالب‌ها گاهی اوقات قالب‌های درون‌خطی (Inline) نامیده می‌شوند. برای نوشتن چندین خط کد، همه کد باید درون نماد backtick (`) قرار گیرد.
  3. templayeUrl – این مشخصه یک روش دیگر برای رندر کردن تگ‌های HTML در مرورگر است. این مشخصه همیشه نام فایل HTML را همراه با مسیر فایل مربوطه می‌گیرد. برخی اوقات آن را قالب خارجی می‌نامند. استفاده از این مشخصه بسیار بهتر از این است که بخواهیم UI پیچیده را درون کامپوننت طراحی کنیم.
  4. moduleId – این مشخصه برای به دست آوردن مسیر مرتبط URL قالب یا URL استایل برای اشیای کامپوننت مورد استفاده قرار می‌گیرد. این مشخصه Id ماژول‌های مرتبط را که کامپوننت در آن‌ها الصاق یافته یا تگ شده است را با هم ترکیب می‌کند.
  5. styles / stylesUrls – کامپوننت‌ها می‌توانند با ارائه CSS سفارشی با استفاده از استایل مخصوص خودشان مورد استفاده قرار گیرند. همچنین می‌توانند به فایل‌های استایل‌شیت بیرونی ارجاع دهند و به این ترتیب می‌توان از یک استایل‌شیت برای تنظیم ظاهر چند کامپوننت استفاده کرد. برای ارائه یک استایل درون‌خطی (Inline) باید از styles و برای ارائه یک مسیر فایل بیرونی باید از styleUrls استفاده کنیم.
  6. providers – در اپلیکیشن‌های واقعی باید انواع مختلفی از سرویس‌های سفارشی را در کامپوننت مورد استفاده قرار داده یا تزریق کنیم تا منطق تجاری را برای کامپوننت پیاده‌سازی کنیم. برای استفاده از سرویس‌های تعریف شده کاربر درون کامپوننت، باید یک وهله از سرویس را درون provider عرضه کنیم. از این رو مشخصه provider همواره مقادیر از نوع آرایه را مجاز می‌داند. به این ترتیب می‌توانیم نام‌های وهله‌های چندگانه سرویس را تعریف کنیم که درون مشخصه با کاما از هم جدا می‌شوند.

در مثال زیر، شیوه تعریف یک کامپوننت را با استفاده از برخی مشخصه‌ها از قبیل سلکتور و قالب را نشان داده‌ایم:

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  template: 'Welcome to Angular 8 Learning Series...'  
6})  
7export class AppComponent {  
8}

در مثال زیر نیز شیوه استفاده از مشخصه‌های دیگر دکوراتور Component@ مانند templateUrls را می‌بینید:

1import { Component } from '@angular/core';  
2  
3@Component({  
4  moduleId: module.id,  
5  selector: 'app-root',  
6  templateUrl: './app.component.html'  
7})  
8export class AppComponent {  
9  title = 'Welcome to Angular 8 Learning Series...';  
10}

بنابراین در مثال فوق، فایل HTML را برای ذخیره‌سازی بخش HTML مرتبط با کامپوننت‌ها جدا خواهیم ساخت. بر اساس مثال فوق، باید دو فایل تایپ اسکریپت و HTML را در مکان یکسانی قرار دهیم. اگر بخواهیم HTML را در مکان مجزایی قرار دهیم، باید از آن به وسیله یک URL نسبی در دکوراتور کامپوننت استفاده کنیم. در بخش زیر نمونه کدی را که در فایل app.component.html نوشته شده است می‌بینید:

1<div style="text-align:center">  
2  <h1>  
3    Welcome to {{ title }}!  
4  </h1>  
5</div>

مثالی از اعمال استایل روی محتوا

اکنون باید استایل‌های مورد نظر خود را روی کامپوننت فوق اعمال کنیم. به این منظور تغییرهای زیر را در کامپوننت اعمال می‌کنیم. ابتدا استایل‌های درون‌خطی را در کامپوننت ایجاد می‌کنیم.

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  template: '<h1>Component is the main Building Block in Angular</h1> <h2>Angular 8 Samples</h2>',  
6  styles: ['h1{color:red;font-weight:bold}','h2{color:blue}']  
7})  
8export class AppComponent {  
9    
10}

اینک با اعمال تغییرهای فوق، اپلیکیشن را اجرا می‌کنیم تا نتیجه‌ای مانند زیر به دست آید:

آموزش انگولار

مثالی از فایل استایل‌شیت اکسترنال برای کامپوننت

در دموی پیشین از استایل‌شیت درون‌خطی برای تزیین محتوای HTML درون کامپوننت استفاده کردیم. این حالت در مواردی که لازم باشد استایل‌ها تنها در یک کامپوننت استفاده شوند، مطلوب است. اما اگر بخواهیم همین استایل‌ها را در همه کامپوننت‌ها اعمال کنیم، باید از استایل‌شیت اکسترنال درون کامپوننت بهره بگیریم. به این منظور ابتدا باید یک فایل استایل‌شیت جدید به نام custom.css درون پروژه ایجاد کنیم و سپس کد زیر را درون همان فایل اضافه کنیم:

1/* You can add global styles to this file, and also import other style files */  
2h1{  
3    color:red;  
4    font-weight:bold;  
5    font-size: 30px;   
6}  
7h2{  
8    color:blue;  
9    font-size: 20px;  
10}  
11  
12p{  
13    color:brown;  
14    font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;  
15}

اینک کافی است مسیر ارجاع style.css را در مشخصه‌های styleUrls درون فایل app.component.ts قرار دهیم.

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  template: '<h1>Component is the main Building Block in Angular</h1> <h2>Angular 8 Samples</h2>',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10}

اینک با بارگذاری مجدد مرورگر، خروجی زیر به دست می‌آید که همانند مثال قبلی است:

آموزش انگولار

مثالی از استفاده از فایل HTML اکسترنال برای محتوای کامپوننت

همانند استایل اکسترنال، امکان استفاده از فایل HTML اکسترنال نیز برای بخش کد HTML وجود دارد. به این منظور ابتدا باید یک فایل HTML به نام app.component.html در پوشه app اضافه کنیم و کد زیر را در آن بنویسیم:

1<h1>Component is the main Building Block in Angular</h1>   
2<h2>Angular 8 Samples</h2>  
3<p>  
4    Use of <b>External HTML</b> files with the Component  
5</p>

در ادامه تغییرهای زیر را در فایل app.component.ts ایجاد می‌کنیم تا ارجاع مسیر فایل HTML بیرونی را به آن ارسال نماییم.

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10}

اینک با رفرش کردن صفحه مرورگر، خروجی زیر حاصل می‌شود:

آموزش انگولار

چرخه عمر یک کامپوننت

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

  • ngOnChanges – این رویداد هر بار که مقدار یک کنترل ورودی در کامپوننت تغییر یابد، اجرا می‌شود. این رویداد نخستین بار زمانی که یک مشخصه‌ی اتصال‌یافته (Bound) تغییر یابد، فعال می‌شود.
  • ngOnInit – این رویداد درزمان مقداردهی کامپوننت اجرا می‌شود. این رویداد تنها یک بار و پس از رویدادهای ()ngOnChanges فراخوانی می‌شود. این رویداد به طور عمده برای مقداردهی یک کامپوننت مورد استفاده قرار می‌گیرد.
  • ngDoCheck – این رویداد هر بار که مشخصه‌های ورودی یک کامپوننت بررسی شوند، اجرا می‌شود. می‌توان از این متد چرخه عمر برای پیاده‌سازی بررسی مقادیر ورودی همانند بررسی منطق سفارشی استفاده کرد.
  • ngAfterContentInit – این متد چرخه عمر در زمانی اجرا می‌شود که هر نوع نمایش محتوا درون نماهای کامپوننت اجرا شود. این متد تنها یک بار و زمانی که همه اتصال‌های کامپوننت قرار است برای نخستین بار بررسی شوند، اجرا می‌شود. این رویداد درست پس از متد ()ngDoCheck اجرا می‌شود.
  • ngAfterContentChecked – این متد قلاب چرخه عمر هر بار که محتوای کامپوننت از سوی سازوکار تشخیص تغییر انگولار بررسی شود، اجرا خواهد شد. این متد پس از متد ()ngAfterContentInit فراخوانی می‌شود. این متد می‌تواند روی هر اجرای رویداد ()ngDoCheck نیز اجرا شود.
  • ngAfterViewInit – این متد چرخه عمر زمانی اجرا می‌شود که کامپوننت کار رندرینگ نمایش را تکمیل کرده باشد. این متد چرخه عمری برای مقداردهی نمای کامپوننت و نماهای فرزند آن استفاده می‌شود. این متد تنها یک بار و پس از ()ngAfterContentChecked فراخوانده می‌شود. این متد قلاب چرخه عمری روی همه کامپوننت‌ها اعمال می‌شود.
  • ngAfterViewChecked – این متد همواره پس از متد ()ngAterViewInit اجرا می‌شود. ngAfterViewChecked یک متد چرخه عمری است که اساساً زمانی اجرا می‌شود که الگوریتم تشخیص تغییر کامپوننت‌های انگولار فعال شود. این متد به صورت خودکار با هر بار اجرای ()ngAfterContentChecked اجرا خواهد شد.
  • ngOnDestroy – این متد زمانی اجرا خواهد شد که بخواهیم کامپوننت‌های انگولار را تخریب کنیم. این متد برای لغو اشتراک observable‌-ها و جداسازی دستگیره‌های رویداد جهت جلوگیری از نشت حافظه بسیار مفید است. این متد تنها یک بار و درست پیش از این که کامپوننت از DOM حذف شود، فراخوانی می‌شود.

مثالی از چرخه عمر کامپوننت

در این مثال به بررسی رویدادهای چرخه عمر یک کامپوننت می‌پردازیم. به این منظور کد زیر را در فایل app.component.ts اضافه می‌کنیم:

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9  data:number=100;      
10    constructor() {    
11        console.log(`new - data is ${this.data}`);    
12    }          
13    ngOnChanges() {    
14        console.log(`ngOnChanges - data is ${this.data}`);    
15    }      
16    ngOnInit() {    
17        console.log(`ngOnInit  - data is ${this.data}`);    
18    }   
19}

همچنین کد زیر را در فایل app.component.html اضافه می‌کنیم:

1<span class="setup">Given Number</span>    
2<h1 class="punchline">{{ data }}</h1>

با رفرش کردن مرورگر، خروجی زیر به دست می‌آید:

آموزش انگولار

کامپوننت‌های تودرتو

در بخش پیش به بررسی وجوه گوناگون کامپوننت‌ها از قبیل تعریف، ‌متادیتا و رویدادهای چرخه عمری پرداختیم. در زمان توسعه یک اپلیکیشن در موارد متعددی لازم است که یک کامپوننت تودرتو را پیاده‌سازی کنیم. کامپوننت تودرتو به کامپوننتی گفته می‌شود که درون کامپوننت دیگری قرار می‌گیرد و یا آن را می‌توان کامپوننت فرزند نامید. نخستین سؤال که پیش می‌آید این است که آیا فریمورک انگولار از چنین کامپوننت‌های پشتیبانی می‌کند؟ پاسخ مثبت است. ما می‌توانیم هر تعداد کامپوننت که دوست داریم درون یک کامپوننت دیگر قرار دهیم. همچنین انگولار به طور کلی از هر سطحی از تودرتوسازی پشتیبانی می‌کند.

مثالی از کامپوننت تودرتو

چنان که پیش‌تر اشاره کردیم، در انگولار می‌توان هر کامپوننتی را به صورت والد-فرزند توسعه داد. به این منظور باید از سلکتور کامپوننت فرزند درون فایل HTML کامپوننت والد استفاده کنیم. بنابراین ابتدا باید یک کامپوننت فرزند مانند زیر توسعه دهیم.

فایل child.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'child',  
5  templateUrl: './child.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class ChildComponent {  
9      
10}

فایل child.component.html

1<h2>It is a Child Component</h2>  
2<p>  
3    A component is a Reusable part of the application.  
4</p>

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10}

فایل app.component.html

1<h1>Demostration of Nested Component in Angular</h1>  
2<h3>It is a Parent Component</h3>  
3<child></child>

اینک کامپوننت فرزند را به صورت زیر در فایل app.module.ts قرار می‌دهیم.

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3  
4import { AppComponent } from './app.component';  
5import { ChildComponent } from './child.component';  
6  
7@NgModule({  
8  declarations: [  
9    AppComponent,ChildComponent  
10  ],  
11  imports: [  
12    BrowserModule  
13  ],  
14  providers: [],  
15  bootstrap: [AppComponent]  
16})  
17export class AppModule { }

اینک با رفرش کردن مرورگر، خروجی زیر حاصل می‌شود:

آموزش انگولار

بدین ترتیب در این بخش با مفاهیم مختلف مرتبط با کامپوننت‌های انگولار از قبیل متادیتا، رویدادهای چرخه عمری و غیره آشنا شدیم و با بررسی مثال‌هایی به صورت عملی آن‌ها را کدنویسی کردیم. در بخش بعدی با مفهوم «اتصال داده‌ها» (Data Binding) آشنا خواهیم شد.

اتصال داده‌ها (Data Binding)

اتصال داده‌ها یکی از مهم‌ترین ویژگی‌های فریمورک انگولار محسوب می‌شود. زیرا در هر وب‌اپلیکیشنی ارسال و دریافت داده‌ها همواره نقشی کلیدی در توسعه دارد. در انگولار این کار با کمک مفهوم «اتصال داده‌ها» به روش بسیار آسانی انجام می‌یابد.

اتصال داده‌ها چیست؟

اتصال داده یکی از ظریف‌ترین و مفیدترین قابلیت‌های فریمورک انگولار است. توسعه‌دهندگان با استفاده از این ویژگی نیاز کمتری به کدنویسی در قیاس با هر کتابخانه یا فریمورک دیگر سمت کلاینت می‌یابند. اتصال داده‌ها در اپلیکیشن انگولار به همگام‌سازی خودکار داده‌ها بین کامپوننت‌ها مدل و «نما» (View) گفته می‌شود. در انگولار با به خدمت گرفتن مفهوم اتصال داده‌ها، هر زمان می‌توانیم با مدل به صورت یک «منبع منفرد واقعیت» (single-source-of-truth) در وب‌اپلیکیشن رفتار کنیم. به این ترتیب UI یا نما همواره مدل داده‌ها را در هر حالت نمایش می‌دهد. توسعه‌دهندگان به کمک «اتصال داده‌ها» می‌توانند رابطه‌ای بین UI اپلیکیشن و منطق تجاری برقرار سازند. اگر اتصال داده‌ها را به روش صحیحی برقرار سازیم و داده‌ها اعلان‌های مناسبی در اختیار فریمورک قرار دهند، در این صورت زمانی که کاربر تغییری در داده‌ها در نما ایجاد می‌کند، عناصری که به داده‌ها اتصال یافته‌اند، به صورت خودکار این تغییر را بازتاب می‌دهند.

مفاهیم مقدماتی اتصال داده‌ها

در مورد هر وب‌اپلیکیشن نیازمند ایجاد نوعی پل ارتباطی بین بک‌اند که داده‌ها در آنجا ذخیره شده‌اند و فرانت‌اند که کاربر داده‌ها را دست‌کاری می‌کند، هستیم. کل این فرایند به برخی تعامل‌های شبکه‌‌ای پی‌در‌پی و ارتباط مکرر بین سرور (بک‌اند) و کلاینت (فرانت‌اند) وابسته است.

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

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

آموزش انگولار

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

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

چه لزومی به اتصال داده وجود دارد؟

فریمورک انگولار از روز نخست خود این قابلیت خاص و قدرتمند «اتصال داده‌ها» را ارائه کرده است که همواره روانی و انعطاف‌پذیری را در هر وب‌اپلیکیشن به ارمغان آورده است. توسعه‌دهندگان با استفاده از این قابلیت «اتصال داده» می‌توانند کنترل بهتری روی فرایند و مراحل مرتبط با پردازش اتصال داده داشته باشند. این فرایند موجب سهولت کار توسعه‌دهنده و کاهش زمان توسعه با توجه به فریمورک‌های دیگر می‌شود. برخی از دلایل مرتبط با دلیل لزوم وجود اتصال داده‌ها در هر وب‌اپلیکیشن به شرح زیر هستند:

  1. به کمک اتصال داده، صفحه‌های وب مبتنی بر داده‌ها می‌توانند به روشی سریع و کارآمد توسعه یابند.
  2. ما همواره نتیجه مطلوب را با کمترین حجم کدنویسی به دست می‌آوریم.
  3. به دلیل این فرایند، زمان اجرا افزایش می‌یابد. در نتیجه، کیفیت اپلیکیشن افزایش می‌یابد.
  4. ما به کمک event emitter می‌توانیم کنترل بهتری روی فرایند اتصال داده‌ها داشته باشیم.

انواع مختلف اتصال داده

در انگولار با چهار نوع مختلف از فرایند‌های اتصال داده مواجه هستیم:

  • درون‌یابی (Interpolation)
  • اتصال مشخصه (Property Binding)
  • اتصال دوطرفه (Two-Way Binding)
  • اتصال رویداد (Event Binding)

درون‌یابی

درون‌یابی اتصال داده‌ها رایج‌ترین و آسان‌ترین روش Data Binding در انگولار محسوب می‌شود. این قابلیت در نسخه‌های قبلی فریمورک انگولار نیز وجود دارد. در واقع context بین آکولاد یک عبارت قالبی است که انگولار ابتدا ارزیابی می‌کند و سپس به رشته تبدیل می‌کند. درون‌یابی از عبارت‌های آکولادی یعنی {{}} برای رندر مقدار اتصال‌یافته به قالب کامپوننت استفاده می‌کند. این مقدار می‌تواند یک رشته استاتیک، مقدار عددی یا یک شیء از مدل داده‌ها باشد. در انگولار ما از ساختاری مانند {{firstName}} استفاده می‌کنیم.

مثال زیر نشان می‌دهد که چگونه می‌توانیم از درون‌یابی در کامپوننت برای نمایش داده‌ها در فرانت‌اند استفاده کنیم.

1<div>   
2    <span>User Name : {{userName}}</span>      
3</div>

مثالی از درون‌یابی

در مثال زیر شیوه استفاده یا پیاده‌سازی درون‌یابی را در اپلیکیشن‌های انگولار برای انواع داده مختلف نشان می‌دهیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9  public value1: number = 10;  
10  public array1: Array<number> = [10, 22, 14];  
11  public dt1: Date = new Date();  
12  
13  public status: boolean = true;  
14  
15  public returnString(): string {  
16      return "String return from function";  
17  }  
18}

فایل app.component.html

1<div>  
2    <span>Current Number is {{value1}}</span>      
3    <br /><br />    
4    <span>Current Number is {{value1 | currency}}</span>  
5    <br /><br />  
6    <span>Current Number is {{dt1}}</span>  
7    <br /><br />  
8    <span>Current Number is {{dt1 | date}}</span>  
9    <br /><br />  
10    <span>Status is {{status}}</span>  
11    <br /><br />  
12    <span>{{status ? "This is correct status" :"This is false status"}}</span>  
13    <br /><br />  
14    <span>{{returnString()}}</span>  
15</div>

خروجی کد فوق به صورت زیر است:

آموزش انگولار

اتصال مبتنی بر مشخصه

در انگولار یک مکانیسم اتصال داده دیگر نیز وجود دارد که به نام اتصال شناخته می‌شود. ماهیت آن مشابه درون‌یابی است. برخی افراد آن را بنا بر نسخه‌های قبلی AngularJS، اتصال یک‌طرفه می‌نامند. اتصال مشخصه از نمادهای [] برای ارسال داده‌ها از کامپوننت به قالب HTML استفاده می‌کند. رایج‌ترین روش برای استفاده از اتصال مشخصه، انتساب هر مشخصه تگ عنصر HTML به [] با استفاده از مقدار مشخصه به صورت زیر است:

1<input type=”text” [value]=”data.name”/>

برای پیاده‌سازی اتصال مشخصه، کافی است تغییر زیر را در فایل HTML قبلی یعنی interpolation.component.html اعمال می‌کنیم:

1<div>       
2    <input [value]="value1" />   
3    <br /><br />  
4</div>

مثالی از اتصال داده مبتنی بر مشخصه

در این مثال به بررسی شیوه استفاده از اتصال‌های مبتنی بر مشخصه در انگولار می‌پردازیم. به این منظور باید یک کادر متنی از نوع ورودی در فایل app.component.html اضافه کنیم و این کادر متنی را با استفاده از اتصال مشخصه‌ای به متغیر value1 وصل می‌کنیم.

فایل app.component.html

1<div>  
2    <span>Current Number is {{value1}}</span>  
3    <br/><br />     
4    Display Value in Input Controls : <input [value]="value1" />  
5    <br /><br />    
6    <span>Current Number is {{value1 | currency}}</span>  
7    <br /><br />  
8    <span>Current Number is {{dt1}}</span>  
9    <br /><br />  
10    <span>Current Number is {{dt1 | date}}</span>  
11    <br /><br />  
12    <span>Status is {{status}}</span>  
13    <br /><br />  
14    <span>{{status ? "This is correct status" :"This is false status"}}</span>  
15    <br /><br />  
16    <span>{{returnString()}}</span>  
17</div>

اینک خروجی مثال ما به صورت زیر است:

آموزش انگولار

اتصال رویداد

«اتصال رویداد» (Event Binding) یک تکنیک دیگر اتصال داده‌ها است که در انگولار استفاده می‌شود. این تکنیک اتصال داده‌ها با مقدار عناصر UI کار نمی‌کند، بلکه با فعالیت‌های رویداد عناصر UI مانند رویداد کلیک، رویداد blur و غیره کار می‌کند. در نسخه قدیمی AngularJS از انواع مختلفی از دایرکتیوها مانند ng-click ،ng-blur برای اتصال هر اکشن رویداد خاص یک کنترل HTML استفاده می‌کنیم. اما در نسخه کنونی انگولار باید از همان مشخصه عنصر HTML (مانند click، change و غیره) استفاده کنیم و آن را درون پرانتزها قرار دهیم. در انگولار برای مشخصه‌ها از براکت و در مورد رویدادها از پرانتز استفاده می‌کنیم:

1<div>  
2    <input type="submit" value="Submit" (click)="fnSubmit()">  
3</div>

مثالی از اتصال داده‌ها مبتنی بر رویداد

در این مثال به بررسی شیوه پیاده‌سازی اتصال‌های مبتنی بر رویداد در انگولار می‌پردازیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  public showAlert() : void {  
11    console.log('You clicked on the button...');  
12    alert("Click Event Fired...");  
13  }  
14}

فایل app.component.html

1<div>  
2    <h2>Demo of Event Binding in Angular 8</h2>  
3    <input type="button" value="Click" class="btn-block" (click)="showAlert()" />  
4    <br /><br />  
5    <input type="button" value="Mouse Enter" class="btn-block" (mouseenter)="showAlert()" />  
6</div>

خروجی کد فوق در مرورگر به صورت زیر است:

آموزش انگولار

اتصال دوطرفه

در فریمورک انگولار، رایج‌ترین و مهم‌ترین تکنیک‌های اتصال داده، نوعی به نام «اتصال داده دوطرفه» (Two-Way Data Binding) است. اتصال دوطرفه به طور عمده در فیلد نوع ورودی یا هر عنصر فرم که کاربر در مرورگر مقادیری را وارد می‌کند یا مقداری را ارائه کرده یا کنترلی را تغییر می‌دهد، استفاده می‌شود. از سوی دیگر، همین تغییر به صورت خودکار در متغیرهای کامپوننت و برعکس به‌روزرسانی می‌شود. در انگولار یک دایرکتیو به نام ngModel داریم که باید به صورت زیر استفاده شود:

1<input type=”text” [(ngModel)] =”firstName”/>

مثالی از اتصال داده دو‌طرفه

در این مثال شیوه استفاده از اتصال‌های داده دوطرفه را در انگولار بررسی می‌کنیم. به این منظور باید ابتدا دو کامپوننت زیر را بسازیم:

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  public val: string = "";  
11}

فایل app.component.html

1<div>  
2    <div>  
3        <span>Enter Your Name </span>  
4        <input [(ngModel)]="val" type="text"/>  
5    </div>  
6    <div>  
7        <span>Your Name :- </span>  
8        <span>{{val}}</span>  
9    </div>  
10</div>

اکنون زمانی که از ngModel یا اتصال داده دوطرفه در کامپوننت‌ها استفاده کنیم، باید FormsModule انگولار را در فایل ماژول اپلیکیشن به صورت زیر تعریف کنیم، چون FormsModule بدون FormsModule کار نخواهد کرد.

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3import { FormsModule } from '@angular/forms';  
4  
5import { AppComponent } from './app.component';  
6  
7@NgModule({  
8  declarations: [  
9    AppComponent  
10  ],  
11  imports: [  
12    BrowserModule,FormsModule  
13  ],  
14  providers: [],  
15  bootstrap: [AppComponent]  
16})  
17export class AppModule { }

خروجی کد فوق به صورت زیر است:

آموزش انگولار

ما از [] استفاده می‌کنیم، زیرا در عمل یک اتصال مشخصه و پرانتز برای مفهوم اتصال داده استفاده می‌شود که نماد اتصال دوطرفه داده‌ها به صورت [()] است.

آموزش انگولار

NgModel هر دو نوع اتصال مشخصه و اتصال رویداد را اجرا می‌کند. در واقع، اتصال مشخصه ngModel یعنی [ngModel]) عمل به‌روزرسانی عنصر ورودی را با یک مقدار اجرا می‌کند. در حالی که (ngModel) یعنی رویداد (ngModelChange) به دنیای خارج اطلاع می‌دهد که تغییری در عنصر dom رخ داده است.

مثال زیر، پیاده‌سازی اتصال دوطرفه را نشان می‌دهد. در این مثال، یک متغیر رشته به نام strName تعریف می‌کنیم و این متغیر را به کنترل Textbox انتساب می‌دهیم. بنابراین هر زمان که محتوایی را در Textbox تغییر دهیم، مقدار متغیر به صورت خودکار تغییر خواهد یافت.

1<div>  
2    <input [(ngModel)]="strName" type="text"/>  
3</div>

دکوراتور ()Input@

در فریمورک انگولار همه کامپوننت‌ها به صورت یک کامپوننت «بدون حالت» (Stateless) یا کامپوننت «با حالت» (Statefull) استفاده می‌شوند. به طور معمول کامپوننت‌ها به روش بی‌حالت استفاده می‌شوند، اما برخی اوقات باید از برخی کامپوننت‌های باحالت نیز استفاده کنیم. دلیل استفاده از کامپوننت باحالت در یک وب‌اپلیکیشن به جهت ارسال یا بازیابی از کامپوننت کنونی به یکی از کامپوننت‌های والد یا فرزند است. بنابراین در این مورد باید به انگولار اطلاع دهیم کدام نوع داده یا چه داده‌ای می‌تواند وارد کامپوننت کنونی ما شود. برای پیاده‌سازی این موضوع باید از دکوراتور ()Input@ در برابر هر متغیر استفاده کنیم. ویژگی‌های کلیدی دکوراتور ()Input@ به شرح زیر هستند:

  • ()Input@ یک دکوراتور برای نشانه‌گذاری مشخصه ورودی است. به کمک این مشخصه، می‌توانیم یک مشخصه پارامتر ورودی را مانند خصوصیت‌های تگ HTML نرمال تعریف کنیم و آن مقدار را به صورت یک اتصال مشخصه به کامپوننت متصل سازیم.
  • دکوراتور ()Input@ همواره یک ارتباط یک‌طرفه داده‌ای از کامپوننت والد به کامپوننت فرزند برقرار می‌سازد. با استفاده از این قابلیت می‌توانیم برخی از مقادیر را برای مشخصه یک کامپوننت فرزند از کامپوننت‌های والد تعیین کنیم.
  • مشخصه کامپوننت باید با دکوراتور ()Input@ حاشیه‌نویسی شود تا به عنوان یک مشخصه ورودی مورد استفاده قرار گیرد. یک کامپوننت می‌تواند مقداری را از کامپوننت دیگر با استفاده از اتصال مشخصه کامپوننت دریافت کند.

دکوراتور ()Input@ می‌تواند به صورت هر نوع از مشخصه از قبیل عدد، رشته، آرایه، یا کلاس تعریف شده کاربر حاشیه‌نویسی شود. برای استفاده از یک نام مستعار برای نام مشخصه اتصال‌یافته، باید یک نام مستعار به صورت Input(alias)@ انتساب دهیم. در مثال زیر کاربرد Input@ را با نوع داده رشته‌ای می‌بینید:

1@Input() caption : string;  
2@Input('phoneNo') phoneNo : string;

در مثال فوق، testValue نام مستعار است. نام مستعار زمانی مورد نیاز است که بخواهیم مقداری را به مشخصه‌های ورودی ارسال کنیم. اگر نام مستعاری یافت نشود، در این صورت باید از نام مشخصه ورودی برای ارسال مقدار بهره بگیریم. بدین ترتیب زمانی که از این کامپوننت استفاده می‌کنیم، باید مقادیر ورودی را به صورت زیر ارسال کنیم:

1<demo-app [name]="'Debasis Saha'" [phoneNo]="9830098300"></demo-app>

مثالی از ارسال مقدار ورودی به کامپوننت

در این مثال با روش استفاده یا پیاده‌سازی مشخصه input یک کامپوننت آشنا خواهیم شد. به این منظور باید کامپوننت نخست زیر را توسعه دهیم که مشخصه ورودی آن تعریف خواهد شد.

فایل message.component.ts

1import { Component, Input } from '@angular/core';  
2  
3@Component({  
4  selector: 'message-info',  
5  templateUrl: './message.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class MessageComponent {  
9  
10    @Input() public message :string = '';  
11  
12    @Input('alert-pop') public message1 :string= ''  
13    
14    public showAlert():void{  
15        alert(this.message1);  
16    }  
17}

فایل message.component.html

1<div>  
2    Message : <h2>{{message}}</h2>  
3    <input type="button" value="Show Alert" (click)="showAlert()"/>  
4</div>

اکنون باید این کامپوننت message-info را در کامپوننت دیگری مصرف کنیم و باید مقدار ورودی را با استفاده از مشخصه‌های input ارسال کنیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  public val: string = "This is alert popup message";  
11  
12}

فایل app.component.html

1<div>  
2    <message-info [message]="'Demostration of Input Property of a Component'" [alert-pop]="val"></message-info>  
3</div>

خروجی مثال فوق به صورت زیر است:

آموزش انگولار

دکوراتور ()Output@

دکوراتور ()Output@ در انگولار به طور معمول به عنوان یک مشخصه خروجی استفاده می‌شود. از ()Output@ برای تعریف مشخصه‌های خروجی که اتصال‌های داده‌ای سفارش را دریافت می‌کنند بهره می‌گیریم. ()Output@ به عنان یک وهله از Event Emitter استفاده می‌شود. قابلیت‌های کلیدی دکوراتور Event Emitter به شرح زیر هستند:

  • دکوراتور ()Output@ یک مشخصه کامپوننت را برای ارسال داده‌ها از یک کامپوننت (کامپوننت فرزند) به کامپوننت فراخوانی‌کننده (کامپوننت والد) اتصال می‌دهد.
  • این یک اتصال یک طرفه از فرزند به سمت والد است.
  • ()Output@ یک مشخصه کلاس EventEmitter را اتصال می‌دهد. اگر کامپوننت را به صورت یک کنترل تصور کنیم، در این صورت مشخصه خروجی به عنوان یک رویداد آن کنترل عمل خواهد کرد.
  • دکوراتور EventEmitter می‌تواند گزینه‌هایی برای سفارشی‌سازی نام مشخصه با استفاده از نام مستعار به صورت Output(alias)@ نیز ارائه کند. در این حالت، اسامی مستعار سفارشی به عنوان یک نام مشخصه اتصال رویداد کامپوننت عمل می‌کنند.

دکوراتور Output(alias)@ در هر نوع مشخصه از قبیل عدد، رشته، آرایه یا کلاس‌های تعریف شده کاربر می‌تواند وارد شود. برای استفاده از یک اسم مستعار برای نام مشخصه اتصال می‌توانیم یک نام مستعار به صورت Output(alias)@ مورد استفاده قرار دهیم. کاربرد Output@ با نوع داده رشته‌ای به صورت مثال زیر است. توجه کنید که دکوراتور خروجی نیز همانند دکوراتور ورودی، ابتدا باید به صورت زیر اعلان شود:

1@Output('onSubmit') submitEvent = new EventEmitter<any>();

سپس باید emitter را از جایی درون کامپوننت فعال کنیم تا رویداد بتواند از سوی کامپوننت والد مورد ردگیری قرار گیرد:

1this.submitEvent.emit();

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

1<demo-app (onSubmit)="receiveData($event)"></demo-app>

مثالی از بازگشت مقدار خروجی از یک کامپوننت

در این مثال به بررسی شیوه استفاده از مشخصه output هر کامپوننت می‌پردازیم. به این منظور باید تغییرهای زیر را در کامپوننت message-info ایجاد کنیم تا یک مشخصه output را تعریف کنیم.

فایل message.component.ts

1import { Component, Input, EventEmitter, Output } from '@angular/core';  
2  
3@Component({  
4  selector: 'message-info',  
5  templateUrl: './message.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class MessageComponent {  
9  
10    @Input() public message :string = '';  
11    @Input('alert-pop') public message1 :string= ''  
12  
13    @Output() onSignup  = new EventEmitter<any>();  
14  
15    public data:any={};  
16    
17    public showAlert():void{  
18        alert(this.message1);  
19    }  
20  
21    public onSubmit() :void{  
22      this.onSignup.emit(this.data);  
23    }  
24}

فایل message.component.html

1<div>  
2    Message : <h2>{{message}}</h2>  
3    <input type="button" value="Show Alert" (click)="showAlert()"/>  
4    <br/><br/>  
5    Provide Full Name : <input type="text" [(ngModel)]="data.name">  
6    <br/>  
7    Provide Email Id : <input type="email" [(ngModel)]="data.email">  
8    <br>  
9    <input type="button" value="Sign Up" (click)="onSubmit()"/>  
10</div>

در بخش فوق، یک مشخصه output به نام ()onSignup درون کامپوننت message-info تعریف می‌کنیم. اکنون باید این رویداد را در کامپوننت والد به صورت زیر مصرف کنیم.

فایل app.component.html

1<div>  
2    <message-info [message]="'Demostration of Input Property of a Component'" [alert-pop]="val" (onSignup)="onSignup($event)"></message-info>      
3</div>

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  public val: string = "This is alert popup message";  
11  
12  public onSignup(data:any):void{  
13    let strMessage:string ="Thanks for Signup " + data.name + ". ";  
14    strMessage += "Email id " + data.email + " has been registered successfully.";  
15    alert(strMessage);  
16  }  
17}

خروجی مثال فوق به صورت زیر است:

آموزش انگولار

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

دایرکتیو چیست؟

«دایرکتیو» (Directive) با تغییر دادن ظاهر، رفتار یا لی‌آوت عناصر DOM آن‌ها را تغییر می‌دهد. دایرکتیوها مانند کامپوننت‌ها یکی از بلوک‌های سازنده اصلی فریمورک انگولار برای ساخت اپلیکیشن‌ها است. در واقع، بخش بزرگی از کامپوننت‌های انگولار در عمل دایرکتیو‌هایی به همراه قالب (Template) هستند. کامپوننت‌های انگولار نقش‌های زیادی را که دایرکتیوها بر عهده دارند مصرف می‌کنند. در انگولار 8 یکی از مشکلات عمده در خصوص تزریق قالب و تزریق وابستگی به کمک کامپوننت‌ها حل شده است و مشکلات مرتبط با ایجاد تغییر در رفتار ژنریک اپلیکیشن نیز به کمک دایرکتیوها حل شده است.

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

مفاهیم مقدماتی دایرکتیوها

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

به طور کلی یک دایرکتیو به تابع مبتنی بر تایپ اسکریپت گفته می‌شود که کامپایلر انگولار آن را درون عنصر DOM شناسایی کرده و اجرا می‌کند. دایرکتیوها برای ارائه یا تولید ساختار جدید مبتنی بر HTML استفاده می‌شوند که قدرت UI را در اپلیکیشن انگولار بسط می‌دهد. هر دایرکتیو باید یک نام سلکتور مانند کامپوننت داشته باشد. این نام هم می‌تواند از الگوهای از پیش‌تعریف‌شده انگولار مانند ng-if یا یک نام سفارشی انتخاب شده از سوی توسعه‌دهنده باشد که نشانگر مقصود دایرکتیو باشد. همچنین هر دایرکتیو می‌تواند به عنوان یک عنصر یا یک خصوصیت یا یک کلاس یا یک کامپوننت در بخش HTML عمل کند.

چرا به دایرکتیوها نیاز داریم؟

دایرکتیوها در فریمورک انگولار همواره سطح بالایی از قابلیت استفاده مجدد کنترل‌های UI را در سراسر اپلیکیشن تضمین می‌کنند. به کمک دایرکتیوها می‌توانیم UI-هایی را توسعه دهیم که بخش‌های متحرک زیادی داشته باشند و هم‌زمان جریان توسعه را در میان مهندسان مختلف تقسیم کنیم. دلیل اصلی استفاده از دایرکتیوها در هر اپلیکیشن انگولار به شرح زیر است:

  • قابلیت استفاده مجدد – دایرکتیو در یک اپلیکیشن انگولار به بخش مستقلی از UI گفته می‌شود. ما به عنوان توسعه‌دهنده می‌توانیم از دایرکتیو در بخش‌های مختلف اپلیکیشن بهره بگیریم. این وضعیت در اپلیکیشن‌های بزرگ-مقیاس که چندین سیستم به عناصر کارکردی یکسانی از قبیل کادر جستجو، کنترل تاریخ و غیره نیاز دارند، اهمیت دوچندان می‌یابد.
  • قابلیت خوانایی – دایرکتیوها خوانایی بیشتری در اختیار ما قرار می‌دهند و می‌توانیم کارکرد پروداکشن و گردش داده‌ها را درک کنیم.
  • قابلیت نگهداری – یکی از کاربردهای اصلی دایرکتیو در هر اپلیکیشنی ایجاد قابلیت نگهداری است. به این ترتیب می‌توانیم به سادگی دایرکتیو را از اپلیکیشن جدا کنیم و یا یک دایرکتیو قدیمی را با دایرکتیو جدید جایگزین نماییم.

تفاوت کامپوننت‌ها با دایرکتیو‌ها

در جدول زیر تفاوت بین کامپوننت‌ها و دایرکتیوها را توضیح می‌دهیم.

کامپوننتدایرکتیو
کامپوننت با دکوراتور Component@ تعریف می‌شود.دایرکتیو با دکوراتور Directive@ تعریف می‌شود.
کامپوننت دایرکتیوی است که از یک DOM سایه برای ایجاد رفتار بصری کپسوله‌سازی‌شده به نام کامپوننت استفاده می‌کند. کامپوننت‌ها به طور معمول برای ایجاد ویجت‌های UI استفاده می‌شود.دایرکتیو به طور عمده برای ارائه رفتار جدید درون عناصر DOM موجود استفاده می‌شود.
به کمک کامپوننت می‌توانیم اپلیکیشن را به چند بخش کوچک‌تر تقسیم کنیم.با کمک دایرکتیو می‌توانیم هر نوع کامپوننت که قابلیت استفاده مجدد داشته باشد را طراحی کنیم.
در DOM مرورگر تنها یک کامپوننت می‌تواند به عنوان کامپوننت والد فعال شود. در این حالت، کامپوننت‌های دیگر به عنوان کامپوننت فرزند عمل می‌کنند.درون یک عنصر منفرد DOM، هر تعداد دایرکتیو می‌تواند مورد استفاده قرار گیرد
وجود دکوراتور @View یا قالب templateUrl در هر کامپوننت الزامی است.دایرکتیو از View استفاده نمی‌کند.

متادیتای Directive@

دکوراتور Directive@ برای تعریف کردن یک کلاس به صورت دایرکتیو انگولار مورد استفاده قرار می‌گیرد. ما همواره می‌توانیم دایرکتیوها را برای تعیین رفتار سفارشی عناصر درون DOM تعریف کنیم. متادیتای Directive@ شامل گزینه‌های زیر است:

  • Selector – مشخصه سلکتور برای شناسایی دایرکتیو درون قالب HTML استفاده می‌شود. همچنین به کمک این مشخصه می‌توانیم دایرکتیو را از روی عناصر DOM مقداردهی کنیم.
  • Inputs – این گزینه برای ارائه یک مجموعه از مشخصه‌های ورودی متصل به داده از دایرکتیوها استفاده می‌شود.
  • Outputs – این گزینه برای شمارش مشخصه‌های رویداد از دایرکتیوها استفاده می‌شود.
  • Providers – این گزینه برای تزریق هر نوع ارائه‌دهنده مانند سرویس یا کامپوننت‌های درون دایرکتیوها استفاده می‌شود.
  • exportAs – این گزینه برای تعریف نام دایرکتیوهایی استفاده می‌شود که می‌توانند برای انتساب دایرکتیو به عنوان یک متغیر استفاده شوند.

انواع دایرکتیوها

سه نوع عمده از دایرکتیوها در انگولار وجود دارند:

  • Component – شامل دایرکتیوهای دارای قالب است.
  • Attribute Directives – دایرکتیوهایی هستند که رفتار یک کامپوننت یا عنصر را تغییر می‌دهند، اما تأثیری روی قالب ندارند.
  • Structural Directives – دایرکتیوی‌هایی هستند که رفتار یک عنصر یا کامپوننت را از طریق تأثیرگذاری روی قالب یا دکوراسیون DOM در UI تغییر می‌دهند.

آموزش انگولار

دایرکتیو خصوصیت چیست؟

«دایرکتیوهای خصوصیت» (Attribute Directive) به طور عمده برای تغییر دادن ظاهر یا رفتار یک کامپوننت یا یک عنصر نیتیو DOM مورد استفاده قرار می‌گیرند. دایرکتیوهای خصوصیت عملاً ظاهر یا رفتار یک عنصر را تغییر می‌دهند. این دایرکتیوها به عنوان یک خصوصیت HTML برای هر تگ HTML عمل می‌کنند. برخی دایرکتیوهای داخلی خصوصیت مانند ngModel در فریمورک انگولار وجود دارند. اما ما می‌توانیم هر نوع دایرکتیو مبتنی بر خصوصیت سفارشی را نیز بسته به نیاز ایجاد کنیم. در این حالت، از نام سلکتور دایرکتیو خصوصیت به عنوان یک خصوصیت درون تگ HTML در بخش کد صفحه استفاده می‌کنیم.

مثالی از دایرکتیو خصوصیت

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

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  showColor: boolean = false;  
11  
12  constructor() { }  
13  
14  public changeColor(): void {  
15      this.showColor = !this.showColor;  
16  }  
17}

فایل app.component.html

1<div>  
2    <h3>This is a Attribute Directives</h3>  
3    <span [class.red]="true">Attribute Change</span><br />  
4    <span [ngClass]="{'blue':true}">Attribute Change by Using NgClass</span><br />  
5    <span [ngStyle]="{'font-size':'14px','color':'green'}">Attribute Change by Using NgStyle</span>  
6    <br /><br />  
7    <span [class.cyan]="showColor">Attribute Change</span><br />  
8    <span [ngClass]="{'brown':showColor}">Attribute Change by Using NgClass</span><br />  
9    <input type="button" value="Change Color" (click)="changeColor()" />  
10    <br /><br />  
11    <span [class.cyan]="showColor">Attribute Change</span><br />  
12    <span [ngClass]="{'cyan':showColor, 'red' : !showColor}">Attribute Change by Using NgClass</span><br />  
13    <br />  
14</div>

فایل custom.css

1.red {color:red;}  
2.blue {color:blue}  
3.cyan {color : cyan}  
4.brown {color : brown}

اکنون خروجی را در مرورگر بررسی کنید.

آموزش انگولار

دایرکتیوهای داخلی خصوصیت

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

  • ngClass – دایرکتیو ngClass موجب تغییر خصوصیت class متصل به کامپوننت یا عنصر می‌شود.
  • ngStyle – دایرکتیو ngStyle برای تغییر یا ویرایش خصوصیت استایل یک عنصر مورد استفاده قرار می‌گیرد. این دایرکتیو خصوصیت کاملاً مشابه استفاده از متادیتای استایل در کلاس کامپوننت است.

دایرکتیو ساختاری چیست؟

انواع دیگری از دایرکتیو در فریمورک انگولار وجود دارند که «دایرکتیوهای ساختاری» (Structural Directive) نام دارند. دایرکتیو‌های ساختاری به طور عمده برای تغییر الگوی طراحی عناصر DOM در UI مورد استفاده قرار می‌گیرند. این دایرکتیوها در HTML به عنوان تگ قالب مورد استفاده قرار می‌گیرند. ما با استفاده از این نوع دایرکتیوها می‌توانیم ساختار هر عنصر DOM را تغییر داده و آن عناصر DOM را بازطراحی کرده یا از نو تزیین کنیم.

در فریمورک انگولار برخی دایرکتیوهای ساختاری مانند ngIf ،ngFor و ngSwitch وجود دارند که از سوی سیستم تولید می‌شوند. همچنین می‌توانیم هر نوع دایرکتیو ساختاری دیگر را نیز به صورت سفارشی ایجاد کنیم.

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

دایرکتیوهای داخلی ساختاری

انگولار دایرکتیوهای ساختاری زیر را عرضه کرده است که می‌توانند درون یک کامپوننت برای تغییر دادن ساختار یا طراحی عناصر مورد استفاده قرار گیرند.

  • ngIf
  • ngFor
  • ngSwitch

مثالی از کاربرد ngIf

در این مثال به بررسی شیوه استفاده از ngIf در یک اپلیکیشن انگولار می‌پردازیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  showInfo: boolean = false;  
11  caption: string = 'Show Text';  
12  
13  constructor() { }  
14  
15  public changeData(): void {  
16      this.showInfo = !this.showInfo;  
17      if (this.showInfo) {  
18          this.caption = 'Hide Text';  
19      }  
20      else {  
21          this.caption = 'Show Text';  
22      }  
23  }  
24}

فایل app.component.html

1<div>  
2    <input type="button" value="{{caption}}" (click)="changeData()"/>  
3    <br />  
4    <h2 *ngIf="showInfo"><span>Demonstrate of Structural Directives - *ngIf</span></h2>  
5</div>

خروجی این مثال در مرورگر به صورت زیر است.

آموزش انگولار

مثالی از کاربرد ngFor

در این مثال، کاربرد دایرکتیو ngFor را در یک اپلیکیشن انگولار بررسی می‌کنیم.

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  productList: Array<string> = ['IPhone','Galaxy 9.0','Blackberry 10Z'];  
11  
12  constructor() { }  
13<b>} </b>

فایل app.component.html

1<div>  
2    <h2>Demonstrate ngFor</h2>  
3    <ul>  
4        <li *ngFor="let item of productList">  
5            {{item}}  
6        </li>  
7    </ul>  
8</div>

خروجی این مثال در مرورگر به صورت زیر است.

آموزش انگولار

مثالی از کاربرد ngSwitch

برای مشاهده کاربرد دایرکتیو ngSwitch به مثال زیر توجه کنید.

فایل app.component.ts

1import { Component, OnInit } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent implements OnInit {  
9    
10  studentList: Array<any> = new Array<any>();  
11  
12    constructor() { }  
13    ngOnInit() {  
14        this.studentList = [  
15            { SrlNo: 1, Name: 'Rajib Basak', Course: 'Bsc(Hons)', Grade: 'A' },  
16            { SrlNo: 2, Name: 'Rajib Basak1', Course: 'BA', Grade: 'B' },  
17            { SrlNo: 3, Name: 'Rajib Basak2', Course: 'BCom', Grade: 'A' },  
18            { SrlNo: 4, Name: 'Rajib Basak3', Course: 'Bsc-Hons', Grade: 'C' },  
19            { SrlNo: 5, Name: 'Rajib Basak4', Course: 'MBA', Grade: 'B' },  
20            { SrlNo: 6, Name: 'Rajib Basak5', Course: 'MSc', Grade: 'B' },  
21            { SrlNo: 7, Name: 'Rajib Basak6', Course: 'MBA', Grade: 'A' },  
22            { SrlNo: 8, Name: 'Rajib Basak7', Course: 'MSc.', Grade: 'C' },  
23            { SrlNo: 9, Name: 'Rajib Basak8', Course: 'MA', Grade: 'D' },  
24            { SrlNo: 10, Name: 'Rajib Basak9', Course: 'B.Tech', Grade: 'A' }  
25        ];  
26    }  
27}

فایل app.component.html

1<div>  
2    <h2>Demonstrate ngSwitch</h2>  
3    <table style="width:100%;border:solid;border-color:blue;border-width:thin;">  
4        <thead>  
5            <tr >  
6                <td>Srl No</td>  
7                <td>Student Name</td>  
8                <td>Course</td>  
9                <td>Grade</td>  
10            </tr>  
11        </thead>  
12        <tbody>  
13            <tr *ngFor="let student of studentList;" [ngSwitch]="student.Grade">  
14                <td>  
15                    <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.SrlNo}}</span>  
16                    <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.SrlNo}}</span>  
17                    <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.SrlNo}}</span>  
18                    <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.SrlNo}}</span>  
19                </td>  
20                <td>  
21                    <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Name}}</span>  
22                    <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Name}}</span>  
23                    <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Name}}</span>  
24                    <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Name}}</span>  
25                </td>  
26                <td>  
27                    <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Course}}</span>  
28                    <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Course}}</span>  
29                    <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Course}}</span>  
30                    <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Course}}</span>  
31                </td>  
32                <td>  
33                    <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Grade}}</span>  
34                    <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Grade}}</span>  
35                    <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Grade}}</span>  
36                    <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Grade}}</span>  
37                </td>  
38            </tr>  
39        </tbody>  
40    </table>  
41</div>

خروجی این مثال در مرورگر به صورت زیر است:

آموزش انگولار

دایرکتیو سفارشی

برای ایجاد دایرکتیوهای خصوصیت باید از اشیای زیر استفاده کرده و یا آن‌ها را در کلاس کامپوننت دایرکتیو خصوصیت سفارشی تزریق کنیم. برای ایجاد یک دایرکتیو خصوصیت باید موضوع‌های زیر را به خاطر داشته باشیم:

  1. ماژول‌های مورد نیاز مانند دایرکتیوها ElementRef و renderer را از کتابخانه مرکزی انگولار ایمپورت کنیم.
  2. یک کلاس TypeScript ایجاد کنیم.
  3. از دکوراتور Directive@ در کلاس استفاده کنیم.
  4. مقدار مشخصه سلکتور را در تابع دکوراتور Directive@ تعریف کنیم. این دایرکتیو با استفاده از مقدار سلکتور روی عناصر مورد استفاده قرار خواهد گرفت.
  5. در سازنده کلاس، ElementRef و شیء renderer را تزریق کنیم.
  6. باید ElementRef را در سازنده دایرکتیو تزریق کنیم تا به عنصر DOM دسترسی داشته باشیم.
  7. همچنین باید رندرر را در سازنده دایرکتیو تزریق کنید تا بتوانید روی استایل عنصر DOM کار کنید.
  8. در نهایت باید تابع setElementStyle رندرر را فراخوانی کنید. در این تابع باید وهله عنصر کنونی DOM را با کمک ElementRef به صورت یک پارامتر ارسال کنیم و رفتار یا مشخصه عنصر جاری را تعیین کنیم.
  • ElementRef – در زمان ایجاد یک دایرکتیو خصوصیت سفارشی باید ElementRef را در سازنده تزریق کنیم تا به عنصر DOM دسترسی داشته باشیم. ElementRef امکان دسترسی به عنصر نیتیو زیربنایی را فراهم می‌سازد. به کمک ElementRef می‌توانیم با استفاده از مشخصه nativeElement آن، دسترسی مستقیمی به عنصر DOM داشته باشیم. در این حالت، ElementRef درست مانند یک سرویس عمل می‌کند. این تنها چیزی است که برای تعیین رنگ عنصر با استفاده از DOM API نیاز داریم.
  • Renderer – در زمان ایجاد یک دایرکتیو خصوصیت سفارشی، Renderer را در سازنده تزریق می‌کنیم تا به استایل عنصر DOM دسترسی داشته باشیم. در واقع ما تابع setElementStyle رندرر را فراخوانی می‌کنیم. در این تابع، عنصر کنونی DOM را به کمک شیء setElementStyle ارسال و خصوصیت مورد نیاز عنصر جاری را تعیین می‌کنیم.
  • HostListener – گاهی اوقات باید به مشخصه ورودی درون دایرکتیو خصوصیت دسترسی داشته باشیم تا بتوانیم خصوصیت مرتبطی را درون عنصر DOM اِعمال کنیم. برای به دست آوردن اکشن‌های کاربر می‌توانیم متدهای مختلفی را فراخوانی کنیم. این متد برای اجرای هر نوع اکشنی استفاده می‌شود. به این منظور باید متد HostListener@ را به این متد اضافه کنیم.

مثالی از کاربرد دایرکتیو‌های سفارشی برای تغییر رنگ

در این مثال یک دایرکتیو مبتنی بر خصوصیت سفارشی می‌سازیم که رنگ متن انتخابی را در زمان رویداد mouseover تغییر می‌دهد. به این منظور ابتدا باید دایرکتیو زیر را اجرا کنیم.

فایل app.directive.ts

1import { Directive, ElementRef, Renderer, HostListener, Input } from '@angular/core';  
2  
3@Directive({  
4    selector: '[colorchange]'  
5})  
6export class ColorChangeDirective {  
7    private _defaulColor = 'red';  
8    @Input('colorchange') highlightColor: string;  
9  
10    constructor(private el: ElementRef, private render: Renderer) {  
11    }  
12  
13    @HostListener('mouseenter') onMouseEnter() {  
14        console.log(this.highlightColor);  
15        this.changecolor(this.highlightColor || this._defaulColor);  
16    }  
17  
18    @HostListener('mouseleave') onMouseLeave() {  
19        console.log(this.highlightColor);  
20        this.changecolor(null);  
21    }  
22  
23    private changecolor(color: string) {  
24        this.render.setElementStyle(this.el.nativeElement, 'color', color);  
25    }  
26}

سپس از این دایرکتیو سفارشی در کامپوننت app-root به صورت زیر استفاده می‌کنیم:

فایل app.component.ts

1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    
10  public message: string = 'Sample Demostration of Attribute Directives using Custom Directives';  
11  public color: string = 'blue';  
12  
13}

فایل app.component.html

1<div>  
2    <input type="radio" name="colors" (click)="color='blue'">blue  
3    <input type="radio" name="colors" (click)="color='orange'">orange  
4    <input type="radio" name="colors" (click)="color='green'">green  
5</div>  
6<h1 [colorchange]="color">{{message}}</h1>

اینک دایرکتیو سفارشی که در بخش قبل ساختیم را در AppModule به صورت زیر قرار می‌دهیم

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3import { FormsModule } from '@angular/forms';  
4  
5import { AppComponent } from './app.component';  
6import { ColorChangeDirective } from './app.directive';  
7  
8@NgModule({  
9  declarations: [  
10    AppComponent,ColorChangeDirective  
11  ],  
12  imports: [  
13    BrowserModule,FormsModule  
14  ],  
15  providers: [],  
16  bootstrap: [AppComponent]  
17})  
18export class AppModule { }

نتیجه نهایی باید به صورت زیر باشد:

آموزش انگولار

بدین ترتیب با مفهوم دایرکتیو در انگولار آشنا شدیم. در بخش بعدی در خصوص Pipe در انگولار توضیح می‌دهیم. با استفاده از Pipe می‌توانیم داده‌ها را به صورت قالب مورد نظر خودمان درآوریم. هدف اصلی استفاده از Pipe انتقال داده‌ها درون یک قالب HTML است.

Pipe چیست؟

زمانی که می‌خواهیم یک اپلیکیشن انگولار بسازیم، همواره کار را از اجزای ساده مانند بازیابی داده‌ها و انتقال و نمایش آن‌ها در برابر کاربر و از طریق رابط کاربری آغاز می‌کنیم. بازیابی داده‌ها از هر نوع منبع داده به صورت کامل به ارائه‌دهنده سرویس داده از قبیل WEB API و غیره بستگی دارد. از این رو زمانی که داده‌ها به دست آمد، می‌توانیم آن‌ها را مستقیماً به رابط کاربری بفرستیم تا در معرض دید کاربر قرار بگیرد. اما گاهی اوقات فرایند کار به این صورت نیست. برای نمونه در اغلب موارد کاربران ترجیح می‌دهند که داده‌ها را در قالب ساده‌ای مانند 15/02/2020 ببینند تا این که یک رشته خام مانند زیر را شاهد باشند:

Wed Feb 15 2017 00:00:00 GMT-0700 (Pacific Daylight Time)

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

Pipe-ها در انگولار به صورت کلاس‌های تایپ اسکریپت هستند که یک اینترفیس تابع منفرد را پیاده‌سازی می‌کنند، یک مقدار ورودی با یک پارامتر اختیاری آرایه می‌پذیرند و یک مقدار تبدیل‌یافته بازگشت می‌دهند. برای اجرای تبدیل مقدار، می‌توانیم هر نوع منطق تجاری را بسته به نیاز پیاده‌سازی کنیم. این Pipe می‌تواند در هر UI بسته به نتیجه مورد نیاز، برای تبدیل آن نوع خاص به داده‌ها استفاده شود.

مفهوم ابتدایی Pipe-ها

Pipe-ها اساساً یک روش جالب و پیچیده برای اجرای وظایف مختلف درون قالب‌ها فراهم می‌سازند. Pipe موجب می‌شود که کد ما تمیز و ساخت‌یافته باشد. Pipe-ها در انگولار، مقادیر را از عناصر DOM می‌گیرند و بر اساس منطق تجاری پیاده‌سازی‌شده درون Pipe-ها یک مقدار بازگشت می‌دهند. بنابراین، Pipe-ها یکی از قابلیت‌های عالی انگولار محسوب می‌شوند که از طریق آن‌ها داده‌های خود را به UI تبدیل کرده و نمایش می‌دهیم. اما یک نکته را باید به روشنی درک کنیم و آن این است که پایپ‌ها به صورت خودکار مقدار مدل ما را به‌روزرسانی نمی‌کنند.

پایپ تبدیل داده‌ها را انجام می‌دهند و آن را به کامپوننت بازگشت می‌دهد. اگر در ادامه نیاز باشد که UI را به‌روزرسانی کنیم، در این صورت باید داده‌های مدل را به صورت دستی به‌روزرسانی کنیم. به جز این، می‌توانیم از پایپ‌ها برای هر یک از دلایل زیر استفاده کنیم:

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

چه نیازی به Pipe داریم؟

در هر اپلیکیشن، پایپ می‌تواند به دلایل زیر مورد نیاز باشد:

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

انواع Pipe

در انگولار می‌توانیم پایپ‌ها را به دو دسته «پایپ‌های خالص» (Pure Pipes) و «پایپ‌های ناخالص» (Impure Pipes) تقسیم کنیم.

  • پایپ‌های خالص – پایپ‌های خالص در انگولار به پایپ‌هایی گفته می‌شود که همواره برخی آرگومان‌ها را به عنوان مقدار ورودی می‌گیرند و برخی مقادیر را بسته به مقادیر ورودی به عنوان مقدار خروجی عرضه می‌کنند. برخی نمونه‌های پایپ‌های خالص شامل پایپ‌های اعشاری، پایپ‌های تاریخ و غیره هستند. زمانی که از این نوع پایپ‌ها در انگولار استفاده کنیم، مقدار ورودی با پیکربندی مرتبط ارائه می‌شود و یک مقدار قالب‌بندی‌شده به عنوان خروجی بازگشت می‌یابد.
  • پایپ‌های ناخالص – پایپ‌های ناخالص در انگولار به انواعی گفته می‌شود که مقادیر ورودی می‌پذیرند، اما انواع متفاوتی از مقدار را بازگشت می‌دهند که بر اساس حالت مقدار ورودی تعیین می‌شوند. نمونه‌ای از پایپ‌های ناخالص شامل async pipes هستند. این پایپ‌ها همواره حالت درونی را ذخیره کرده و انواع متفاوتی از مقدار را بسته به حالت درونی و منطق در خروجی بازگشت می‌دهند.

تفاوت فیلتر با Pipe

مفهوم فیلتر به طور عمده در انگولار نسخه 1.x استفاده می‌شد. از انگولار 2 به این سو، گوگل مفهوم فیلتر را منسوخ کرده و مفهوم Pipe را جایگزین آن ساخته است. اکنون بسته به کارکرد، فیلتر و پایپ‌ها، هر دو کارکرد مشابهی دارند. اما همچنان برخی تفاوت‌ها به شرح زیر وجود دارد:

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

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

Pipe-های ابتدایی

اغلب پایپ‌ها که از سوی انگولار عرضه شده‌اند، در نسخه‌های قبلی نیز وجود دارند. در نسخه‌های جدید انگولار امکان استفاده از منطق در قالب وجود دارد. به این ترتیب می‌توانیم هر تابع را درون کلاس پایپ تعریف کنیم تا تبدیل نوع‌های خاص و یا منطق تجاری معینی را پیاده‌سازی کرده و سپس تابع خاصی را از قالب HTML اجرا کند تا نتیجه مطلوب به دست آید. ساختار پایپ در قالب HTML با یک مقدار ورودی آغاز می‌شود و سپس نماد پایپ (|) می‌آید و در ادامه باید نام پایپ ارائه شود.

پارامترهای این پایپ می‌توانند با دونقطه (:) از هم جدا شوند. ترتیب اجرای پایپ از راست به چپ است. به طور کلی پایپ صرفاً درون HTML عمل می‌کند. پایپ‌های داخلی که بیشترین استفاده را دارند به شرح زیر هستند:

  • Currency
  • Date
  • Uppercase
  • Lowercase
  • JSON
  • Decimal
  • Percent
  • Async

مثالی از یک Pipe مقدماتی

در این مثال با شیوه استفاده از Pipe-های ابتدایی در انگولار آشنا می‌شویم:

فایل app.component.ts

1import { Component, OnInit } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent implements OnInit {  
9  public todayDate: Date;  
10  public amount: number;  
11  public message: string;  
12  
13  constructor() { }  
14  
15  ngOnInit(): void {  
16    this.todayDate = new Date();  
17    this.amount = 100;  
18    this.message = "Angular 8.0 is a Component Based Framework";  
19  }  
20}
  • فایل app.component.html
1<div>  
2    <h1>Demonstrate of Pipe in Angular 8</h1>  
3    <h2>Date Pipes</h2>  
4    Full Date : {{todayDate}}<br />  
5    Short Date : {{todayDate | date:'shortDate'}}<br />  
6    Medium Date : {{todayDate | date:'mediumDate'}}<br />  
7    Full Date : {{todayDate | date:'fullDate'}}<br />  
8    Time : {{todayDate | date:'HH:MM'}}<br />  
9    Time : {{todayDate | date:'hh:mm:ss a'}}<br />  
10    Time : {{todayDate | date:'hh:mm:ss p'}}<br />  
11  
12    <h2>Number Pipes</h2>  
13    No Formatting : {{amount}}<br />  
14    2 Decimal Place : {{amount |number:'2.2-2'}}  
15  
16    <h2>Currency Pipes</h2>  
17    No Formatting : {{amount}}<br />  
18    USD Doller($) : {{amount |currency:'USD':true}}<br />  
19    USD Doller : {{amount |currency:'USD':false}}<br />  
20    INR() : {{amount |currency:'INR':true}}<br />  
21    INR : {{amount |currency:'INR':false}}<br />  
22  
23    <h2>String Related Pipes</h2>  
24    Actual Message : {{message}}<br />  
25    Lower Case : {{message | lowercase}}<br />  
26    Upper Case : {{message | uppercase}}<br />  
27  
28    <h2> Percentage Pipes</h2>  
29    2 Place Formatting : {{amount | percent :'.2'}}<br /><br />   
30</div>

خروجی مرورگر به صورت زیر است:

آموزش انگولار

ساختار پایپ‌ها

{{myValue | myPipe:param1:param2 | mySecondPipe:param1}}

Pipe-های سفارشی

در انگولار امکان تعریف پایپ‌های سفارشی بسته به منطق خاص تجاری وجود دارد. برای پیکربندی پایپ‌های سفارشی باید از شیء pipe استفاده کنیم. به این منظور باید یک پایپ سفارشی با دکوراتور Pipe@ تعریف کنیم و با افزودن یک مشخصه پایپ به دکوراتور ‎@View یا با نام کلاس پایپ، آن را مورد استفاده قرار دهیم. ما از متد تبدیلی برای انجام هر گونه منطق مورد نیاز برای تبدیل مقداری که به عنوان ورودی ارسال شده استفاده می‌کنیم. می‌توانیم یک آرایه از آرگومان‌ها را به عنوان پارامتر دوم داشته باشیم و هر تعداد که دوست داریم از قالب ارسال کنیم. دکوراتور Pipe@ شامل و مشخصه زیر است:

  1. Name – این مشخصه شامل نام پایپ است که در عناصر DOM مورد استفاده قرار می‌گیرد.
  2. Pure – این مشخصه یک مقدار بولی می‌پذیرد. این مشخصه تعیین می‌کند که پایپ از نوع خالص یا ناخالص است. مقدار پیش‌فرض مشخصه pure به صورت true است یعنی تصور می‌شود که یک پایپ سفارشی همواره یک پایپ خالص است. از این رو فریمورک انگولار یک پایپ خالص را زمانی اجرا می‌کند که یک تغییر خالص در مقدار ورودی تشخیص دهد. داده‌های تغییرهای خالص می‌توانند از نوع مقدماتی (شامل مقادیر منفرد) یا غیر مقدماتی (داده‌های شامل نوع داده‌ای که گروهی از مقادیر را نیز می‌پذیرد). اگر لازم باشد که یک پایپ را به شکل غیر خالص دربیاوریم، در این صورت باید مقدار false را برای این مشخصه ارسال کنیم. در مورد پایپ ناخالص، فریمورک انگولار، پایپ‌ها را در هر بار چرخه تشخیص تغییر در کامپوننت اجرا خواهد کرد. در این حالت، پایپ غالباً در هر بار ضربه کیبورد یا حرکت ماوس فراخوانی می‌شود.

زمانی که یک کلاس پایپ با استفاده از دکوراتور Pipe@ تعریف می‌شود، باید اینترفیس PipeTransforms را پیاده‌سازی کنیم که به طور عمده برای گرفتن مقادیر ورودی (مقادیر اختیاری) و بازگشت مقادیر تبدیل‌یافته به DOM مورد استفاده قرار می‌گیرد.

مثالی از Pipe سفارشی

در این بخش یک Pipe سفارشی را تعریف می‌کنیم در مثال قبل دیدیم که انگولار پایپ‌های UpperCase و LowerCase را روی انواع رشته‌ای به صورت پایپ‌های داخلی خود عرضه کرده است. اما انگولار هیچ پایپی به صورت Proper Case عرضه نکرده است. از این رو این Pipe را تعریف می‌کنیم تا هر مقدار رشته را به حالت Proper تبدیل کنیم و نتیجه را بازگشت دهیم. به این منظور باید فایل‌های کلاس تایپ اسکریپت زیر را تعریف کنیم:

  • فایل propercase.pipe.ts
1import { Pipe, PipeTransform } from "@angular/core"  
2  
3@Pipe({  
4    name: 'propercase'  
5})  
6  
7export class ProperCasePipe implements PipeTransform {  
8    transform(value: string, reverse: boolean): string {  
9        if (typeof (value) == 'string') {  
10            let intermediate = reverse == false ? value.toUpperCase() : value.toLowerCase();  
11            return (reverse == false ? intermediate[0].toLowerCase() :  
12                intermediate[0].toUpperCase()) + intermediate.substr(1);  
13        }  
14        else {  
15            return value;  
16        }  
17    }  
18}

در ادامه پایپ proper case را به صورت زیر در AppModule قرار می‌دهیم:

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3import { FormsModule } from '@angular/forms';  
4  
5import { AppComponent } from './app.component';  
6import { ColorChangeDirective } from './app.directive';  
7import { ProperCasePipe } from './propercase.pipe';  
8  
9@NgModule({  
10  declarations: [  
11    AppComponent,ColorChangeDirective,ProperCasePipe  
12  ],  
13  imports: [  
14    BrowserModule,FormsModule  
15  ],  
16  providers: [],  
17  bootstrap: [AppComponent]  
18})  
19export class AppModule { }
  • فایل app.component.ts
1import { Component, OnInit } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent implements OnInit {  
9  public message: string;  
10  
11  constructor() { }  
12  
13  ngOnInit(): void {  
14    this.message = "This is a Custom Pipe";  
15  }  
16}
  • فایل app.component.html
1<div>  
2    <div class="form-horizontal">  
3        <h2 class="aligncenter">Custom Pipes - Proper Case</h2><br />  
4        <div class="row">  
5            <div class="col-xs-12 col-sm-2 col-md-2">  
6                <span>Enter Text</span>  
7            </div>  
8            <div class="col-xs-12 col-sm-4 col-md-4">  
9                <input type="text" id="txtFName" placeholder="Enter Text" [(ngModel)]="message" />  
10            </div>  
11        </div>  
12        <div class="row">  
13            <div class="col-xs-12 col-sm-2 col-md-2">  
14                <span>Result in Proper Case</span>  
15            </div>  
16            <div class="col-xs-12 col-sm-4 col-md-4">  
17                <span>{{message | propercase}}</span>  
18            </div>  
19        </div>  
20    </div>  
21</div>

با اجرای این مثال، خروجی زیر را در مرورگر مشاهده خواهید کرد:

آموزش انگولار

Viewchild چیست؟

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

به این ترتیب برخی موقعیت‌ها وجود دارند که در آن‌ها کامپوننت والد باید با کامپوننت فرزند تعامل پیدا کند. ما در این موقعیت‌ها می‌توانیم به روش‌های مختلف، ارتباط‌هایی بین کامپوننت‌های والد و فرزند برقرار سازیم. یکی از این روش‌ها دکوراتور Viewchild است.

در صورتی که بخواهیم به وهله‌ای از کامپوننت فرزند یا یک دایرکتیو کلاس کامپوننت والد دسترسی داشته باشیم، می‌توانیم از دکوراتور Viewchild استفاده کنیم. بنابراین زمانی که لازم شود هر نوع متد کامپوننت فرزند را از کامپوننت والد اجرا کنیم، می‌توانیم کامپوننت فرزند را به صورت یک Viewchild درون کامپوننت والد تزریق کنیم. در مواردی که بخواهیم به چند فرزند دسترسی داشته باشیم، می‌توانیم به جای Viewchild از Viewchildren استفاده کنیم.

برای پیاده‌سازی ViewChild باید دکوراتور ViewChild@ را در کامپوننت والد مورد استفاده قرار دهیم. این دکوراتور به ما دسترسی به کلاس کامپوننت فرزند را از کامپوننت والد فراهم می‌سازد. دکوراتور ViewChild اساساً یک تابع یا متد است که نام کلاس کامپوننت را به عنوان ورودی خود می‌گیرند و سلکتور آن را در قالب کامپوننت مرتبط یافته و متصل می‌شود. ساختار ابتدایی دکوراتور ViewChild به صورت زیر است:

@ViewChild(ChildComponent, { static:true}) _calculator: ChildComponent;

بنابراین بر اساس مثال فوق، دکوراتور ViewChild شامل متادیتای زیر است:

  • Selector – این سلکتور شامل نام دایرکتیو یا کامپوننت است که باید به عنوان ViewChild استفاده شود.
  • Static – این مشخصه یک مقدار بولی می‌گیرد. در صورتی که می‌خواهد نتیجه کوئری پیش از تشخیص تغییر ریزالو شود، باید مقدار true به آن بدهید. بنابراین زمانی که مقدار true ارسال شود، فریمورک انگولار تلاش می‌کند تا آن عنصر را در زمان مقداردهی کامپوننت یعنی در متد ngOnInit پیدا کند. اگر مقدار false به این مشخصه بدهیم، انگولار تلاش خواهد کرد تا عنصر را پس از مقداردهی نما یعنی در متد ngAfterViewInit پیدا کند.

مثالی از ViewChild

در این بخش به بررسی شیوه استفاده از دکوراتور ViewChild درون یک کامپوننت می‌پردازیم. به این منظور ابتدا باید یک کامپوننت فرزند بسازیم که به عنوان یک ViewChild در کامپوننت والد یا روت استفاده می‌شود.

  • فایل child.component.ts
1import { Component, OnInit, Output, EventEmitter } from '@angular/core';  
2  
3@Component({  
4    selector: 'child',  
5    templateUrl: 'child.component.html'  
6})  
7  
8export class ChildComponent implements OnInit {  
9    private firstNumber: number = 0;  
10    private secondNumber: number = 0;  
11    private result: number = 0;  
12  
13    @Output() private addNumber: EventEmitter<number> = new EventEmitter<number>();  
14    @Output() private subtractNumber: EventEmitter<number> = new EventEmitter<number>();  
15    @Output() private multiplyNumber: EventEmitter<number> = new EventEmitter<number>();  
16    @Output() private divideNumber: EventEmitter<number> = new EventEmitter<number>();  
17  
18    constructor() { }  
19  
20    ngOnInit(): void {  
21    }  
22  
23    private add(): void {  
24        this.result = this.firstNumber + this.secondNumber;  
25        this.addNumber.emit(this.result);  
26    }  
27  
28    private subtract(): void {  
29        this.result = this.firstNumber - this.secondNumber;  
30        this.subtractNumber.emit(this.result);  
31    }  
32  
33    private multiply(): void {  
34        this.result = this.firstNumber * this.secondNumber;  
35        this.multiplyNumber.emit(this.result);  
36    }  
37  
38    private divide(): void {  
39        this.result = this.firstNumber / this.secondNumber;  
40        this.divideNumber.emit(this.result);  
41    }  
42  
43    public clear(): void {  
44        this.firstNumber = 0;  
45        this.secondNumber = 0;  
46        this.result = 0;  
47    }  
48}
  • فایل child.component.html
1<div class="ibox-content">  
2    <div class="row">  
3        <div class="col-md-4">  
4            Enter First Number  
5        </div>  
6        <div class="col-md-8">  
7            <input type="number" [(ngModel)]="firstNumber" />  
8        </div>  
9    </div>  
10    <div class="row">  
11        <div class="col-md-4">  
12            Enter Second Number  
13        </div>  
14        <div class="col-md-8">  
15            <input type="number"  [(ngModel)]="secondNumber" />  
16        </div>  
17    </div>  
18    <div class="row">  
19        <div class="col-md-4">  
20        </div>  
21        <div class="col-md-8">  
22           <input type="button" value="+" (click)="add()" />  
23                
24            <input type="button" value="-" (click)="subtract()" />  
25                
26            <input type="button" value="X" (click)="multiply()" />  
27                
28            <input type="button" value="/" (click)="divide()" />  
29        </div>  
30    </div>  
31</div>

اینک کامپوننت فرزند را در فایل app.module.ts قرار می‌دهیم.

1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule } from '@angular/core';  
3import { FormsModule } from '@angular/forms';  
4  
5import { AppComponent } from './app.component';  
6import { ChildComponent } from './child.component';  
7  
8@NgModule({  
9  declarations: [  
10    AppComponent,ChildComponent  
11  ],  
12  imports: [  
13    BrowserModule, FormsModule  
14  ],  
15  providers: [],  
16  bootstrap: [AppComponent]  
17})  
18export class AppModule { }

اینک می‌توانیم کامپوننت فرزند فوق را در کامپوننت root به صورت زیر استفاده کنیم.

  • فایل app.component.ts
1import { Component,ViewChild } from '@angular/core';  
2import { ChildComponent } from './child.component';  
3  
4@Component({  
5  selector: 'app-root',  
6  templateUrl: './app.component.html',  
7  styleUrls : ['./custom.css']  
8})  
9export class AppComponent {  
10  private result: string = '';  
11  
12    @ViewChild(ChildComponent, { static:true}) private _calculator: ChildComponent;  
13  
14    constructor() {  
15    }  
16  
17    private add(value: number): void {  
18        this.result = 'Result of Addition ' + value;  
19    }  
20  
21    private subtract(value: number): void {  
22        this.result = 'Result of Subtraction ' + value;  
23    }  
24  
25    private multiply(value: number): void {  
26        this.result = 'Result of Multiply ' + value;  
27    }  
28  
29    private divide(value: number): void {  
30        this.result = 'Result of Division ' + value;  
31    }  
32  
33    private reset(): void {  
34        this.result = '';  
35        this._calculator.clear();  
36    }  
37}
  • فایل app.component.html
1<h2>Demo of Viewchild</h2>  
2<div>  
3    <child (addNumber)="add($event)" (subtractNumber)="subtract($event)" (multiplyNumber)="multiply($event)"  
4            (divideNumber)="divide($event)" #calculator></child>  
5</div>  
6<h3>Result</h3>  
7<span>{{result}}</span>  
8<br />  
9<br />  
10<input type="button" value="Reset" (click)="reset()" />

خروجی مثال فوق در مرورگر به صورت زیر است:

آموزش انگولار

در این بخش از راهنمای جامع انگولار با مفهوم Pipe در این فریمورک آشنا شدیم. همچنین انواع مختلفی از Pipe-ها از قبیل پایپ‌های خالص و ناخالص را بررسی کردیم. در ادامه این مقاله به بررسی دو مفهوم دیگر به نام‌های کپسوله‌سازی نما و پروجکشن محتوا خواهیم پرداخت.

کپسوله‌سازی نما

در این بخش از راهنمای آموزش انگولار به بررسی جنبه دیگری از مدیریت DOM در انگولار می‌پردازیم. در توسعه وب‌اپلیکیشن، کامپوننت وب نقش مهمی در زمینه طراحی و توسعه UI وب ایفا می‌کند. کامپوننت‌های وب به طور کلی در همه مرورگرهای مدرن به جز Microsoft Edge و IE 11 حضور دارند. اما برای این مرورگرها نیز می‌توان از polyfills استفاده کرد. کامپوننت‌های وب به طور عمده شامل سه عنصر زیر هستند:

  • عناصر سفارشی (Custom Elements) - شامل عناصر کاملاً معتبر HTML
  • DOM سایه – شامل CSS جدا شده و API جاوا اسکریپت
  • قالب‌های تعریف‌شده‌ی کاربر

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

کامپوننت وب چیست؟

فریمورک انگولار همواره یک مجموعه از کتابخانه‌ها برای توسعه کامپوننت‌های کپسوله‌سازی‌شده، تزریق وابستگی، قالب‌های HTML مبتنی بر اتصال داده و غیره ارائه می‌کند. با استفاده از این فریمورک، می‌توانیم منطق نمایش UI (کامپوننت‌ها) را از منطق تجاری اپلیکیشن (سرویس‌ها و منطق) طوری جدا کنیم که چند توسعه‌دهنده یا تیم‌های مختلف بتوانند هم‌زمان روی بخش‌های مختلف یک اپلیکیشن کار کنند. یکی از اجزای کلیدی برای پیاده‌سازی این رویکرد، کامپوننت‌های وب هستند.

کامپوننت‌های وب به گروهی از API-های استاندارد گفته می‌شود که امکان ایجاد تگ‌های مبتنی بر HTML سفرشی را فراهم می‌سازند که کارکرد و چرخه عمر کامپوننت خاص خود را دارند. هدف اصلی کامپوننت وب، کپسوله‌سازی کد برای کامپوننت‌های انگولار به صورت یک پکیج با قابلیت استفاده مجدد با بیشترین امکان «هم‌کنش‌پذیری» (interoperability) است. به بیان ساده کامپوننت‌های وب مجموعه‌ای از API-های استاندارد هستند که به ما امکان می‌دهند تا عناصر سفارشی با قابلیت استفاده مجدد بسازیم که کل کارکردشان از باقی کد جدا شود و بتوانیم به صورت مستقل از آن‌ها در وب‌اپلیکیشن خود استفاده کنیم.

برای تعریف کردن هر کامپوننت وب باید قابلیت‌های زیر را در هر کامپوننت انگولار تعریف کنیم، به طوری که به عنوان یک کامپوننت وب عمل کند.

  • عناصر سفارشی – این مشخصات به ما کمک می‌کنند که انواع جدیدی از عناصر DOM را طراحی کنیم.
  • DOM سایه – به ما کمک می‌کند که شیوه استفاده از استایل کپسوله‌سازی‌شده و markup را در کامپوننت‌های وب تعریف کنیم.
  • ماژول‌های ES – این مشخصه موجب می‌شود بتوانیم هر کتابخانه جاوا اسکریپت را تعریف کنیم و گزینه‌هایی برای استفاده مجدد از آن‌ها در اسناد به روش استاندارد فراهم می‌سازد.
  • قالب‌های HTML – با استفاده از این مشخصه می‌توانیم هر مارکاپ HTML را تعریف کنیم که به صورت نرمال در زمان بارگذاری صفحه در حالت بی‌استفاده است، اما متعاقباً در زمان وقوع یک رویداد خاص یا اجرای یک کار معین فعال یا مقداردهی می‌شود.

مزیت‌های کامپوننت وب

ما به عنوان توسعه‌دهنده ترجیح می‌دهیم تا حد امکان از کدهای خود در توسعه اپلیکیشن به صورت مجدد استفاده بکنیم. هدف اصلی استفاده از کامپوننت‌های وب فراهم ساختن امکان استفاده مجدد از کد است. یک کامپوننت وب اساساً به فرایند استانداردشده ساخت عناصر رابط کاربری کپسوله‌سازی‌شده و با قابلیت استفاده مجدد گفته می‌شود. یک کامپوننت وب همواره قابلیت‌های زیر را در فرایند توسعه وب در اختیار ما قرار می‌دهد

  • قابلیت استفاده مجدد
  • قابلیت نگهداری
  • قابلیت بهره‌وری
  • قابلیت کپسوله‌سازی
  • قابلیت بسط‌پذیری
  • قابلیت ترکیب‌بندی

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

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

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

  • هم‌کنش‌پذیری (Interoperability) – کامپوننت‌های وب می‌توانند فریمورک را ارتقا ببخشند و در پروژه‌های مختلف با مجموعه فناوری‌های‌متفاوت مورد استفاده قرار گیرند.
  • طول عمر – از آنجا که کامپوننت وب هم‌کنش‌پذیر است، طول عمر بیشتری دارد و نیازی به بازنویسی آن‌ برای استفاده از فناوری‌های جدید وجود ندارد.
  • قابلیت انتقال – کامپوننت وب قابلیت انتقال بالایی دارد و در هر محیط با هر فریمورک یا کتابخانه‌ای کار می‌کند.

با توجه به مزایایی که در بخش فوق برشمردیم، کامپوننت وب همواره قابلیت سازگاری چندگانه دارد که نقش مهمی در محیط دائماً در حال تغییر وب ایفا می‌کند.

DOM سایه

پیش از آن که در مورد «کپسوله‌سازی نما» (View Encapsulation) در فریمورک انگولار صحبت کنیم، باید ابتدا مفهوم «DOM سایه» (Shadow DOM) را درک کنیم و همچنین دلیل استفاده از آن‌ها را متوجه شویم. DOM سایه به طور خلاصه یکی از اجزای مهم کامپوننت‌های وب است که امکان قرارگیری درخت DOM و کپسوله‌سازی CSS یا استایل را درون کامپوننت‌های وب فراهم می‌سازد.

به بیان ساده‌تر به کمک DOM سایه می‌توانیم منطق مرتبط با DOM را پشت عناصر دیگر DOM پنهان سازیم. این قابلیت بسیار خوبی است، زیرا در نهایت کامپوننتی توسعه داده‌ایم که به طور معمول یک عنصر مبتنی بر HTML منفرد (سفارشی یا تعریف‌شده‌ی کاربر) عرضه می‌کند که به همراه منطق و استایل DOM پنهان می‌تواند روی آن عنصر خاص یعنی کامپوننت وب اعمال شود. برای نمونه می‌تواند شامل یک عنصر HTML به صورت <”input type=“date> باشد. بنابراین قابلیتی زیبا و عالی است چون یک تگ منفرد داریم و مرورگر کل کنترل انتخاب‌گر تاریخ را برای ما رندر می‌کند.

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

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

بنابراین زمانی که در انگولار یک کامپوننت می‌سازیم، یک قالب HTML به shadowRoot انتساب می‌یابد که اساساً همان DOM سایه مربوط به آن کامپوننت خاص است. به این ترتیب درخت DOM و کپسوله‌سازی استایل به صورت پیش‌فرض به دست می‌آید. حتی در صورتی که مرورگر از DOM سایه پشتیبانی نکند، می‌توانیم اپلیکیشن انگولار را اجرا کنیم، زیرا این فریمورک به صورت پیش‌فرض از DOM سایه نیتیو استفاده نمی‌کند و در واقع آن را شبیه‌سازی می‌کند. دلیل این مسئله آن است که اغلب مرورگرها از قابلیت‌های DOM سایه پشتیبانی نمی‌کنند.

کپسوله‌سازی نما

فریمورک انگولار به صورت پیش‌فرض قابلیت کپسوله‌سازی نما را عرضه می‌کند. ما می‌توانیم آن را از طریق DOM سایه فعال کنیم و یا آن را شبیه‌سازی نماییم. ما با استفاده از کپسوله‌سازی نما می‌توانیم تعریف کنیم که قالب یا استایل‌های مورد استفاده درون کامپوننت‌ها بر کل اپلیکیشن تأثیر داشته باشند یا برعکس آن اتفاق بیفتد.

انواع کپسوله‌سازی نما

انگولار سه نوع کپسوله‌سازی نما ارائه کرده است:

  • ViewEncapsulation.None – در این نوع از کپسوله‌سازی هیچ گزینه‌ای برای DOM سایه ارائه نشده است. همچنین استایل دارای دامنه کامپوننت نیست. از این رو کد استایل به بخش هدر DOM منتقل خواهد شد و روی همه گره‌های کامپوننت تأثیر می‌گذارد.
  • ViewEncapsulation.Emulated – در انگولار، حالت پیش‌فرض ViewEncapsulation به این صورت یعنی شبیه‌سازی‌شده است. انگولار در این گزینه DOM سایه را برای کامپوننت تولید نخواهد کرد. بنابراین استایل کامپوننت دارای دامنه خود کامپوننت است. در این حالت تنها DOM سایه شبیه‌سازی می‌شود و واقعاً ایجاد نخواهد شد. از این رو اپلیکیشن‌ها می‌توانند در مرورگرهای فاقد پشتیبانی از DOMshdi نیز اجرا شوند و استایل‌ها نیز دارای دامنه محدود به کامپوننت خواهند بود.
  • ViewEncapsulation.ShadowDom – انگولار در این گزینه DOM سایه را برای کامپوننت مورد می‌سازد. همچنین دارای دامنه‌ای یکسان با بخش مرتبط با استایل کامپوننت است.

مفهوم پروجکشن محتوا

مفهوم پروجکشن محتوا در انگولار به درجه یک عنصر DOM سایه درون کامپوننت کمک می‌کند. به بیان ساده، اگر بخواهیم عناصر HTML یا کامپوننت دیگری را به صورت دینامیک درون یک کامپوننت درج کنیم، باید از مفهوم پروجکشن محتوا استفاده کنیم. «پروجکشن محتوا» (Content Projection) یکی از مهم‌ترین و مفیدترین قابلیت‌های فریمورک انگولار محسوب می‌شود. این قابلیت به ما کمک می‌کند که داده‌ها را از کامپوننت والد به کمک قالب HTML به کامپوننت‌های داخلی فرزند بفرستیم. ما در غالب موارد از یک بخش خاص کد در بخش‌های مختلف قالب‌های کامپوننت‌های فرزند استفاده کنیم. این کار با استفاده از پروچکشن محتوا امکان‌پذیر است.

ngContent

یکی از بهترین رویکردها برای پیاده‌سازی پروجکشن محتوا در اپلیکیشن‌های مبتنی بر ‌فریمورک انگولار استفاده از ngContent است. از ngContent می‌توان برای ارسال هر نوع محتوای HTML به کامپوننت‌های فرزند استفاده کرد. ngContent نه تنها قالب HTML ساده را ارسال می‌کند، بلکه «اتصال مشخصه» (property binding) و «فرستنده‌های رویداد» (event emitters) را نیز ارسال می‌کند.

ما می‌توانیم با استفاده از <ng-content></ng-content> درون یک قالب HTML کامپوننت فرزند یک placeholder تعبیه کنیم که انگولار در آنجا محتوا را رندر کند. قالب HTML که در تگ <ng-content> پروجکت می‌شود، همواره درون تگ کامپوننت فرزند تعریف خواهد شد.

مقایسه فرزندان نما با فرزندان محتوا

فرزندان نمافرزندان محتوا
عناصر فرزندی که درون قالب HTML کامپوننت قرار دارند، به طور معمول View Children نامیده می‌شوند.عناصری که بین تگ‌های آغازی و پایانی عنصر والد یک کامپوننت قرار می‌گیرند، به نام فرزندان محتوا شناخته می‌شوند.
در زمان افزودن عنصری که قرار است به صورت مستقیم در خود کامپوننت استفاده شود، باید از ViewChildren @ استفاده کنیم.از فرزندان محتوا می‌توان برای به دست آوردن یک ارجاع به محتوایی که با استفاده از <ng-content> درون کامپوننت پروجکت شده است، بهره گرفت.

مثالی از حالت ViewEncapsulation.None

در این بخش ابتدا یک کامپوننت فرزند ایجاد و از آن در کامپوننت ریشه به عنوان یک کامپوننت تودرتو استفاده می‌کنیم. در ابتدا هیچ گزینه‌ای در ارتباط با نمای کپسوله‌سازی ارائه نمی‌کنیم. بنابراین در آغاز تنها یک کامپوننت مبتنی بر رابطه والد-فرزند است. همچنین در کامپوننت ریشه از برخی استایل‌شیت‌های سفارشی مرتبط با فایل‌ها استفاده می‌کنیم که بعضی استایل‌های مبتنی بر CSS را اعمال می‌کنند.

  • فایل ViewEncapsulation.None
1import { Component } from '@angular/core';  
2  
3@Component({  
4  selector: 'app-root',  
5  templateUrl: './app.component.html',  
6  styleUrls : ['./custom.css']  
7})  
8export class AppComponent {  
9    private message: string = 'Welcome to Angular 8';  
10  
11    constructor() {  
12    }  
13}
  • فایل app.component.html
1<h2>Demostration of View Encapsulation</h2>  
2  
3<div>  
4    <h1>{{message}}</h1>  
5</div>  
6  
7<child></child>
  • فایل custom.css
1h1{  
2    color:red;  
3    font-weight:bold;  
4    font-size: 30px;   
5    text-align: center;  
6    text-transform: uppercase;  
7}  
8h2,h3{  
9    color:blue;  
10    font-size: 20px;  
11}
  • فایل child.component.ts
1import { Component } from '@angular/core';  
2  
3@Component({  
4    selector: 'child',  
5    templateUrl: 'child.component.html'  
6})  
7  
8export class ChildComponent  {  
9    public title:string ='This is a Child Component';  
10}
  • فایل child.component.html
1<div>  
2    <h1>{{title}}</h1>  
3</div>

اکنون می‌توانید نتیجه را در مرورگر بررسی کنید:

آموزش انگولار

بر اساس خروجی فوق، روشن است که از استایل‌ها در کامپوننت ریشه استفاده کرده‌ایم و استایل‌ها روی تگ <h1> اعمال شده‌اند. دلیل این که این استایل‌ها روی کامپوننت فرزند اعمال نشده‌اند، این است که از این استایل در کامپوننت فرزند استفاده نکرده‌ایم. اینک در کامپوننت ریشه، حالت کپسوله‌سازی نمای زیر را فعال می‌کنیم:

1import { Component } from '@angular/core';  
2import { ViewEncapsulation } from '@angular/core';  
3  
4@Component({  
5  selector: 'app-root',  
6  templateUrl: './app.component.html',  
7  styleUrls : ['./custom.css'],  
8  encapsulation:ViewEncapsulation.None  
9})  
10export class AppComponent {  
11    private message: string = 'Welcome to Angular 8';  
12  
13    constructor() {  
14    }  
15}

بدین ترتیب خروجی در مرورگر به صورت زیر درمی‌آید:

آموزش انگولار

چنان که در تصویر فوق می‌بینید، همان استایل‌ها روی کامپوننت فرزند نیز اعمال شده‌اند، زیرا از کپسوله‌سازی نما استفاده کرده‌ایم. اکنون اگر HTML را در مرورگر بررسی کنیم، می‌توانیم کد مرتبط با استایل را که در بخش هدر فایل HTML قرار گرفته است، شناسایی کنیم. به همین دلیل است که این استایل روی هر دو کامپوننت والد و فرزند اعمال شده است.

آموزش انگولار

مثالی از حالت ViewEncapsulation.ShadowDOM

در این بخش به بررسی شیوه استفاده از کپسوله‌سازی نما با گزینه‌های DOM سایه خواهیم پرداخت. به این منظور، تغییرهای زیر را در کامپوننت app-root اعمال می‌کنیم.

1import { Component } from '@angular/core';  
2import { ViewEncapsulation } from '@angular/core';  
3  
4@Component({  
5  selector: 'app-root',  
6  templateUrl: './app.component.html',  
7  styleUrls : ['./custom.css'],  
8  encapsulation:ViewEncapsulation.ShadowDom  
9})  
10export class AppComponent {  
11    private message: string = 'Welcome to Angular 8';  
12  
13    constructor() {  
14    }  
15}

اکنون مرورگر را بررسی می‌کنیم تا ببینیم آیا خروجی همانند قبل است یا نه. اما اگر عنصر HTML را در مرورگر بررسی کنیم، می‌بینیم که کد مرتبط با استایل دارای دامنه‌ای در چارچوب سلکتور کامپوننت است.

مثالی از ViewEncapsulation.Emulated

در این بخش به بررسی شیوه کپسوله‌سازی نما در انواع نوع شبیه‌سازی‌شده می‌پردازیم. به این منظور کافی است تغییرهای زیر را روی کامپوننت app-root اعمال کنید. چنان که پیش‌تر اشاره کردیم، این نوع از کپسوله‌سازی نما هیچ DOM سایه را درون مرورگر ایجاد نمی‌کند.

1import { Component } from '@angular/core';  
2import { ViewEncapsulation } from '@angular/core';  
3  
4@Component({  
5  selector: 'app-root',  
6  templateUrl: './app.component.html',  
7  styleUrls : ['./custom.css'],  
8  encapsulation:ViewEncapsulation.Emulated  
9})  
10export class AppComponent {  
11    private message: string = 'Welcome to Angular 8';  
12  
13    constructor() {  
14    }  
15}

خروجی این مثال در مرورگر به صورت زیر است:

آموزش انگولار

مثالی از ng-content

در این بخش به بررسی شیوه استفاده از ng-content در اپلیکیشن می‌پردازیم. به این منظور ابتدا یک کامپوننت به نام modal-window می‌سازیم که یک صفحه modal بازشونده است و این کامپوننت را در کامپوننت aoo-root خود پیاده‌سازی می‌کنیم. مفهوم پنجره modal از روی قالب HTML کامپوننت app-root تعریف می‌شود.

  • فایل modal.component.ts
1import { Component, OnInit, ViewChild, Input } from '@angular/core';  
2  
3@Component({  
4    selector: 'modal-window',  
5    templateUrl: 'modal.component.html'  
6})  
7  
8export class ModalComponent implements OnInit {  
9    @Input() private display: string = 'none';  
10    @Input('header-caption') private header: string = 'Modal';  
11  
12    constructor() {  
13    }  
14  
15    ngOnInit(): void {  
16    }  
17  
18    private fnClose(): void {  
19        this.display = 'none';  
20    }  
21  
22    showModal(): void {  
23        this.display = 'block';  
24    }  
25  
26    close(): void {  
27        this.fnClose();  
28    }  
29  
30    setModalTitle(args: string): void {  
31        this.header = args;  
32    }  
33}
  • فایل modal.component.html
1<div class="modal" id="myModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" [ngStyle]="{'display' : display }">  
2    <div class="modal-dialog">  
3        <div class="modal-content animated bounceInRight">  
4            <div class="modal-header">  
5                <button type="button" class="close" (click)="fnClose()">×</button>  
6                <h3 class="modal-title">{{header}}</h3>  
7            </div>  
8            <div class="modal-body">  
9                <ng-content select="content-body"></ng-content>  
10            </div>  
11            <div class="modal-footer">  
12                <ng-content select="content-footer"></ng-content>  
13            </div>  
14        </div>  
15    </div>  
16</div>
  • فایل app.module.ts
1import { BrowserModule } from '@angular/platform-browser';  
2import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';  
3import { FormsModule } from '@angular/forms';  
4  
5import { AppComponent } from './app.component';  
6import { ModalComponent } from './modal.component';  
7  
8@NgModule({  
9  declarations: [  
10    AppComponent,ModalComponent  
11  ],  
12  imports: [  
13    BrowserModule, FormsModule  
14  ],  
15  providers: [],  
16  bootstrap: [AppComponent],  
17  schemas: [NO_ERRORS_SCHEMA]  
18})  
19export class AppModule { }
  • فایل app.component.ts
1import { Component, OnInit, ViewChild } from '@angular/core';  
2import { ModalComponent } from './modal.component';  
3  
4@Component({  
5  selector: 'app-root',  
6  templateUrl: './app.component.html',  
7  styleUrls : ['./custom.css']  
8})  
9export class AppComponent implements OnInit {  
10    private caption: string = 'Custom Modal';  
11    @ViewChild('modal',{static:true}) private _ctrlModal: ModalComponent;  
12  
13    constructor() {  
14    }  
15  
16    ngOnInit(): void {  
17    }  
18  
19    private fnOpenModal(): void {  
20        this._ctrlModal.showModal();  
21    }  
22  
23    private fnHideModal(): void {  
24        this._ctrlModal.close();  
25    }  
26}
  • فایل app.component.html
1<div>  
2    <h2>Demonstrate Modal Window using ngContent</h2>  
3    <input type="button" value="Show Modal" class="btn-group" (click)="fnOpenModal()" />  
4    <br />  
5    <modal-window [header-caption]="caption" #modal>  
6        <content-body>  
7            <h1>Modal Contain Defined at Parent Component</h1>  
8            <p>  
9                Lorem ipsum dolor, sit amet consectetur adipisicing elit. Atque natus minima
10 suscipit magnam, quas provident aperiam? Quam maiores saepe placeat soluta, vel qui dolorem dolorum dignissimos veniam iusto facilis totam?  
11            </p>  
12        </content-body>  
13        <content-footer>  
14            <input type="button" class="btn-default active" class="btn btn-primary" value="Modal Close" (click)="fnHideModal();" />   
15        </content-footer>  
16    </modal-window>  
17</div>

برای نمایش پنجره modal از CSS bootstrap در فایل index.html به صورت زیر استفاده کرده‌ایم:

1<!doctype html>  
2<html lang="en">  
3<head>  
4  <meta charset="utf-8">    
5  <title>Angular8Demo</title>  
6  <base href="/">  
7  
8  <meta name="viewport" content="width=device-width, initial-scale=1">  
9  <link rel="icon" type="image/x-icon" href="favicon.ico">  
10  <link href="assets/bootstrap.min.css" rel="stylesheet" type="text/css"  
11</head>  
12<body>  
13  <app-root></app-root>