اشتراکها
نظرات مطالب
ASP.NET MVC #14
از Html.RenderAction در layout استفاده کنید.
نظرات مطالب
Url Routing در ASP.Net WebForms
نکته ای که الان یادم اومد اینه که در برنامه من، کاربران به صفحات اصلی سایت بصورت routing دسترسی دارند و در قسمت داشبورد به صورت ادرس مستقیم.
چطور میشه در webform این دو حالت رو در یک سایت از هم تفکیک کرد؟
متشکرم
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- محاسبه کاربران آنلاین در ASP.NET
- نحوه جابجایی tempdb در SQL Server 2005 و ضرورت آن
- Firefox فارسی در دست تهیه
- تست نرمافزار در Agile Software Development
- محافظت از فرمهای اینترنتی با ریکپچا
- تبادل NTFS و رجیستری
- غیر فعال کردن اشتراک درایوهای مخفی در ویندوز
- تحت نظر قرار دادن پوشههای سیستمی در سی شارپ
- نحوه استفاده از jQuery در ASP.Net
- پلاگین کیبورد مجازی فارسی برای jQuery
- بکارگیری ویژگیهای CSS3 در طراحی وب
- ۹ قابلیت پنهان گوگل که باید بدانید
- فیدبرنر و گوگل
- پشتیبانی چیست و چگونه باید انجام شود
- دانشآموزان ایرانی در IT از معلمان خود باسوادترند
- امنیت وبلاگ وردپرس خود را بالا ببرید
- سرعت تبدیل اعداد انگلیسی به فارسی
- آیا پنگوئن میتواند پنجره را بشکند؟ و نصیحت لینوکسی
- دل پر خون کاربران از عملکرد وزارت ICT در حوزه اینترنت
- انتخاب افقی قسمتی از کد!
Visual Studio
ASP. Net
- از سیر تا پیاز نحوهی کارکرد سشن در ASP.Net
- آشنایی با برنامه Fiddler
- jQuery, JSON, and ASP.NET
- ASP کلاسیک مرده است!
- دستکاری ظاهر شیرپوینت با استفاده از jQuery
- بررسی سایت whitehouse.gov
طراحی و توسعه وب
- ایجاد گوشههای گرد
- استفاده از فایرفاکس برای غلط یابی یک صفحه وب
- کتابخانه DateJs (و اگر وقت کردید سری به این پست بزنید)
- 45 تکنیک JQuery
- نحوه نمایش لینکهای خود را بهبود بخشید
- Formy CSS Form Framework
- Top list of ajax upload scripts
اسکیوال سرور
- به روز رسانی سوم اس کیوال سرور 2008 ، لیست موارد فیکس شده، دریافت
- طراحی جدید سایت اس کیوال سرور 2008
- آشنایی با لاگ فایلها در SQL Server
- تولید اعداد منحصربفرد در اس کیوال سرور
- از sp_detach_db استفاده نکنید!
سی شارپ
عمومی دات نت
- UTF8Encoding.Default != Encoding.UTF8
- مقایسه Nhibernate و Linq
- مقایسه Entity Framework و LINQ to SQL
- بررسی اندازه یک رشته در دات نت
- مونو و Android
- Google Maps for Windows Forms
- برگههای تقلب دات نت
- دایرکتوری کتاب خانههای ثبت وقایع در دات نت
ویندوز
مسایل اجتماعی و انسانی برنامه نویسی
- اثر رکود اقتصادی بر بازار کار برنامه نویسی
- دلایل استعفای تعدادی از کارمندان گوگل
- تهیهی برنامههایی با مقیاس گسترده در چند روز بجای چند هفته
- از دست همکاران خود خسته شدهاید؟!
- هنر مصاحبه کردن
متفرقه
یک برنامهی Angular، از گروهی از کامپوننتها تشکیل میشود؛ برای مثال یک کامپوننت App وجود دارد که آن نیز از تعدادی کامپوننت مختلف تشکیل میشود. ماژولها کار سازماندهی و بسته بندی این کامپوننتها را انجام میدهند و با بزرگتر شدن برنامه میتوان قسمتهای مختلف را در ماژولهای متفاوتی قرار داد. مزایای این روش به شرح زیر هستند:
- بهبود کپسوله سازی قسمتهای مختلف برنامه با بسته بندی آنها در ماژولهای متفاوت
- فراهم آوردن امکان lazy loading و بهبود کارآیی برنامه
انواع ماژولهای توصیه شدهی در برنامههای Angular
منهای App Module پیشفرض یک برنامههای Angular، ایجاد سه نوع ماژول دیگر نیز در جهت سازماندهی اینگونه برنامهها توصیه میشوند:
- Core Module
هدف از آن فراهم آوردن سرویسهای Singleton اشتراکی بین کامپوننتها و ماژولهای مختلف برنامه است. علت اینجا است که سیستم تزریق وابستگیهای Angular، به ازای هر ماژولی که Lazy loaded باشد، سرویس تزریقی در آنها را مجددا وهله سازی میکند. به همین جهت نیاز است تک ماژول اختصاصی را برای مدیریت سرویسهایی که نیازی است تنها یکبار در طول عمر برنامه وهله سازی شوند، تدارک ببینیم و Core Module مکان مناسبی برای اینکار است.
همچنین Code Module باید شامل کامپوننتهایی در سطح برنامه باشد. دراینجا منظور از «در سطح برنامه»، کامپوننتهایی که قرار است در بین تمام ماژولها به اشتراک گذاشته شوند، نیست. منظور تنها کامپوننتهایی هستند که در App Component اصلی برنامه قرار است استفاده شوند؛ مانند منوی راهبری بالای سایت.
- Shared Module
هدف از آن مدیریت و بسته بندی کامپوننتها، دایرکتیوها و Pipes اشتراکی بین تمام اجزای برنامه است. برای مثال کامپوننت «لطفا منتظر بمانید ...» اگر قرار است در تمام قسمتهای برنامه استفاده شود، نیاز است در Shared Module تعریف شود. از این جهت که در یک برنامهی Angular نمیتوان یک کامپوننت را بین دو ماژول مختلف به اشتراک گذاشت. به همین جهت نیاز است یک مکان مرکزی برای تعریف این کامپوننتهای اشتراکی ایجاد شود و سپس این تک ماژول را در قسمتهای مختلف برنامه، بدون مشکل مورد استفاده قرار داد.
- Feature Module
این ماژولها به ازای هر ویژگی برنامه ایجاد شده و کامپوننتها، سرویسها، دایرکتیوها و Pipes اختصاصی آن ویژگی را بسته بندی میکنند.
ایجاد Core Module
فرض کنید میخواهید اطلاعات کاربر جاری لاگین شده را در طول عمر برنامه نگهداری کنید و از آن در تمام قسمتهای برنامه استفاده نمائید. یک چنین سرویسی نیاز است دارای طول عمر Singleton باشد و تنها یکبار وهله سازی شود تا اطلاعات کاربر جاری از دست نرود. به همین جهت بهترین مکان تعریف این سرویس، در Core Module است.
برای این منظور در ساختار برنامهی خود، پوشهی جدید src\app\core را ایجاد میکنیم. سپس فایل core.module.ts را به صورت ذیل در آن تعریف خواهیم کرد:
- CoreModule در ابتدا تنها CommonModule و RouterModule را در صورت نیاز import میکند.
- سپس سرویسهای اشتراکی و Singleton برنامه در قسمت providers آن قرار میگیرند.
- در اینجا همچنین دو کامپوننت منو که توسط app.component.ts مورد استفاده قرار میگیرند نیز import شدهاند.
- فایلهای account-menu.component.ts، nav-bar.component.ts و user-repository.service.ts نیز به درون پوشهی src\app\core منتقل خواهند شد (به همراه تمام فایلهای html و css متناظر با آنها).
- اگر دقت کنید، قسمت exports این ماژول نیز مقدار دهی شدهاست. چون این کامپوننتها قرار است خارج از این ماژول و در AppModule استفاده شوند، نیاز است آنها را به صورت خروجی نیز معرفی کنیم.
اکنون جهت استفادهی از این قابلیتها، تنها کافی است تعریف CoreModule را به AppModule در فایل app.module.ts اضافه کنیم:
ایجاد Shared Modules
در Shared Module اجزایی را قرار خواهیم داد که قرار است در بیش از یک ماژول مورد استفاده قرار گیرند. به همین جهت در ساختار برنامهی خود، پوشهی جدید src\app\shared را ایجاد میکنیم. سپس در آن، ماژول جدید shared.module.ts را ایجاد خواهیم کرد:
ساختار این ماژول نیز شبیه به Core Module است. ابتدای CommonModule به آن import شدهاست. سپس کامپوننتهایی که قرار است در بین سایر ماژولهای سایت به اشتراک گذاشته شوند (برای مثال یک کامپوننت Loading Spinner فرضی)، در هر دو قسمت declarations و exports این ماژول اشتراکی قرار میگیرند. همچنین فایل loading-spinner.component.ts و تمام اجزای وابستهی به آن نیز به پوشهی src\app\shared منتقل میشوند.
از این جهت که اجزای خروجی این ماژول قرار است در Feature Moduleها استفاده شوند، CommonModule مورد استفادهی در آنها نیز در قسمت exports ذکر شدهاست.
اکنون جهت استفادهی از این قابلیتها، تنها کافی است تعریف SharedModule را به AppModule در فایل app.module.ts اضافه کنیم:
ایجاد Feature Modules
این مورد نکتهی ویژهای را به همراه ندارد و همانند ایجاد سایر ماژولهای برنامهاست. برای مثال ویژگی مدیریت کاربران، به همراه تمام اجزای آن درون ماژول کاربران قرار میگیرد و به همین ترتیب برای سایر ویژگیهای دیگر برنامه. ایجاد و مدیریت اینگونه ماژولها توسط Angular CLI بسیار سادهاست:
دستور اول ایجاد ماژول جدید users، پوشهی مرتبط با آن و همچنین به روز رسانی فایل app.module را به صورت خودکار انجام میدهد.
دستور دوم نیز کامپوننتی را به این ماژول اضافه میکند؛ به همراه به روز رسانی تعاریف این ماژول.
فقط در اینجا SharedModule ایی را که پیشتر اضافه کردیم، به قسمت imports آن اضافه میکنیم:
- بهبود کپسوله سازی قسمتهای مختلف برنامه با بسته بندی آنها در ماژولهای متفاوت
- فراهم آوردن امکان lazy loading و بهبود کارآیی برنامه
انواع ماژولهای توصیه شدهی در برنامههای Angular
منهای App Module پیشفرض یک برنامههای Angular، ایجاد سه نوع ماژول دیگر نیز در جهت سازماندهی اینگونه برنامهها توصیه میشوند:
- Core Module
هدف از آن فراهم آوردن سرویسهای Singleton اشتراکی بین کامپوننتها و ماژولهای مختلف برنامه است. علت اینجا است که سیستم تزریق وابستگیهای Angular، به ازای هر ماژولی که Lazy loaded باشد، سرویس تزریقی در آنها را مجددا وهله سازی میکند. به همین جهت نیاز است تک ماژول اختصاصی را برای مدیریت سرویسهایی که نیازی است تنها یکبار در طول عمر برنامه وهله سازی شوند، تدارک ببینیم و Core Module مکان مناسبی برای اینکار است.
همچنین Code Module باید شامل کامپوننتهایی در سطح برنامه باشد. دراینجا منظور از «در سطح برنامه»، کامپوننتهایی که قرار است در بین تمام ماژولها به اشتراک گذاشته شوند، نیست. منظور تنها کامپوننتهایی هستند که در App Component اصلی برنامه قرار است استفاده شوند؛ مانند منوی راهبری بالای سایت.
- Shared Module
هدف از آن مدیریت و بسته بندی کامپوننتها، دایرکتیوها و Pipes اشتراکی بین تمام اجزای برنامه است. برای مثال کامپوننت «لطفا منتظر بمانید ...» اگر قرار است در تمام قسمتهای برنامه استفاده شود، نیاز است در Shared Module تعریف شود. از این جهت که در یک برنامهی Angular نمیتوان یک کامپوننت را بین دو ماژول مختلف به اشتراک گذاشت. به همین جهت نیاز است یک مکان مرکزی برای تعریف این کامپوننتهای اشتراکی ایجاد شود و سپس این تک ماژول را در قسمتهای مختلف برنامه، بدون مشکل مورد استفاده قرار داد.
- Feature Module
این ماژولها به ازای هر ویژگی برنامه ایجاد شده و کامپوننتها، سرویسها، دایرکتیوها و Pipes اختصاصی آن ویژگی را بسته بندی میکنند.
ایجاد Core Module
فرض کنید میخواهید اطلاعات کاربر جاری لاگین شده را در طول عمر برنامه نگهداری کنید و از آن در تمام قسمتهای برنامه استفاده نمائید. یک چنین سرویسی نیاز است دارای طول عمر Singleton باشد و تنها یکبار وهله سازی شود تا اطلاعات کاربر جاری از دست نرود. به همین جهت بهترین مکان تعریف این سرویس، در Core Module است.
برای این منظور در ساختار برنامهی خود، پوشهی جدید src\app\core را ایجاد میکنیم. سپس فایل core.module.ts را به صورت ذیل در آن تعریف خواهیم کرد:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { UserRepositoryService } from './user-repository.service'; import { NavBarComponent } from './nav-bar.component'; import { AccountMenuComponent } from './account-menu.component'; @NgModule({ imports: [ CommonModule, RouterModule ], exports: [ NavBarComponent, AccountMenuComponent ], declarations: [ NavBarComponent, AccountMenuComponent ], providers: [ UserRepositoryService ] }) export class CoreModule { };
- سپس سرویسهای اشتراکی و Singleton برنامه در قسمت providers آن قرار میگیرند.
- در اینجا همچنین دو کامپوننت منو که توسط app.component.ts مورد استفاده قرار میگیرند نیز import شدهاند.
- فایلهای account-menu.component.ts، nav-bar.component.ts و user-repository.service.ts نیز به درون پوشهی src\app\core منتقل خواهند شد (به همراه تمام فایلهای html و css متناظر با آنها).
- اگر دقت کنید، قسمت exports این ماژول نیز مقدار دهی شدهاست. چون این کامپوننتها قرار است خارج از این ماژول و در AppModule استفاده شوند، نیاز است آنها را به صورت خروجی نیز معرفی کنیم.
اکنون جهت استفادهی از این قابلیتها، تنها کافی است تعریف CoreModule را به AppModule در فایل app.module.ts اضافه کنیم:
import { CoreModule } from "./core/core.module"; @NgModule({ imports: [ //... CoreModule, //... RouterModule.forRoot(appRoutes) ], //... }) export class AppModule { }
ایجاد Shared Modules
در Shared Module اجزایی را قرار خواهیم داد که قرار است در بیش از یک ماژول مورد استفاده قرار گیرند. به همین جهت در ساختار برنامهی خود، پوشهی جدید src\app\shared را ایجاد میکنیم. سپس در آن، ماژول جدید shared.module.ts را ایجاد خواهیم کرد:
import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { LoadingSpinnerComponent } from './loading-spinner.component'; @NgModule({ imports: [ CommonModule ], declarations: [ LoadingSpinnerComponent ], exports: [ LoadingSpinnerComponent, CommonModule ], providers: [ ] }) export class SharedModule { };
از این جهت که اجزای خروجی این ماژول قرار است در Feature Moduleها استفاده شوند، CommonModule مورد استفادهی در آنها نیز در قسمت exports ذکر شدهاست.
اکنون جهت استفادهی از این قابلیتها، تنها کافی است تعریف SharedModule را به AppModule در فایل app.module.ts اضافه کنیم:
import { CoreModule } from "./core/core.module"; import { SharedModule } from "./shared/shared.module"; @NgModule({ imports: [ //... CoreModule, SharedModule, //... RouterModule.forRoot(appRoutes) ], //... }) export class AppModule { }
ایجاد Feature Modules
این مورد نکتهی ویژهای را به همراه ندارد و همانند ایجاد سایر ماژولهای برنامهاست. برای مثال ویژگی مدیریت کاربران، به همراه تمام اجزای آن درون ماژول کاربران قرار میگیرد و به همین ترتیب برای سایر ویژگیهای دیگر برنامه. ایجاد و مدیریت اینگونه ماژولها توسط Angular CLI بسیار سادهاست:
> ng g m users -m app.module --routing > ng g c users/users-list
دستور دوم نیز کامپوننتی را به این ماژول اضافه میکند؛ به همراه به روز رسانی تعاریف این ماژول.
فقط در اینجا SharedModule ایی را که پیشتر اضافه کردیم، به قسمت imports آن اضافه میکنیم:
import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SharedModule } from '../shared/shared.module'; import { UsersListComponent } from './users-list.component'; @NgModule({ imports: [ RouterModule, SharedModule ], declarations: [ UsersListComponent ], exports: [ ], providers: [ ] }) export class UsersModule { };
اشتراکها
ویژگی ها و مزایای AngularJs 2
شاید برای شما هم پیش آمده باشد که در یک برنامهی Angular بخواهید layoutهای مختلفی داشته باشید؛ مثلا هنگام لاگین، طبق عرف کار باید هدر و فوتر صفحه از بین بروند و فقط فرم لاگین نمایش داده شود و یا بخواهید هنگام لاگین، یک layout مخصوص پنل مدیریتی داشته باشید و یا …
قبل از شروع، فرض را بر آن میگیریم که حداقل نیازهای یک پروژهی Angular را آماده کرده اید. سپس یک پوشهی جدید را به نام layout میسازیم و layoutهای مربوطه را در آن ایجاد میکنیم. با دستور زیر یک کامپوننت جدید را که layout ما خواهد شد، با نام دلخواهی ایجاد میکنیم:
و همچنین یک کامپوننت دیگر را برای صفحهی اصلی به نام homelayout میسازیم:
در ادامه Loginlayout را باز کرده و تنظیمات زیر را لحاظ کنید:
در اینجا یک هدر و یک فوتر را ساخته و <router-outlet></router-outlet> را در آن قرار میدهیم که قسمت پویای ما خواهد شد.
اکنون وارد کامپوننت home layout شوید و دقیقا مانند قبل، تنظیمات دلخواه خود را انجام داده و همچنین <router-outlet></router-outlet> راهم درون جائیکه میخواهید به صورت پویا باشد بگذارید.
تا اینجا ما فقط layoutها را طراحی کردیم. در ادامه در ریشهی پروژه، سه کامپوننت را به نامهای Home , Login, About میسازیم. Home و About دارای یک قالب و Login هم داری قالب مخصوص به خود میباشد.
سپس وارد کامپوننت آغازین برنامه (app.component.html) شوید و در آن <router-outlet></router-outlet> را وارد کنید. در اینجا دیگر نیازی به نوشتن تگهای خاص دیگری نیست.
در ادامه به اصلیترین قسمت، یعنی مسیریابی میرسیم. وارد app.module.ts شوید و آن را به صورت زیر تنظیم کنید:
همانطورکه ملاحظه میکنید، مسیریابی بالا شامل مسیریابیهای تو در تویی است. در اینجا کامپوننتهای Home و About درون HomelayoutComponent بارگذاری میشوند و خود HomelayoutComponent نیز درون app.component.
همچنین برای اینکه مشخص شود کدام کامپوننت به عنوان کامپوننت پیشفرض نمایش داده شود، به صورت زیر عمل میکنیم:
به این روش میتوانید هر تعداد layout ایی را که میخواهید، ایجاد کنید.
کدهای کامل این مطلب را میتوانید از اینجا دریافت و یا به صورت آنی آزمایش کنید.
قبل از شروع، فرض را بر آن میگیریم که حداقل نیازهای یک پروژهی Angular را آماده کرده اید. سپس یک پوشهی جدید را به نام layout میسازیم و layoutهای مربوطه را در آن ایجاد میکنیم. با دستور زیر یک کامپوننت جدید را که layout ما خواهد شد، با نام دلخواهی ایجاد میکنیم:
ng g c Loginlayout
ng g c homelayout
در ادامه Loginlayout را باز کرده و تنظیمات زیر را لحاظ کنید:
<div style="width: 100%;height: 250px;background-color: aquamarine"> <h1>Header</h1> </div> <router-outlet></router-outlet> <div style="width: 100%;height: 250px;background-color: brown"> <h1>Foother</h1> </div>
اکنون وارد کامپوننت home layout شوید و دقیقا مانند قبل، تنظیمات دلخواه خود را انجام داده و همچنین <router-outlet></router-outlet> راهم درون جائیکه میخواهید به صورت پویا باشد بگذارید.
تا اینجا ما فقط layoutها را طراحی کردیم. در ادامه در ریشهی پروژه، سه کامپوننت را به نامهای Home , Login, About میسازیم. Home و About دارای یک قالب و Login هم داری قالب مخصوص به خود میباشد.
سپس وارد کامپوننت آغازین برنامه (app.component.html) شوید و در آن <router-outlet></router-outlet> را وارد کنید. در اینجا دیگر نیازی به نوشتن تگهای خاص دیگری نیست.
در ادامه به اصلیترین قسمت، یعنی مسیریابی میرسیم. وارد app.module.ts شوید و آن را به صورت زیر تنظیم کنید:
export const routes: Routes = [ { path: 'Loginlayout', component: LoginlayoutComponent , children: [ { path: 'Login', component: LoginComponent, pathMatch: 'full'} ] }, { path: 'Homelayout', component: HomelayoutComponent, children: [ { path: 'About', component: AbouComponent, pathMatch: 'full'}, { path: 'Home', component: HomeComponent, pathMatch: 'full'} ] } ];
همچنین برای اینکه مشخص شود کدام کامپوننت به عنوان کامپوننت پیشفرض نمایش داده شود، به صورت زیر عمل میکنیم:
path: '', component: HomelayoutComponent, children: [ { path: '', component:HomeComponent, pathMatch: 'full'} ]
کدهای کامل این مطلب را میتوانید از اینجا دریافت و یا به صورت آنی آزمایش کنید.
تاکنون دو مطلب مشابه «ساخت DropDownListهای مرتبط به کمک jQuery Ajax در MVC» و «ایجاد Drop Down Listهای آبشاری توسط Kendo UI» را در مورد ساخت Cascading Drop-down Lists در این سایت مطالعه کردهاید. در اینجا قصد داریم چنین قابلیتی را توسط Angular پیاده سازی کنیم (بدون استفاده از هیچ کتابخانهی ثالث دیگری).
مدلهای سمت سرور برنامه
در این مطلب قصد داریم لیست گروهها را به همراه محصولات مرتبط با آنها، توسط دو drop down list نمایش دهیم:
از ویژگی JsonIgnore جهت عدم درج لیست محصولات، در خروجی JSON نهایی تولیدی گروهها، استفاده شدهاست (و کتابخانهی JSON.NET، کتابخانهی پیش فرض کار با JSON در ASP.NET Core است).
منبع داده JSON سمت سرور
پس از مشخص شدن مدلهای برنامه، اکنون توسط دو اکشن متد، لیست گروهها و همچنین لیست محصولات یک گروه خاص را با فرمت JSON بازگشت میدهیم:
- بار اولی که صفحه بارگذاری میشود، توسط یک درخواست Ajax ایی، لیست گروهها دریافت خواهد شد. سپس با انتخاب یک گروه، اکشن متد GetProducts جهت بازگرداندن لیست محصولات آن گروه، فراخوانی میگردد. کدهای کامل CategoriesDataSource در فایل پیوستی انتهای بحث قرار داده شدهاست و یک منبع ساده درون حافظهای است.
- در اینجا از یک Delay نیز استفاده شدهاست تا بتوان آیکنهای چرخندهی Loading سمت کاربر را در حین کار با عملیاتی زمانبر، بهتر مشاهده کرد.
کدهای سمت کاربر برنامه
کدهای سمت کاربر این مثال در ادامهی همان مطلب «فرمهای مبتنی بر قالبها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» هستند که بر روی آن این دستورات فراخوانی شدهاست:
ماژول جدیدی به نام محصولات اضافه و به app.module معرفی شدهاست. البته پس از اصلاح، ProductModule بجای ProductRoutingModule در این فایل تنظیم خواهد شد.
سپس یک کامپوننت جدید به نام ProductGroupComponent درون ماژول Product ایجاد شدهاست.
در ادامه سه کلاس Product، Category و ProductGroupForm به این ماژول اضافه شدهاند که دو مورد اول، معادل کلاسهای مدل سمت سرور و مورد سوم، معادل فرم جدید ProductGroupComponent است:
سپس سرویسی را جهت دریافت اطلاعات دراپ داونها از سرور تهیه کردهایم:
با این محتوا:
از متد getCategories برای پر کردن اولین drop down استفاده خواهد شد و از متد دوم برای دریافت لیست محصولات متناظر با یک گروه انتخاب شده کمک میگیریم.
پس از این مقدمات اکنون میتوان کدهای ProductGroupComponent را تکمیل کرد.
ابتدا در متد ngOnInit آن کار دریافت لیست آغازین گروههای محصولات را انجام میدهیم:
برای این منظور ابتدا ProductItemsService به سازندهی کلاس تزریق شدهاست تا بتوان به متدهای دریافت اطلاعات از سرور دسترسی یافت. سپس در متد ngOnInit، اطلاعات دریافتی به خاصیت عمومی categories انتساب داده شدهاست.
اکنون چون این خاصیت در دسترس است، میتوان به قالب این کامپوننت مراجعه کرده و قسمت ابتدایی فرم را تکمیل کرد:
- در اینجا اولین ngIf بکار گرفته شده، طول آرایهی categories (همان خاصیت عمومی معرفی شدهی در کامپوننت) را بررسی میکند. اگر این آرایه خالی باشد، یک آیکن چرخنده را نمایش میدهد.
- سپس ngModel به خاصیت categoryId وهلهای از کلاس ProductGroupForm که مدل معادل فرم است، متصل شدهاست.
- همچنین با اتصال به رخداد change، مقدار Id عضو انتخابی به متد fetchProducts ارسال میشود. دسترسی به این Id از طریق یک template reference variable به نام categoryCtrl# انجام شدهاست.
- در آخر، ngFor تعریف شده به ازای هر عضو آرایهی categories، یکبار تگ option را تکرار میکند و در هربار تکرار، مقدار ویژگی value را به categoryId تنظیم میکند و برچسب نمایشی آنرا از categoryName دریافت خواهد کرد.
بنابراین مرحلهی بعدی تکمیل این drop down آبشاری، واکنش نشان دادن به رخداد change و تکمیل متد fetchProducts است:
- در ابتدای متد fetchProducts، آرایهی خاصیت عمومی products که به drop down دوم متصل خواهد شد، خالی میشود تا تداخلی با اطلاعات قبلی آن حاصل نشود.
- سپس بررسی میکنیم که آیا categoryId دریافتی undefined است یا خیر؟ این مساله دو علت دارد:
الف) اولین عضو drop down انتخاب محصولات را با مقدار undefined مشخص کردهایم:
ب) علت اینجا است که چون ngModel به model.categoryId متصل شدهاست و در این مدل، پارامتر و همچنین خاصیت عمومی categoryId از نوع optional است و با ؟ مشخص شدهاست:
به همین جهت زمانیکه مدل را به این صورت تعریف میکنیم:
مقدار categoryId همان undefined جاوا اسکریپت خواهد بود.
- پس از آن همانند قسمت قبل، این categoryId را به سرور ارسال کرده و سپس اطلاعات متناظری را دریافت و به خاصیت عمومی products نسبت دادهایم. همچنین از یک خاصیت عمومی دیگر به نام isLoadingProducts نیز استفاده شدهاست تا مشخص شود چه زمانی کار دریافت اطلاعات از سرور خاتمه پیدا میکند. از آن برای نمایش یک آیکن چرخندهی دیگر استفاده میکنیم:
به این ترتیب drop down دوم بر اساس مقدار خاصیت عمومی products تشکیل میشود. اگر مقدار isLoadingProducts مساوی true باشد، یک spinner که کدهای css آنرا در فایل src\styles.css به نحو ذیل تعریف کردهایم، نمایان میشود و برعکس. همچنین ngFor به ازای هر عضو آرایهی products یکبار تگ option را تکرار خواهد کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-06.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
و در دومی دستورات ذیل را اجرا کنید:
اکنون میتوانید برنامه را در آدرس http://localhost:5000 مشاهده و اجرا کنید.
مدلهای سمت سرور برنامه
در این مطلب قصد داریم لیست گروهها را به همراه محصولات مرتبط با آنها، توسط دو drop down list نمایش دهیم:
public class Category { public int CategoryId { set; get; } public string CategoryName { set; get; } [JsonIgnore] public IList<Product> Products { set; get; } } public class Product { public int ProductId { set; get; } public string ProductName { set; get; } }
منبع داده JSON سمت سرور
پس از مشخص شدن مدلهای برنامه، اکنون توسط دو اکشن متد، لیست گروهها و همچنین لیست محصولات یک گروه خاص را با فرمت JSON بازگشت میدهیم:
namespace AngularTemplateDrivenFormsLab.Controllers { [Route("api/[controller]")] public class ProductController : Controller { [HttpGet("[action]")] public async Task<IActionResult> GetCategories() { await Task.Delay(500); return Json(CategoriesDataSource.Items); } [HttpGet("[action]/{categoryId:int}")] public async Task<IActionResult> GetProducts(int categoryId) { await Task.Delay(500); var products = CategoriesDataSource.Items .Where(category => category.CategoryId == categoryId) .SelectMany(category => category.Products) .ToList(); return Json(products); } } }
- در اینجا از یک Delay نیز استفاده شدهاست تا بتوان آیکنهای چرخندهی Loading سمت کاربر را در حین کار با عملیاتی زمانبر، بهتر مشاهده کرد.
کدهای سمت کاربر برنامه
کدهای سمت کاربر این مثال در ادامهی همان مطلب «فرمهای مبتنی بر قالبها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» هستند که بر روی آن این دستورات فراخوانی شدهاست:
>ng g m Product -m app.module --routing
>ng g c product/product-group
>ng g cl product/product >ng g cl product/Category >ng g cl product/product-group-form
export class ProductGroupForm { constructor( public categoryId?: number, public productId?: number ) { } } export class Product { constructor( public productId: number, public productName: string ) { } } export class Category { constructor( public categoryId: number, public categoryName: string ) { } }
سپس سرویسی را جهت دریافت اطلاعات دراپ داونها از سرور تهیه کردهایم:
>ng g s product/product-items -m product.module
import { Injectable } from "@angular/core"; import { Http, Response, Headers, RequestOptions } from "@angular/http"; import { Observable } from "rxjs/Observable"; import "rxjs/add/operator/do"; import "rxjs/add/operator/catch"; import "rxjs/add/observable/throw"; import "rxjs/add/operator/map"; import "rxjs/add/observable/of"; import { Category } from "./category"; import { Product } from "./product"; @Injectable() export class ProductItemsService { private baseUrl = "api/product"; constructor(private http: Http) { } private handleError(error: Response): Observable<any> { console.error("observable error: ", error); return Observable.throw(error.statusText); } getCategories(): Observable<Category[]> { return this.http .get(`${this.baseUrl}/GetCategories`) .map(response => response.json() || {}) .catch(this.handleError); } getProducts(categoryId: number): Observable<Product[]> { return this.http .get(`${this.baseUrl}/GetProducts/${categoryId}`) .map(response => response.json() || {}) .catch(this.handleError); } }
پس از این مقدمات اکنون میتوان کدهای ProductGroupComponent را تکمیل کرد.
ابتدا در متد ngOnInit آن کار دریافت لیست آغازین گروههای محصولات را انجام میدهیم:
export class ProductGroupComponent implements OnInit { categories: Category[] = []; model = new ProductGroupForm(); constructor(private productItemsService: ProductItemsService) { } ngOnInit() { this.productItemsService.getCategories().subscribe( data => { this.categories = data; }, err => console.log("get error: ", err) ); }
اکنون چون این خاصیت در دسترس است، میتوان به قالب این کامپوننت مراجعه کرده و قسمت ابتدایی فرم را تکمیل کرد:
<div class="container"> <h3>Cascading Drop-down Lists</h3> <form #form="ngForm" (submit)="submitForm(form)" novalidate> <div class="form-group"> <label class="control-label">Category</label> <span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="categories.length == 0"></span> <select class="form-control" name="categoryCtrl" #categoryCtrl (change)="fetchProducts(categoryCtrl.value)" [(ngModel)]="model.categoryId"> <option value="undefined">Select a Category...</option> <option *ngFor="let category of categories" value="{{category.categoryId}}"> {{ category.categoryName }} </option> </select> </div>
- سپس ngModel به خاصیت categoryId وهلهای از کلاس ProductGroupForm که مدل معادل فرم است، متصل شدهاست.
- همچنین با اتصال به رخداد change، مقدار Id عضو انتخابی به متد fetchProducts ارسال میشود. دسترسی به این Id از طریق یک template reference variable به نام categoryCtrl# انجام شدهاست.
- در آخر، ngFor تعریف شده به ازای هر عضو آرایهی categories، یکبار تگ option را تکرار میکند و در هربار تکرار، مقدار ویژگی value را به categoryId تنظیم میکند و برچسب نمایشی آنرا از categoryName دریافت خواهد کرد.
بنابراین مرحلهی بعدی تکمیل این drop down آبشاری، واکنش نشان دادن به رخداد change و تکمیل متد fetchProducts است:
products: Product[] = []; isLoadingProducts = false; fetchProducts(categoryId?: number) { console.log(categoryId); this.products = []; if (categoryId === undefined || categoryId.toString() === "undefined") { return; } this.isLoadingProducts = true; this.productItemsService.getProducts(categoryId).subscribe( data => { this.products = data; this.isLoadingProducts = false; }, err => { console.log("get error: ", err); this.isLoadingProducts = false; } ); }
- سپس بررسی میکنیم که آیا categoryId دریافتی undefined است یا خیر؟ این مساله دو علت دارد:
الف) اولین عضو drop down انتخاب محصولات را با مقدار undefined مشخص کردهایم:
<option value="undefined">Select a Category...</option>
public categoryId?: number
model = new ProductGroupForm();
- پس از آن همانند قسمت قبل، این categoryId را به سرور ارسال کرده و سپس اطلاعات متناظری را دریافت و به خاصیت عمومی products نسبت دادهایم. همچنین از یک خاصیت عمومی دیگر به نام isLoadingProducts نیز استفاده شدهاست تا مشخص شود چه زمانی کار دریافت اطلاعات از سرور خاتمه پیدا میکند. از آن برای نمایش یک آیکن چرخندهی دیگر استفاده میکنیم:
<div class="form-group"> <label class="control-label">Product</label> <span class="glyphicon glyphicon-refresh glyphicon-spin spinner" *ngIf="isLoadingProducts"></span> <select class="form-control" name="productCtrl" [(ngModel)]="model.productId"> <option value="undefined">Select a Product...</option> <option *ngFor="let product of products" value="{{product.productId}}"> {{ product.productName }} </option> </select> </div>
/* Spinner */ .spinner { font-size:15px; z-index:10 } .glyphicon-spin { -webkit-animation: spin 1000ms infinite linear; animation: spin 1000ms infinite linear; } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-06.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس به ریشهی پروژه وارد شده و دو پنجرهی کنسول مجزا را باز کنید. در اولی دستورات
>npm install >ng build --watch
>dotnet restore >dotnet watch run