مسیریابی در +Angular 2 به همراه قابلیتهای فراوان و ویژهای است که تعدادی از آنها را تابحال در این سایت بررسی کردهایم. مورد مقدماتی اول، نیاز به بازنویسی کامل دارد، مورد دوم جهت آشنایی با ساختار Angular CLI مفید است و مورد سوم یکی از مباحث تکمیلی آن است. به همین جهت در طی یک سری، ویژگیهای متعدد سیستم مسیریابی +Angular 2 را از ابتدا بررسی خواهیم کرد.
مسیریابی در +Angular 2
عموما از مسیریابی جهت حرکت بین Viewهای مختلف برنامه استفاده میشود، اما کارهای بیشتری را نیز میتوان با آن انجام داد؛ مانند ارسال اطلاعات، به مسیریابیها، پیش بارگذاری اطلاعات، جهت نمایش در Viewها، گروه بندی و محافظت از مسیریابیها، پویانمایی و انیمیشن و همچنین بهبود کارآیی، با بارگذاری async مسیرهای مختلف.
کار سیستم مسیریاب +Angular 2 زمانی شروع میشود که تغییری را در آدرس درخواستی از برنامه مشاهده میکند؛ یا از طریق درخواست آدرسی توسط مرورگر و یا هدایت به قسمتی خاص، از طریق کدنویسی. سپس مسیریاب به آرایهی تنظیم شدهی مسیرهای سیستم مراجعه میکند تا بتواند تطابقی را بین آدرس درخواستی و یکی از کلیدهای تنظیم شدهی در آن پیدا کند. در این حالت اگر تطابقی یافت نشود، کارمسیریابی خاتمه خواهد یافت. در غیراینصورت کار ادامه یافته و سپس مسیریاب، محافظهای مسیر درخواستی را بررسی میکند تا مشخص شود که آیا کاربر مجاز به هدایت به این قسمت خاص از برنامه هست یا خیر؟ در صورت مثبت بودن پاسخ، مرحلهی بعد، پیش بارگذاری اطلاعات درخواستی جهت نمایش View مرتبط است. در ادامه کامپوننت متناظر با مسیریابی فعالسازی میشود. سپس قالب این کامپوننت را در قسمتی که توسط router-outlet مشخص میگردد، جایگذاری کرده و نمایش میدهد.
تعریف مسیر پایه یا Base path
اولین مرحلهی کار با سیستم مسیریابی +Angular 2، تعریف یک base path است. مسیرپایه، به زیرپوشهای اشاره میکند که برنامهی ما در آن قرار گرفتهاست:
در این مثال، مسیرپایه برنامه، /myapp/ است.
مسیریاب از این مسیرپایه جهت ساخت آدرسهای مسیریابی استفاده میکند. مقدار آن نیز به صورت ذیل در فایل index.html برنامه، درست پس از تگ head تعیین میگردد:
در اینجا با ذکر / ، به عنوان مسیرپایه، به ریشهی سایت اشاره شدهاست. اگر برنامهی شما قرار است در یک زیرپوشه قرار بگیرد، در این حالت نحوهی تعیین این زیر پوشه به صورت ذیل خواهد بود:
تعیین مسیرپایه جهت ارائهی نهایی
استفاده از مسیر پایه / برای حالت توسعه و همچنین زمانیکه برنامهی نهایی شما در ریشهی سایت توزیع میشود، بسیار مناسب است. اما اگر برای حالت توسعه از مقدار / و برای حالت توزیع از مقدار /myapp/ بخواهید استفاده کنید، مدام نیاز خواهید داشت تا فایل index.html نهایی سایت را ویرایش کنید. برای این منظور Angular CLI دارای پرچمی است به نام base-href:
به این ترتیب در زمان ساخت و توزیع نهایی برنامه توسط Angular CLI، کار تنظیم خودکار مسیرپایه نیز صورت خواهد گرفت.
حالت پیش فرض تولید برنامههای Angular توسط Angular CLI، تنظیم مسیرپایه در فایل src\index.html به صورت خودکار به / میباشد.
تعریف مسیریاب Angular
مسیریاب Angular در ماژولی به نام RouterModule قرار گرفتهاست و باید در ابتدای کار import شود. این ماژول شامل سرویسی است جهت هدایت کاربران به صفحات دیگر و مدیریت URLها، تنظیماتی برای تعریف جزئیات مسیریابیها و تعدادی دایرکتیو که برای فعالسازی و نمایش مسیرها از آنها استفاده میشود. برای مثال دایرکتیو RouterLink آن یک المان قابل کلیک HTML را به مسیر و کامپوننتی خاص در برنامه متصل میکند. RouterLinkActive، شیوهنامهها را به لینک فعال انتساب میدهد و RouterOutlet محل نمایش قالب کامپوننت فعال شده را مشخص میکند.
یک مثال: در ادامه، یک پروژهی جدید مبتنی بر Angular CLI را به نام angular-routing-lab به همراه تنظیمات ابتدایی مسیریابی آن ایجاد میکنیم:
به علاوه قصد استفادهی از بوت استرپ را نیز داریم. به همین جهت ابتدا به ریشهی پروژه وارد شده و سپس دستور ذیل را صادر کنید تا بوت استرپ نیز نصب شود و پرچم save آن سبب به روز رسانی فایل package.json نیز گردد:
پس از آن نیاز است به فایل angular-cli.json. مراجعه کرده و شیوهنامهی بوت استرپ را تعریف کنیم:
به این ترتیب به صورت خودکار این شیوه نامه به همراه توزیع برنامه حضور خواهد داشت و نیازی به تعریف مستقیم آن در فایل index.html نیست.
در ادامه اگر به فایل src\app\app-routing.module.ts مراجعه کنید، یک چنین محتوایی را خواهید یافت:
در اینجا برای شروع به کار با سیستم مسیریابی، RouterModule معرفی شدهاست. ابتدا import آنرا مشاهده میکنید و سپس این ماژول به قسمت imports مربوط به NgModule اضافه شدهاست.
میتوان این قسمت را خلاصه کرد و فایل app-routing.module.ts را نیز حذف کرد و سپس import لازم و تعریف ماژول آنرا به ماژول آغازین برنامه یا همان src\app\app.module.ts نیز منتقل کرد. اما پس از مدتی تنظیمات مسیریابی آن، فایل ماژول اصلی برنامه را بیش از اندازه شلوغ خواهند کرد. بنابراین Angular-CLI تصمیم به ایجاد یک ماژول مستقل را برای تعریف تنظیمات مسیریابی برنامه گرفتهاست. سپس تعریف آن را به فایل src\app\app.module.ts به صورت خودکار اضافه میکند:
اگر به قسمت import مربوط به NgModule فایل src\app\app-routing.module.ts دقت کنید، این ماژول به همراه متد forRoot معرفی شدهاست.
ماژول مسیریابی به همراه دو متد ذیل است:
الف) forRoot
- کار آن تعریف دایرکتیوهای مسیریابی، مدیریت تنظیمات مسیریابی و ثبت سرویس مسیریابی است.
- نکتهی مهم اینجا است که متد forRoot تنها یکبار باید در طول عمر یک برنامه تعریف شود.
- این متد آرایهای از تنظیمات مسیریابیهای تعریف شده را دریافت میکند.
ب) forChild
- کار آن تعریف دایرکتیوهای مسیریابی و مدیریت تنظیمات مسیریابی است؛ اما سرویس مسیریابی را مجددا ثبت نمیکند.
- از این متد در جهت تعریف مسیریابیهای ماژولهای ویژگیهای مختلف برنامه و نظم بخشیدن به آنها استفاده میشود.
بنابراین زمانیکه از forRoot استفاده میشود، سرویس مسیریابی تنها یکبار ثبت خواهد شد و تنها یک وهله از آن موجود خواهد بود. در ادامه هر کدام از ماژولهای دیگر برنامه میتوانند forChild خاص خودشان را داشته باشند.
اکنون تمام کامپوننتهای قید شدهی در قسمت declaration، امکان دسترسی به دایرکتیوهای مسیریابی را پیدا میکنند. همچنین از آنجائیکه AppRoutingModule به همراه متد forRoot است، سرویس مسیریابی نیز در کل برنامه قابل دسترسی است.
تنظیمات اولیه مسیریابی برنامه
آرایهی const routes: Routes فایل src\app\app-routing.module.ts در ابتدای کار خالی است. در اینجا کار تعریف URL segments و سپس اتصال آنها به کامپوننتهای متناظری جهت فعالسازی و نمایش قالب آنها صورت میگیرد. این نمایش نیز در محل router-outlet تعریف شدهی در فایل src\app\app.component.html انجام میشود:
به ازای هر router-outlet، تنها یک مسیر را در هر زمان میتوان نمایش داد. بنابراین با فعال شدن هر کامپوننت، قالب آن، قالب کامپوننت پیشین را در router-outlet جایگزین خواهد کرد. امکان تعریف router-outletهای دیگری نیز وجود دارند که به آنها secondary routes گفته میشود.
در ادامه برای تکمیل مثال جاری، دو کامپوننت جدید خوشآمد گویی و همچنین یافتن نشدن مسیرها را به برنامه اضافه میکنیم:
که سبب ایجاد کامپوننتهای src\app\welcome\welcome.component.ts و src\app\page-not-found\page-not-found.component.ts خواهند شد، به همراه به روز رسانی خودکار فایل src\app\app.module.ts جهت تکمیل قسمت declarations آن:
سپس فایل src\app\app-routing.module.ts را به نحو ذیل تکمیل نمائید:
در اینجا در ابتدا دو کامپوننت جدید تعریف شده، import شدهاند.
یک نکته: افزونهی auto import، کار تعریف کامپوننتها را در VSCode بسیار ساده میکند و امکان تشکیل خودکار قسمت import را با ارائهی یک intellisense به همراه دارد.
سپس کار تکمیل آرایهی Routes انجام شدهاست. همانطور که مشاهده میکنید، این آرایه متشکل است از اشیایی که به همراه خاصیت path و سایر پارامترهای مورد نیاز هستند.
کار خاصیت path، تعیین URL segment متناظری است که این مسیریابی را فعال میکند. برای مثال اولین شیء تعریف شده با آدرسهایی مانند www.mysite.com/welcome متناظر است.
در اغلب حالات به همراه path، خاصیت کامپوننت متناظر با آن مسیر نیز ذکر میشود؛ مانندWelcomeComponent ایی که در اینجا مقدار دهی شدهاست. هنگامیکه این مسیریابی فعال میشود، قالب این کامپوننتاست که در router-outlet نمایش داده خواهد شد (فایل src\app\welcome\welcome.component.html).
چند نکته:
- در حین تعریف مقدار خاصیت path، هیچ / آغاز کنندهای تعریف نشدهاست.
- مقدار خاصیت path، حساس به کوچکی و بزرگی حروف است.
- WelcomeComponent تعریف شده، یک رشته نیست و ارجاعی را به کامپوننت مرتبط دارد. به همین جهت نیاز به import statement ابتدایی را دارد و وجود آن توسط کامپایلر بررسی میشود.
تعیین مسیریابی پیش فرض سایت
اما زمانیکه برنامه برای بار اول بارگذاری میشود، چطور؟ در این حالت هیچ URL segment ایی وجود ندارد. بنابراین برای تنظیم مسیرپیش فرض سایت، خاصیت path، به یک رشتهی خالی همانند دومین شیء تنظیمات مسیریابی، تنظیم میشود:
در اینجا زمانیکه کاربر، ریشهی سایت را درخواست میکند، به کامپوننت welcome هدایت خواهد شد. در این تنظیم خاصیت pathMatch نحوهی تطابق با خاصیت path را مشخص میکند. مقدار full نیز به این معنا است که اگر تمام URL segment دریافتی خالی بود، از این تنظیم استفاده کن.
مدیریت مسیریابی آدرسهای ناموجود در سایت
تنظیم سومی را نیز در اینجا مشاهده میکنید:
** به این معنا است که اگر مسیریاب موفق به تطابق URL segment دریافتی، با هیچکدام از pathهای تعریف شده نبود، از این تنظیم استفاده کند (catch all). برای مثال اگر کاربر آدرس www.mysite.com/someAddr را درخواست کند، چون URL segment آن در آرایهی تنظیمات، تعریف نشدهاست، میخواهیم یک صفحهی یافت نشد را نمایش دهیم.
یک نکته: ترتیب مسیریابیها در آرایهی تعریف آنها اهمیت دارد. در اینجا از استراتژی «اولین تطابق یافته، برنده خواهد بود» استفاده میشود. بنابراین تنظیم ** باید در انتهای لیست ذکر شود؛ در غیراینصورت هیچکدام از مسیریابیهای تعریف شدهی پس از آن پردازش نخواهند شد.
مدیریت تغییرات آدرسهای برنامه
در طول عمر برنامه ممکن است نیاز به تغییر آدرسهای برنامه باشد. برای مثال بجای مسیر welcome مسیر home نمایش داده شود:
در این تنظیمات جدید برنامه، مسیر home به WelcomeComponent اشاره میکند. اما جهت هدایت آدرسهای قدیمی welcome، به این آدرس جدید home میتوان از redirectTo، مانند دومین شیء مسیریابی اضافه شدهی به آرایهی تنظیمات استفاده کرد. به این ترتیب هم تغییرات آرایهی Routes به حداقل میرسد و هم آدرسهای ذخیره شدهی توسط کاربران هنوز هم معتبر خواهند بود.
نکته: redirectToها قابلیت تعریف زنجیرهای را ندارند. به این معنا که اگر ریشهی سایت درخواست شود، ابتدا به مسیر welcome هدایت خواهیم شد. مسیر welcome هم یک redirectTo دیگر به مسیر home را دارد. اما در اینجا کار به این redirectTo دوم نخواهد رسید و این پردازش، زنجیرهای نیست. بنابراین مسیریابی پیشفرض را نیز باید ویرایش کرد و به home تغییر داد:
به این ترتیب دیگر درخواست زنجیرهوار redirectToها رخ نخواهد داد.
نکته: redirectToها میتوانند local و یا absolute باشند. تعریف محلی آنها مانند ذکر home و welcome در اینجا است و تنها سبب تغییر یک URL segment میشود. اما اگر در ابتدای مقادیر redirectToها یک / قرار دهیم، به معنای تعریف یک مسیر مطلق است و کل URL را جایگزین میکند.
تعیین محل نمایش قالبهای کامپوننتها
زمانیکه یک کامپوننت فعالسازی میشود، قالب آن در router-outlet نمایش داده خواهد شد. برای این منظور فایل src\app\app.component.html را گشوده و به نحو ذیل تغییر دهید:
در اینجا دایرکتیو router-outlet محلی است که قالب کامپوننت فعالسازی شده، نمایش داده میشود. برای مثال اگر کاربر بر روی لینک Home کلیک کند، کامپوننت متناظر با آن از طریق تنظیمات مسیریابی یافت شده و فعالسازی میشود و سپس قالب آن در محل router-outlet رندر خواهد شد.
یک نکته: چون کامپوننت welcome از طریق مسیریابی نمایش داده میشود و دیگر به صورت مستقیم با درج تگ selector آن در صفحه فعالسازی نخواهد شد، میتوان به تعریف کامپوننت آن مراجعه کرده و selector آنرا حذف کرد.
تا اینجا اگر دستور ng serve -o را صادر کنیم (کار build درون حافظهای جهت محیط توسعه و نمایش خودکار برنامه در مرورگر)، چنین خروجی در مرورگر نمایان خواهد شد:
اگر به آدرس تنظیم شدهی در مرورگر دقت کنید، http://localhost:4200/home آدرسی است که در ابتدای نمایش سایت نمایان خواهد شد. علت آن نیز به تنظیم مسیریابی پیش فرض سایت برمیگردد.
و اگر یک مسیر غیرموجود را درخواست دهیم، قالب کامپوننت PageNotFound ظاهر میشود:
هدایت کاربران به قسمتهای مختلف برنامه
کاربران را میتوان به روشهای مختلفی به قسمتهای گوناگون برنامه هدایت کرد؛ برای مثال با کلیک بر روی المانهای قابل کلیک HTML و سپس اتصال آنها به کامپوننتهای برنامه. استفادهی کاربر از bookmark مرورگر و یا ورود مستقیم و دستی آدرس قسمتی از برنامه و یا کلیک بر روی دکمههای forward و back مرورگر. تنها مورد اول است که نیاز به تنظیم دارد و سایر قسمتها به صورت خودکار مدیریت خواهند شد. نمونهی آنرا نیز با تعریف لینک Home پیشتر مشاهده کردید:
- در اینجا با استفاده از یک دایرکتیو ویژه به نام routerLink، المانهای قابل کلیک HTML را به خاصیتهای path تعریف شدهی در تنظیمات مسیریابی برنامه متصل میکنیم. routerLink یک attribute directive است؛ بنابراین میتوان آنرا به یک المان قابل کلیک anchor tag، مانند مثال فوق نسبت داد.
- زمانیکه کاربر بر روی این لینک کلیک میکند، اولین path متناظر با routerLink یافت شده و فعالسازی خواهد شد.
- علت تعریف مقدار routerLink به صورت [] این است که آرایهی پارامترهای لینک را مشخص میکند. بنابراین چون آرایهاست، نیاز به [] دارد. اولین پارامتر این آرایه مفهوم root URL segment را دارد. در اینجا حتما نیاز است URL segment را با یک / شروع کرد. به علاوه باید دقت داشت که خاصیت path تنظیمات مسیریابی، حساس به حروف کوچک و بزرگ است. بنابراین این مورد را باید در اینجا نیز مدنظر داشت.
- پارامترهای دیگر routerLink میتوانند مفهوم پارامترهای این segment و یا حتی segments دیگری باشند.
یک نکته: چون در مثال فوق، آرایهی تعریف شده تنها دارای یک عضو است، آنرا میتوان به صورت ذیل نیز خلاصه نویسی کرد (one-time binding):
تفاوت بین آدرسهای HTML 5 و Hash-based
زمانیکه مسیریاب Angular کار پردازش آدرسهای رسیده را انجام میدهد، اینکار در سمت کلاینت صورت میگیرد و تنها URL segment مدنظر را تغییر داده و این درخواست را به سمت سرور ارسال نمیکند. به همین جهت سبب reload صفحه نمیشود. دو روش در اینجا جهت مدیریت سمت کلاینت آدرسها قابل استفاده است:
الف) HTML 5 Style
- آدرسی مانند http://localhost:4200/home، یک آدرس به شیوهی HTML 5 است. در اینجا مسیریاب Angular با استفاده از HTML 5 history pushState سبب به روز رسانی History مرورگر شده و آدرسها را بدون ارسال درخواستی به سمت سرور، در همان سمت کلاینت تغییر میدهد.
- این روش حالت پیش فرض Angular است و نحوهی نمایش آن بسیار طبیعی به نظر میرسد.
- در اینجا URL rewriting سمت سرور نیز جهت هدایت آدرسها، به برنامهی Angular ضروری است. برای مثال زمانیکه کاربری آدرس http://localhost:4200/home را مستقیما در مرورگر وارد میکند، این درخواست ابتدا به سمت سرور ارسال خواهد شد و چون چنین صفحهای در سمت سرور وجود ندارد، پیغام خطای 404 را دریافت میکند. اینجا است که URL rewriting سمت سرور به فایل index.html برنامه، جهت مدیریت یک چنین حالتهایی ضروری است.
برای نمونه اگر از وب سرور IIS استفاده میکنید، تنظیم ذیل را به فایل web.config در قسمت system.webServer اضافه کنید (کار کرد آن هم وابستهاست به نصب و فعالسازی ماژول URL Rewrite بر روی IIS):
ب) Hash-based
- آدرسی مانند http://localhost:4200/#/home یک آدرس به شیوهی Hash-based بوده و مخصوص مرورگرهایی است بسیار قدیمی که از HTML 5 پشتیبانی نمیکنند. اینبار قطعات قرار گرفتهی پس از علامت # دارای نام URL fragments بوده و قابلیت پردازش در سمت کلاینت را دارا میباشند.
- اگر علاقمند به استفادهی از این روش هستید، نیاز است خاصیت useHash را به true تنظیم کنید:
- این روش نیازی به URL rewriting سمت سرور ندارد. از آنجائیکه سرور هرچیزی را که پس # باشد ندید خواهد گرفت و سعی در یافتن آن، در سمت سرور نخواهد کرد.
مسیریابی در +Angular 2
عموما از مسیریابی جهت حرکت بین Viewهای مختلف برنامه استفاده میشود، اما کارهای بیشتری را نیز میتوان با آن انجام داد؛ مانند ارسال اطلاعات، به مسیریابیها، پیش بارگذاری اطلاعات، جهت نمایش در Viewها، گروه بندی و محافظت از مسیریابیها، پویانمایی و انیمیشن و همچنین بهبود کارآیی، با بارگذاری async مسیرهای مختلف.
کار سیستم مسیریاب +Angular 2 زمانی شروع میشود که تغییری را در آدرس درخواستی از برنامه مشاهده میکند؛ یا از طریق درخواست آدرسی توسط مرورگر و یا هدایت به قسمتی خاص، از طریق کدنویسی. سپس مسیریاب به آرایهی تنظیم شدهی مسیرهای سیستم مراجعه میکند تا بتواند تطابقی را بین آدرس درخواستی و یکی از کلیدهای تنظیم شدهی در آن پیدا کند. در این حالت اگر تطابقی یافت نشود، کارمسیریابی خاتمه خواهد یافت. در غیراینصورت کار ادامه یافته و سپس مسیریاب، محافظهای مسیر درخواستی را بررسی میکند تا مشخص شود که آیا کاربر مجاز به هدایت به این قسمت خاص از برنامه هست یا خیر؟ در صورت مثبت بودن پاسخ، مرحلهی بعد، پیش بارگذاری اطلاعات درخواستی جهت نمایش View مرتبط است. در ادامه کامپوننت متناظر با مسیریابی فعالسازی میشود. سپس قالب این کامپوننت را در قسمتی که توسط router-outlet مشخص میگردد، جایگذاری کرده و نمایش میدهد.
تعریف مسیر پایه یا Base path
اولین مرحلهی کار با سیستم مسیریابی +Angular 2، تعریف یک base path است. مسیرپایه، به زیرپوشهای اشاره میکند که برنامهی ما در آن قرار گرفتهاست:
www.mysite.com/myapp
مسیریاب از این مسیرپایه جهت ساخت آدرسهای مسیریابی استفاده میکند. مقدار آن نیز به صورت ذیل در فایل index.html برنامه، درست پس از تگ head تعیین میگردد:
<!DOCTYPE html> <html> <head> <base href="/">
<base href="/myapp/">
تعیین مسیرپایه جهت ارائهی نهایی
استفاده از مسیر پایه / برای حالت توسعه و همچنین زمانیکه برنامهی نهایی شما در ریشهی سایت توزیع میشود، بسیار مناسب است. اما اگر برای حالت توسعه از مقدار / و برای حالت توزیع از مقدار /myapp/ بخواهید استفاده کنید، مدام نیاز خواهید داشت تا فایل index.html نهایی سایت را ویرایش کنید. برای این منظور Angular CLI دارای پرچمی است به نام base-href:
> ng build --base-href /myapp/
حالت پیش فرض تولید برنامههای Angular توسط Angular CLI، تنظیم مسیرپایه در فایل src\index.html به صورت خودکار به / میباشد.
تعریف مسیریاب Angular
مسیریاب Angular در ماژولی به نام RouterModule قرار گرفتهاست و باید در ابتدای کار import شود. این ماژول شامل سرویسی است جهت هدایت کاربران به صفحات دیگر و مدیریت URLها، تنظیماتی برای تعریف جزئیات مسیریابیها و تعدادی دایرکتیو که برای فعالسازی و نمایش مسیرها از آنها استفاده میشود. برای مثال دایرکتیو RouterLink آن یک المان قابل کلیک HTML را به مسیر و کامپوننتی خاص در برنامه متصل میکند. RouterLinkActive، شیوهنامهها را به لینک فعال انتساب میدهد و RouterOutlet محل نمایش قالب کامپوننت فعال شده را مشخص میکند.
یک مثال: در ادامه، یک پروژهی جدید مبتنی بر Angular CLI را به نام angular-routing-lab به همراه تنظیمات ابتدایی مسیریابی آن ایجاد میکنیم:
> ng new angular-routing-lab --routing
> npm install bootstrap --save
"apps": [ { "styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.css" ],
در ادامه اگر به فایل src\app\app-routing.module.ts مراجعه کنید، یک چنین محتوایی را خواهید یافت:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: '', children: [] } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
میتوان این قسمت را خلاصه کرد و فایل app-routing.module.ts را نیز حذف کرد و سپس import لازم و تعریف ماژول آنرا به ماژول آغازین برنامه یا همان src\app\app.module.ts نیز منتقل کرد. اما پس از مدتی تنظیمات مسیریابی آن، فایل ماژول اصلی برنامه را بیش از اندازه شلوغ خواهند کرد. بنابراین Angular-CLI تصمیم به ایجاد یک ماژول مستقل را برای تعریف تنظیمات مسیریابی برنامه گرفتهاست. سپس تعریف آن را به فایل src\app\app.module.ts به صورت خودکار اضافه میکند:
import { AppRoutingModule } from './app-routing.module'; @NgModule({ imports: [ AppRoutingModule ],
اگر به قسمت import مربوط به NgModule فایل src\app\app-routing.module.ts دقت کنید، این ماژول به همراه متد forRoot معرفی شدهاست.
@NgModule({ imports: [RouterModule.forRoot(routes)],
الف) forRoot
- کار آن تعریف دایرکتیوهای مسیریابی، مدیریت تنظیمات مسیریابی و ثبت سرویس مسیریابی است.
- نکتهی مهم اینجا است که متد forRoot تنها یکبار باید در طول عمر یک برنامه تعریف شود.
- این متد آرایهای از تنظیمات مسیریابیهای تعریف شده را دریافت میکند.
ب) forChild
- کار آن تعریف دایرکتیوهای مسیریابی و مدیریت تنظیمات مسیریابی است؛ اما سرویس مسیریابی را مجددا ثبت نمیکند.
- از این متد در جهت تعریف مسیریابیهای ماژولهای ویژگیهای مختلف برنامه و نظم بخشیدن به آنها استفاده میشود.
بنابراین زمانیکه از forRoot استفاده میشود، سرویس مسیریابی تنها یکبار ثبت خواهد شد و تنها یک وهله از آن موجود خواهد بود. در ادامه هر کدام از ماژولهای دیگر برنامه میتوانند forChild خاص خودشان را داشته باشند.
اکنون تمام کامپوننتهای قید شدهی در قسمت declaration، امکان دسترسی به دایرکتیوهای مسیریابی را پیدا میکنند. همچنین از آنجائیکه AppRoutingModule به همراه متد forRoot است، سرویس مسیریابی نیز در کل برنامه قابل دسترسی است.
تنظیمات اولیه مسیریابی برنامه
آرایهی const routes: Routes فایل src\app\app-routing.module.ts در ابتدای کار خالی است. در اینجا کار تعریف URL segments و سپس اتصال آنها به کامپوننتهای متناظری جهت فعالسازی و نمایش قالب آنها صورت میگیرد. این نمایش نیز در محل router-outlet تعریف شدهی در فایل src\app\app.component.html انجام میشود:
<h1> {{title}} </h1> <router-outlet></router-outlet>
در ادامه برای تکمیل مثال جاری، دو کامپوننت جدید خوشآمد گویی و همچنین یافتن نشدن مسیرها را به برنامه اضافه میکنیم:
>ng g c welcome >ng g c PageNotFound
@NgModule({ declarations: [ AppComponent, WelcomeComponent, PageNotFoundComponent ],
سپس فایل src\app\app-routing.module.ts را به نحو ذیل تکمیل نمائید:
import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { WelcomeComponent } from './welcome/welcome.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'welcome', component: WelcomeComponent }, { path: '', redirectTo: 'welcome', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
یک نکته: افزونهی auto import، کار تعریف کامپوننتها را در VSCode بسیار ساده میکند و امکان تشکیل خودکار قسمت import را با ارائهی یک intellisense به همراه دارد.
سپس کار تکمیل آرایهی Routes انجام شدهاست. همانطور که مشاهده میکنید، این آرایه متشکل است از اشیایی که به همراه خاصیت path و سایر پارامترهای مورد نیاز هستند.
کار خاصیت path، تعیین URL segment متناظری است که این مسیریابی را فعال میکند. برای مثال اولین شیء تعریف شده با آدرسهایی مانند www.mysite.com/welcome متناظر است.
{ path: 'welcome', component: WelcomeComponent },
چند نکته:
- در حین تعریف مقدار خاصیت path، هیچ / آغاز کنندهای تعریف نشدهاست.
- مقدار خاصیت path، حساس به کوچکی و بزرگی حروف است.
- WelcomeComponent تعریف شده، یک رشته نیست و ارجاعی را به کامپوننت مرتبط دارد. به همین جهت نیاز به import statement ابتدایی را دارد و وجود آن توسط کامپایلر بررسی میشود.
تعیین مسیریابی پیش فرض سایت
اما زمانیکه برنامه برای بار اول بارگذاری میشود، چطور؟ در این حالت هیچ URL segment ایی وجود ندارد. بنابراین برای تنظیم مسیرپیش فرض سایت، خاصیت path، به یک رشتهی خالی همانند دومین شیء تنظیمات مسیریابی، تنظیم میشود:
{ path: '', redirectTo: 'welcome', pathMatch: 'full' },
مدیریت مسیریابی آدرسهای ناموجود در سایت
تنظیم سومی را نیز در اینجا مشاهده میکنید:
{ path: '**', component: PageNotFoundComponent },
یک نکته: ترتیب مسیریابیها در آرایهی تعریف آنها اهمیت دارد. در اینجا از استراتژی «اولین تطابق یافته، برنده خواهد بود» استفاده میشود. بنابراین تنظیم ** باید در انتهای لیست ذکر شود؛ در غیراینصورت هیچکدام از مسیریابیهای تعریف شدهی پس از آن پردازش نخواهند شد.
مدیریت تغییرات آدرسهای برنامه
در طول عمر برنامه ممکن است نیاز به تغییر آدرسهای برنامه باشد. برای مثال بجای مسیر welcome مسیر home نمایش داده شود:
const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'welcome', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
نکته: redirectToها قابلیت تعریف زنجیرهای را ندارند. به این معنا که اگر ریشهی سایت درخواست شود، ابتدا به مسیر welcome هدایت خواهیم شد. مسیر welcome هم یک redirectTo دیگر به مسیر home را دارد. اما در اینجا کار به این redirectTo دوم نخواهد رسید و این پردازش، زنجیرهای نیست. بنابراین مسیریابی پیشفرض را نیز باید ویرایش کرد و به home تغییر داد:
const routes: Routes = [ { path: 'home', component: WelcomeComponent }, { path: 'welcome', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ];
نکته: redirectToها میتوانند local و یا absolute باشند. تعریف محلی آنها مانند ذکر home و welcome در اینجا است و تنها سبب تغییر یک URL segment میشود. اما اگر در ابتدای مقادیر redirectToها یک / قرار دهیم، به معنای تعریف یک مسیر مطلق است و کل URL را جایگزین میکند.
تعیین محل نمایش قالبهای کامپوننتها
زمانیکه یک کامپوننت فعالسازی میشود، قالب آن در router-outlet نمایش داده خواهد شد. برای این منظور فایل src\app\app.component.html را گشوده و به نحو ذیل تغییر دهید:
<nav class="navbar navbar-default"> <div class="container-fluid"> <a class="navbar-brand">{{title}}</a> <ul class="nav navbar-nav"> <li> <a [routerLink]="['/home']">Home</a> </li> </ul> </div> </nav> <div class="container"> <router-outlet></router-outlet> </div>
یک نکته: چون کامپوننت welcome از طریق مسیریابی نمایش داده میشود و دیگر به صورت مستقیم با درج تگ selector آن در صفحه فعالسازی نخواهد شد، میتوان به تعریف کامپوننت آن مراجعه کرده و selector آنرا حذف کرد.
@Component({ //selector: 'app-welcome', templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.css'] })
تا اینجا اگر دستور ng serve -o را صادر کنیم (کار build درون حافظهای جهت محیط توسعه و نمایش خودکار برنامه در مرورگر)، چنین خروجی در مرورگر نمایان خواهد شد:
اگر به آدرس تنظیم شدهی در مرورگر دقت کنید، http://localhost:4200/home آدرسی است که در ابتدای نمایش سایت نمایان خواهد شد. علت آن نیز به تنظیم مسیریابی پیش فرض سایت برمیگردد.
و اگر یک مسیر غیرموجود را درخواست دهیم، قالب کامپوننت PageNotFound ظاهر میشود:
هدایت کاربران به قسمتهای مختلف برنامه
کاربران را میتوان به روشهای مختلفی به قسمتهای گوناگون برنامه هدایت کرد؛ برای مثال با کلیک بر روی المانهای قابل کلیک HTML و سپس اتصال آنها به کامپوننتهای برنامه. استفادهی کاربر از bookmark مرورگر و یا ورود مستقیم و دستی آدرس قسمتی از برنامه و یا کلیک بر روی دکمههای forward و back مرورگر. تنها مورد اول است که نیاز به تنظیم دارد و سایر قسمتها به صورت خودکار مدیریت خواهند شد. نمونهی آنرا نیز با تعریف لینک Home پیشتر مشاهده کردید:
<a [routerLink]="['/home']">Home</a>
- زمانیکه کاربر بر روی این لینک کلیک میکند، اولین path متناظر با routerLink یافت شده و فعالسازی خواهد شد.
- علت تعریف مقدار routerLink به صورت [] این است که آرایهی پارامترهای لینک را مشخص میکند. بنابراین چون آرایهاست، نیاز به [] دارد. اولین پارامتر این آرایه مفهوم root URL segment را دارد. در اینجا حتما نیاز است URL segment را با یک / شروع کرد. به علاوه باید دقت داشت که خاصیت path تنظیمات مسیریابی، حساس به حروف کوچک و بزرگ است. بنابراین این مورد را باید در اینجا نیز مدنظر داشت.
- پارامترهای دیگر routerLink میتوانند مفهوم پارامترهای این segment و یا حتی segments دیگری باشند.
یک نکته: چون در مثال فوق، آرایهی تعریف شده تنها دارای یک عضو است، آنرا میتوان به صورت ذیل نیز خلاصه نویسی کرد (one-time binding):
<a routerLink="/home">Home</a>
تفاوت بین آدرسهای HTML 5 و Hash-based
زمانیکه مسیریاب Angular کار پردازش آدرسهای رسیده را انجام میدهد، اینکار در سمت کلاینت صورت میگیرد و تنها URL segment مدنظر را تغییر داده و این درخواست را به سمت سرور ارسال نمیکند. به همین جهت سبب reload صفحه نمیشود. دو روش در اینجا جهت مدیریت سمت کلاینت آدرسها قابل استفاده است:
الف) HTML 5 Style
- آدرسی مانند http://localhost:4200/home، یک آدرس به شیوهی HTML 5 است. در اینجا مسیریاب Angular با استفاده از HTML 5 history pushState سبب به روز رسانی History مرورگر شده و آدرسها را بدون ارسال درخواستی به سمت سرور، در همان سمت کلاینت تغییر میدهد.
- این روش حالت پیش فرض Angular است و نحوهی نمایش آن بسیار طبیعی به نظر میرسد.
- در اینجا URL rewriting سمت سرور نیز جهت هدایت آدرسها، به برنامهی Angular ضروری است. برای مثال زمانیکه کاربری آدرس http://localhost:4200/home را مستقیما در مرورگر وارد میکند، این درخواست ابتدا به سمت سرور ارسال خواهد شد و چون چنین صفحهای در سمت سرور وجود ندارد، پیغام خطای 404 را دریافت میکند. اینجا است که URL rewriting سمت سرور به فایل index.html برنامه، جهت مدیریت یک چنین حالتهایی ضروری است.
برای نمونه اگر از وب سرور IIS استفاده میکنید، تنظیم ذیل را به فایل web.config در قسمت system.webServer اضافه کنید (کار کرد آن هم وابستهاست به نصب و فعالسازی ماژول URL Rewrite بر روی IIS):
<rewrite> <rules> <rule name="Angular 2+ pushState routing" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" pattern=".*\.[\d\w]+$" negate="true" /> <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" /> </conditions> <action type="Rewrite" url="/index.html" /> </rule> </rules> </rewrite>
ب) Hash-based
- آدرسی مانند http://localhost:4200/#/home یک آدرس به شیوهی Hash-based بوده و مخصوص مرورگرهایی است بسیار قدیمی که از HTML 5 پشتیبانی نمیکنند. اینبار قطعات قرار گرفتهی پس از علامت # دارای نام URL fragments بوده و قابلیت پردازش در سمت کلاینت را دارا میباشند.
- اگر علاقمند به استفادهی از این روش هستید، نیاز است خاصیت useHash را به true تنظیم کنید:
@NgModule({ imports: [RouterModule.forRoot(routes, { useHash: true })],