مطالب
ASP.NET MVC #23

اجرای برنامه‌های ASP.NET MVC توسط نگارش‌های متفاوت IIS

تا اینجا برای اجرای برنامه‌های ASP.NET MVC از وب سرور توکار VS.NET استفاده شد که صرفا جهت آزمایش برنامه‌ها طراحی شده است. تا این تاریخ سه رده از وب سرورهای مایکروسافت ارائه شده‌اند که برای نصب ASP.NET MVC می‌توانند مورد استفاده قرار گیرند و هر کدام هم نکته‌های خاص خودشان را دارند که در ادامه به بررسی آن‌ها خواهیم پرداخت.


اجرای برنامه‌های ASP.NET MVC بر روی IIS 5.x ویندوز XP

پس از ایجاد یک دایرکتوری مجازی بر روی پوشه یک برنامه ASP.NET MVC و سعی در اجرای برنامه، بلافاصله پیغام خطای HTTP 403 forbidden مشاهده می‌شود.
اولین کاری که برای رفع این مساله باید صورت گیرد، کلیک راست بر روی نام دایرکتوری مجازی در کنسول IIS، انتخاب گزینه خواص و سپس مراجعه به برگه «ASP.NET» آن است. در اینجا شماره نگارش دات نت فریم ورک مورد استفاده را به 4 تغییر دهید (برای نمونه ASP.NET MVC 3.0 مبتنی بر دات نت فریم ورک 4 است).
بعد از این تغییر، بازهم موفق به اجرای برنامه‌های ASP.NET MVC بر روی IIS 5.x نخواهیم شد؛ چون در آن زمان مفاهیم مسیریابی و Routing که اصل و پایه ASP.NET MVC هستند وجود خارجی نداشتند. این نگارش از IIS به صورت پیش فرض تنها قادر به پردازش درخواست‌های رسیده‌ای که به یک فایل فیزیکی بر روی سرور اشاره می‌کند، می‌باشد (یعنی مشکلی با اجرای برنامه‌های ASP.NET Web forms ندارد).
برای رفع این مشکل، مجددا بر روی نام دایرکتوری مجازی برنامه در کنسول IIS کلیک راست کرده و گزینه خواص را انتخاب کنید. در صفحه ظاهر شده، در برگه «Virtual directory» آن، بر روی دکمه «Configuration» کلیک نمائید. در صفحه باز شده مجددا بر روی دکمه «Add» کلیک کنید.
در صفحه باز شده، مسیر Executable را C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll وارد کرده و Extension را به .* (دات هرچی) تنظیم کنید. همین مقدار تنظیم، برای اجرای برنامه‌های ASP.NET MVC بر روی IIS 5.x ویندوز XP کفایت می‌کند.

کاری که در اینجا انجام شده است، نگاشت تمام درخواست‌های رسیده صرفنظر از پسوند فایل‌ها، به موتور ASP.NET می‌باشد. به صورت پیش فرض در IIS 5.x درخواست‌ها تنها بر اساس پسوند فایل‌ها پردازش می‌شوند. مثلا اگر فایل درخواستی aspx است، درخواست رسیده به aspnet_isapi.dll یاد شده هدایت خواهد شد. اگر پسوند فایل php است به isapi مخصوص آن (در صورت نصب) هدایت می‌گردد و به همین ترتیب برای سایر سیستم‌های دیگر. زمانیکه Extension به «دات هرچی» و Executable به aspnet_isapi.dll دات نت 4 تنظیم می‌شود، دایرکتوری مجازی تنظیم شده تنها جهت سرویس دهی به یک برنامه ASP.NET عمل خواهد کرد و تمام درخواست‌های رسیده به آن، به موتور اجرایی ASP.NET هدایت می‌شوند.

بدیهی است تنظیمات فوق تنها به یک دایرکتوری مجازی اعمال شدند. اگر نیاز باشد تا بر روی تمام سایت‌ها تاثیر گذار شود، اینبار در کنسول IIS 5.x بر روی «Default web site» کلیک راست کرده و گزینه خواص را انتخاب کنید. در صفحه باز شده به برگه «Home directory» مراجعه کرده و مراحل ذکر شده را تکرار کنید.

مشکل! این روش بهینه نیست.
روش فوق خوبه، کار می‌کنه، اما بهینه نیست؛ از این جهت که «نگاشت تمام درخواست‌ها به موتور ASP.NET» یعنی پروسه پردازش درخواست یک فایل تصویری، js یا css هم باید از فیلتر موتور ASP.NET عبور کند که ضروری نیست.
برای رفع این مشکل، توصیه شده است که سیستم مسیریابی ASP.NET MVC را در IIS 5.x «پسوند دار» کنید. به این نحو که با مراجعه به فایل Global.asax.cs، تعاریف مسیریابی را به نحو زیر ویرایش کنید:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
new Route("{controller}.aspx/{action}/{id}", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
})
});



اینبار برای مثال مسیر http://localhost/MyMvcApp/home.aspx/index به علت داشتن پسوند aspx وارد موتور پردازشی ASP.NET خواهد شد. البته در این حالت URL های تمیز ASP.NET MVC را از دست خواهیم داد و مدام باید دقت داشت که مسیرهای کنترلرها حتما باید به aspx ختم شوند. ضمنا با این تنظیم، دیگر نیازی به تغییر تعاریف نگاشت‌ها در کنسول مدیریتی IIS، نخواهد بود.


اجرای برنامه‌های ASP.NET MVC بر روی IIS 6.x ویندوز سرور 2003

تمام نکات عنوان شده جهت IIS 5.x در IIS 6.x نیز صادق هستند. به علاوه برای اجرای برنامه‌های ASP.NET بر روی IIS 6.x باید به دو نکته مهم دیگر نیز دقت داشت:
الف) ASP.NET 4 به صورت پیش فرض در IIS 6.x غیرفعال است که باید با مراجعه به قسمت Web Services Extensions در کنسول مدیریتی IIS، آن‌را از حالت prohibited خارج کرد.
ب) در هر Application pool تنها از یک نگارش دات نت فریم ورک می‌توان استفاده کرد. برای مثال اگر هم اکنون AppPool1 مشغول سرویس دهی به یک سایت ASP.NET 3.5 است، از آن نمی‌توانید جهت اجرای برنامه‌های ASP.NET MVC 3 به بعد استفاده کنید. زیرا برای مثال ASP.NET MVC 3 مبتنی بر دات نت فریم ورک 4 است. به همین جهت حتما نیاز است تا یک Application pool مجزا را برای برنامه‌های دات نت 4 در IIS 6 اضافه نمائید و سپس در تنظیمات سایت، از این Application pool جدید استفاده نمائید.
البته روش صحیح و اصولی کار با IIS از نگارش 6 به بعد هم مطابق شرحی است که عنوان شد. برای دستیابی به بهترین کارآیی و امنیت بیشتر، بهتر است به ازای هر سایت، از یک Application pool مجزا استفاده نمائید.

اطلاعات تکمیلی:
نکات نصب برنامه‌های ASP.NET 4.0 بر روی IIS 6
مروری بر تاریخچه محدودیت حافظه مصرفی برنامه‌های ASP.NET در IIS



اجرای برنامه‌های ASP.NET MVC بر روی IIS 7.x ویندوز 7 و ویندوز سرور 2008

اگر برنامه ASP.NET MVC در IIS 7.x در حالت یکپارچه (integrated mode) اجرا شود، بدون نیاز به هیچگونه تغییری در تنظیمات سرور یا برنامه، بدون مشکل قابل اجرا خواهد بود. بدیهی است در اینجا نیز بهتر است به ازای هر برنامه، یک Application pool مجزا را ایجاد کرد.
اما در حالت classic (که برای برنامه‌های جدید توصیه نمی‌شود) نیاز است همان مراحل IIS 5,x تکرار شود. البته اینبار مسیر زیر را باید طی کرد تا به صفحه افزودن نگاشت‌ها رسید:
Right-click on a web site -> Properties -> Home Directory tab -> click on the Configuration button -> Mappings tab



نکته‌ای مهم در تمام نگارش‌های IIS

ترتیب نصب دات نت فریم ورک 4 و IIS مهم است. اگر ابتدا IIS نصب شود و سپس دات نت فریم ورک 4، به صورت خودکار، کار نگاشت اطلاعات ASP.NET به IIS صورت خواهد گرفت.
اگر ابتدا دات نت فریم ورک 4 نصب شود و سپس IIS، برای مثال دیگر از برگه ASP.NET در IIS 6.x خبری نخواهد بود. برای رفع این مشکل دستور زیر را در خط فرمان اجرا کنید:

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe /i

به این ترتیب، اطلاعات مرتبط با موتور ASP.NET مجددا به تنظیمات IIS اضافه خواهند شد.


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


ایجاد برنامه‌های جدید توسط Angular CLI

دستور خط فرمان ابتدایی ایجاد یک برنامه‌ی جدید توسط Angular CLI به صورت ذیل است
> ng new my-app
در اینجا ng همان Angular CLI است. new عملی است که قرار است رخ دهد و my-app یک نام دلخواه می‌باشد.
پس از اجرای این دستور، برنامه‌ی جدید ایجاد شده، در پوشه‌ی جدید my-app قرار می‌گیرد.

گزینه‌ی دیگر این دستور، استفاده از پرچم dry-run است:
> ng new my-app --dry-run
کار این پرچم صرفا گزارش دادن جزئیات عملیات ng new است؛ بدون اینکه فایلی را تولید کند. به این ترتیب می‌توان برآوردی را از فایل‌های تولید شده‌ی توسط فرامین ng، پیش از تولید واقعی آن‌ها، مشاهده کرد. برای مثال:
>ng new my-app --dry-run
installing ng
You specified the dry-run flag, so no changes will be written.
  create .editorconfig
  create README.md
  create src\app\app.component.css
  create src\app\app.component.html
.
.
.
Project 'my-app' successfully created.
همانطور که مشاهده می‌کنید، در ابتدای کار پیامی را مبنی بر عدم نوشته شدن این فایل‌ها بر روی فایل سیستم، ارائه داده‌است.

گزینه‌ی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
> ng new my-app --skip-install
کار پرچم skip-install عدم فراخوانی خودکار دستور npm install است که سبب خواهد شد، برنامه بدون نصب وابستگی‌های npm آن، با سرعت هرچه بیشتر، ایجاد شود. از این گزینه می‌توان جهت مشاهده‌ی ساختار فایل‌های تولیدی استفاده کرد و در نهایت در این حالت باید دستور npm install را به صورت دستی فراخوانی کرد. پرچم dry-run نیز skip-install را به صورت ضمنی به همراه دارد.

برای مشاهده‌ی سایر پرچم‌های مرتبط با دستور ng new می‌توان از پرچم help استفاده کرد:
> ng new --help


بررسی فایل angular-cli.json.

فایل angular-cli.json‌. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامه‌ی جدید را مشاهده می‌کنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
"project": {
      "name": "my-app"
  },
سپس محل پوشه‌ی source برنامه و همچنین خروجی نهایی آن، مشخص می‌شوند:
"apps": [
{
   "root": "src",
   "outDir": "dist",

یکی از تنظیمات مهم این فایل، مقدار prefix است:
"prefix": "app",
از این مقدار برای تنظیم مقدار prefix تمام کامپوننت‌ها و دایرکتیوهای تولیدی توسط Angular CLI استفاده می‌شود. برای مثال اگر به فایل src\app\app.component.ts دقت کنید:
@Component({
  selector: 'app-root',
نام selector آن با app- شروع شده‌است که این app، از مقدار prefix فایل angular-cli.json‌. دریافت شده‌است.
تغییر این مقدار صرفا بر روی کامپوننت‌های جدید تولید شده‌ی توسط Angular CLI تاثیرگذار خواهند بود. اگر می‌خواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، می‌توان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته می‌شود:
> ng new my-project --skip-install --prefix myCompany


عدم ایجاد مخزن Git به همراه ng new

با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامه‌ای را بدون نصب وابستگی‌ها، بدون ایجاد تست‌ها و بدون ایجاد مخزن git آن تولید کنید، می‌توان از دستور ذیل استفاده کرد:
> ng new my-project --skip-install --skip-git --skip-tests --skip-commit


استفاده‌ی از sass بجای css توسط Angular CLI

سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایل‌های sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایل‌های css این ابزار را که در فایل angular-cli.json‌. نمونه‌ای از آن ذکر شده‌است، به همراه فایل‌هایی مانند app.component.css، به sass تغییر دهید:
"styles": [
     "styles.css"
],

"defaults": {
   "styleExt": "css",
   "component": {}
  }
تنها کافی است پرچم style را با sass مقدار دهی کرد که حالت پیش فرض آن css است:
> ng new my-project --skip-install --style sass
با ذکر این پرچم، تغییرات فایل angular-cli.json‌  به صورت ذیل خواهند بود:
"styles": [
    "styles.sass"
],
"defaults": {
   "styleExt": "sass",
   "component": {}
}
و حتی کامپوننت src\app\app.component.ts نیز به همراه شیوه‌نامه‌ای از نوع sass که در حین ارائه نهایی توسط webpack به صورت خودکار پردازش می‌شود (بدون نیاز به تنظیمات اضافه‌تری)، مقدار دهی شده‌است:
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.sass']
})
و یا حتی اگر علاقمند به استفاده‌ی از less باشید نیز می‌توان پرچم style less-- را استفاده نمود.


انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI

حالت پیش فرض تولید برنامه‌های جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، می‌توان پرچم routing را نیز در اینجا ذکر کرد:
> ng new my-project --skip-install --routing
در این حالت اگر به پوشه‌ی src\app مراجعه کنید، فایل جدید app-routing.module.ts را نیز مشاهده خواهید کرد که AppRoutingModule پیش فرضی در آن تنظیم شده‌است و آماده‌ی استفاده می‌باشد.
const routes: Routes = [
  {
     path: '',
     children: []
  }
];
همچنین فایل app.module.ts را نیز اندکی تغییر داده و این AppRoutingModule جدید را نیز به آن معرفی کرده‌است.
imports: [
   BrowserModule,
   FormsModule,
   HttpModule,
   AppRoutingModule
],
به این ترتیب ارتباطات ابتدایی مورد نیاز سیستم مسیریابی برقرار شده و قابل استفاده‌است. بنابراین ذکر پرچم routing می‌تواند یکی از پرچم‌های اصلی ایجاد برنامه‌های جدید مبتنی بر Angular CLI باشد.


اجرای ابتدایی یک برنامه‌ی مبتنی بر Angular CLI

پس از انتخاب پرچم‌های مناسب جهت ایجاد یک پروژه‌ی جدید مبتنی بر Angular CLI و همچنین نصب وابستگی‌های آن‌ها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژه‌است. به همین جهت از طریق خط فرمان به ریشه‌ی پوشه‌ی برنامه‌ی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
>ng serve -o
در اینجا o- به معنای open است؛ یا گشودن آن در یک مرورگر. به این ترتیب کار کامپایل برنامه صورت گرفته و توسط مرورگر پیش‌فرض سیستم به صورت خودکار باز خواهد شد. آدرس پیش فرض آن نیز به صورت ذیل است:
 http://localhost:4200/


نکته‌ی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه می‌دهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
 <h1>
Test
  {{title}}
</h1>
پس از ذخیره‌ی آن، بلافاصله خروجی نهایی را در مرورگر خواهید دید.


تغییر پیش فرض‌های عمومی Angular CLI

تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شده‌است به myCompany تغییر دهیم، یا می‌توان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا می‌توان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app می‌توان از دستور ng set استفاده کرد:
>ng set apps.prefix myCompany
>ng set apps.prefix myCompany -g
دستور اول فایل angular-cli.json. پروژه‌ی جاری را ویرایش می‌کند و دستور دوم، فایل angular-cli.json سراسری Angular CLI را مقدار دهی خواهد کرد (با توجه به سوئیچ g- آن). البته بدیهی است ویرایشگرهای امروزی به خوبی قابلیت ویرایش فایل‌های json را ارائه می‌دهند و شاید نیازی به استفاده‌ی از این دستورات،‌حداقل برای اعمال تغییرات محلی نباشد.
و یا اگر بخواهید نوع شیوه‌نامه‌ی مورد استفاده را ویرایش کنید، می‌توان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
>ng set defaults.styleExt sass
>ng set defaults.styleExt sass -g


اجرای امکانات Linting پروژه‌های مبتنی بر Angular CLI

برای بررسی کیفیت کدهای نوشته شده، می‌توان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشه‌ی پروژه اجرا نمود:
> ng lint
برای مشاهده‌ی تمام گزینه‌های آن دستور زیر را صادر کنید:
> ng lint --help
اگر علاقمند هستید تا خروجی این ابزار، با رنگ‌های بهتری نمایش داده شوند، می‌توان از دستور ذیل استفاده کرد:
> ng lint --format stylish
به علاوه این ابزار قابلیت رفع مشکلات را با اعمال تغییراتی در کدهای موجود نیز به همراه دارد:
>ng lint --fix
برای این منظور می‌توان از پرچم fix آن استفاده کرد.

یک مثال: فایل src\app\app.component.ts‌  را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آن‌را به صورت ذیل تغییر دهید:
import { Component } from '@angular/core'
let number = 10;
اکنون اگر ng lint را اجرا کنیم، به خروجی ذیل خواهیم رسید:
>ng lint --format stylish

/src/app/app.component.ts[3, 5]: Identifier 'number' is never reassigned; use 'const' instead of 'let'.
/src/app/app.component.ts[1, 42]: Missing semicolon

Lint errors found in the listed files.
عنوان می‌کند که بهتر است number به صورت یک const تعریف شود و همچنین یک سمی‌کالن در سطر اول فراموش شده‌است.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
import { Component } from '@angular/core';
const number = 10;
به صورت خودکار، به سطر اول، یک سمی‌کالن را اضافه کرده و همچنین سطر دوم را نیز به const تبدیل کرده‌است.
نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت نهم - مسیریابی
چند نکته‌ی تکمیلی
- مسیریابی در نگارش نهایی AngularJS 2.0 تغییرات کلی خواهد داشت. به همین جهت نام ماژول آن‌را به منسوخ شده تغییر داده‌اند (در نگارش RC):
 import { ROUTER_PROVIDERS, RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprecated';
- با تغییرات نگارش RC، دیگر نیازی به ذکر router.dev.js در فایل index.html نیست. این مدخل به صورت خودکار توسط systemjs.config.js اضافه می‌شود:
 <script src="~/systemjs.config.js"></script>
نظرات مطالب
سفارشی سازی صفحه‌ی اول برنامه‌های Angular CLI توسط ASP.NET Core
آیا پس از توزیع یک برنامه آنگولار تک صفحه ای صرفا با اجرای index.html در مرورگر بدون نیاز به وب سرور ( در مواردی مانند web api که داده‌ها یه صورت json و از راه دور تامین می‌شوند ) مشکلی نخواهد بود و مواردی مانند مسیریابی صرفا توسط جاوا اسکریپت هندل خواهند شد ؟
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت دهم - کار با فرم‌ها - قسمت اول
هر برنامه‌ی وبی، نیاز به کار با فرم‌های وب را دارد و به همین جهت، AngularJS 2.0 به همراه دو نوع از فرم‌ها است: فرم‌های مبتنی بر قالب‌ها و فرم‌های مبتنی بر مدل‌ها.
کار با فرم‌های مبتنی بر قالب‌ها ساده‌تر است؛ اما کنترل کمتری را بر روی مباحث اعتبارسنجی داده‌های ورودی توسط کاربر، در اختیار ما قرار می‌دهند. اما فرم‌های مبتنی بر مدل‌ها هر چند به همراه اندکی کدنویسی بیشتر هستند، اما کنترل کاملی را جهت اعتبارسنجی ورودی‌های کاربران، ارائه می‌دهند. در این قسمت فرم‌های مبتنی بر قالب‌ها (Template-driven forms) را بررسی می‌کنیم.


ساخت فرم مبتنی بر قالب‌های ثبت یک محصول جدید

در ادامه‌ی مثال این سری، می‌خواهیم به کاربران، امکان ثبت اطلاعات یک محصول جدید را نیز بدهیم. به همین جهت فایل‌های جدید product-form.component.ts و product-form.component.html را به پوشه‌ی App\products برنامه اضافه می‌کنیم (جهت تعریف کامپوننت فرم جدید به همراه قالب HTML آن).
الف) محتوای کامل product-form.component.html
<form #f="ngForm" (ngSubmit)="onSubmit(f.form)">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3 class="panel-title">
                Add Product
            </h3>
        </div>
        <div class="panel-body form-horizontal">
            <div class="form-group">
                <label for="productName" class="col col-md-2 control-label">Name</label>
                <div class="controls col col-md-10">
                    <input ngControl="productName" id="productName" required
                           #productName="ngForm"
                           (change)="log(productName)"
                           minlength="3"
                           type="text" class="form-control"
                           [(ngModel)]="productModel.productName"/>
                    <div *ngIf="productName.touched && productName.errors">
                        <label class="text-danger" *ngIf="productName.errors.required">
                            Name is required.
                        </label>
                        <label class="text-danger" *ngIf="productName.errors.minlength">
                            Name should be minimum {{ productName.errors.minlength.requiredLength }} characters.
                        </label>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <label for="productCode" class="col col-md-2 control-label">Code</label>
                <div class="controls col col-md-10">
                    <input ngControl="productCode" id="productCode" required
                           #productCode="ngForm"
                           type="text" class="form-control"
                           [(ngModel)]="productModel.productCode"/>
                    <label class="text-danger" *ngIf="productCode.touched && !productCode.valid">
                        Code is required.
                    </label>
                </div>
            </div>
            <div class="form-group">
                <label for="releaseDate" class="col col-md-2 control-label">Release Date</label>
                <div class="controls col col-md-10">
                    <input ngControl="releaseDate" id="releaseDate" required
                           #releaseDate="ngForm"
                           type="text" class="form-control"
                           [(ngModel)]="productModel.releaseDate"/>
                    <label class="text-danger" *ngIf="releaseDate.touched && !releaseDate.valid">
                        Release Date is required.
                    </label>
                </div>
            </div>
            <div class="form-group">
                <label for="price" class="col col-md-2 control-label">Price</label>
                <div class="controls col col-md-10">
                    <input ngControl="price" id="price" required
                           #price="ngForm"
                           type="text" class="form-control"
                           [(ngModel)]="productModel.price"/>
                    <label class="text-danger" *ngIf="price.touched && !price.valid">
                        Price is required.
                    </label>
                </div>
            </div>
            <div class="form-group">
                <label for="description" class="col col-md-2 control-label">Description</label>
                <div class="controls col col-md-10">
                    <textarea ngControl="description" id="description" required
                              #description="ngForm"
                              rows="10" type="text" class="form-control"
                              [(ngModel)]="productModel.description"></textarea>
                    <label class="text-danger" *ngIf="description.touched && !description.valid">
                        Description is required.
                    </label>
                </div>
            </div>
            <div class="form-group">
                <label for="imageUrl" class="col col-md-2 control-label">Image</label>
                <div class="controls col col-md-10">
                    <input ngControl="imageUrl" id="imageUrl" required
                           #imageUrl="ngForm"
                           type="text" class="form-control"
                           [(ngModel)]="productModel.imageUrl"/>
                    <label class="text-danger" *ngIf="imageUrl.touched && !imageUrl.valid">
                        Image is required.
                    </label>
                </div>
            </div>
        </div>
        <footer class="panel-footer">
            <button [disabled]="!f.valid"
                    type="submit" class="btn btn-primary">
                Submit
            </button>
        </footer>
    </div>
</form>

ب) محتوای کامل product-form.component.ts
import { Component } from 'angular2/core';
import { Router } from 'angular2/router';
import { IProduct } from './product';
import { ProductService } from './product.service';
 
@Component({
    //selector: 'product-form',
    templateUrl: 'app/products/product-form.component.html'
})
export class ProductFormComponent {
 
    productModel = <IProduct>{}; // creates an empty object of an interface
 
    constructor(private _productService: ProductService, private _router: Router) { }
 
    log(productName): void {
        console.log(productName);
    }
 
    onSubmit(form): void {
        console.log(form);
        console.log(this.productModel);
 
        this._productService.addProduct(this.productModel)
            .subscribe((product: IProduct) => {
                console.log(`ID: ${product.productId}`);
                this._router.navigate(['Products']);
            });
    }
}

اکنون ریز جزئیات و تغییرات این دو فایل را قدم به قدم بررسی خواهیم کرد.

تا اینجا در فایل product-form.component.html یک فرم ساده‌ی HTML ایی مبتنی بر بوت استرپ 3 را تهیه کرده‌ایم. نکات ابتدایی آن، دقیقا مطابق است با مستندات بوت استرپ 3؛ از لحاظ تعریف form-horizontal و سپس ایجاد یک div با کلاس form-group و قرار دادن المان‌هایی با کلاس‌های form-control در آن. همچنین برچسب‌های تعریف شده‌ی با ویژگی for، در این المان‌ها، جهت بالارفتن دسترسی پذیری به عناصر فرم، اضافه شده‌اند. این مراحل در مورد تمام فرم‌های استاندارد وب صادق هستند و نکته‌ی جدیدی ندارند.

در ادامه تعاریف AngularJS 2.0 را به این فرم اضافه کرد‌ه‌ایم. در اینجا هر کدام از المان‌های ورودی، تبدیل به Controlهای AngularJS 2.0 شده‌اند. کلاس Control، خواص ویژه‌ای را در اختیار ما قرار می‌دهد. برای مثال value یا مقدار این المان چیست؟ وضعیت touched و untouched آن چیست؟ (آیا کاربر فوکوس را به آن منتقل کرده‌است یا خیر؟) آیا dirty است؟ (مقدار آن تغییر کرده‌است؟) و یا شاید هم pristine است؟ (مقدار آن تغییری نکرده‌است). علاوه بر این‌ها دارای خاصیت valid نیز می‌باشد (آیا اعتبارسنجی آن موفقیت آمیز است؟)؛ به همراه خاصیت errors که مشکلات اعتبارسنجی موجود را باز می‌گرداند.
<div class="form-group">
    <label for="description" class="col col-md-2 control-label">Description</label>
    <div class="controls col col-md-10">
        <textarea ngControl="description" id="description" required
                  #description="ngForm"
                  rows="10" type="text" class="form-control"
                  [(ngModel)]="productModel.description"></textarea>
        <label class="text-danger" *ngIf="description.touched && !description.valid">
            Description is required.
        </label>
    </div>
</div>
در اینجا کلاس مفید دیگری به نام ControlGroup نیز درنظر گرفته شده‌است. برای مثال هر فرم، یک ControlGroup است (گروهی متشکل از کنترل‌ها، در صفحه). البته می‌توان یک فرم بزرگ را به چندین ControlGroup نیز تقسیم کرد. تمام خواصی که برای کلاس Control ذکر شدند، در مورد کلاس ControlGroup نیز صادق هستند. با این تفاوت که این‌بار اگر به خاصیت valid آن مراجعه کردیم، یعنی تمام کنترل‌های قرار گرفته‌ی در آن گروه معتبر هستند و نه صرفا یک تک کنترل خاص. به همین ترتیب خاصیت errors نیز تمام خطاهای اعتبارسنجی یک گروه را باز می‌گرداند.
هر دو کلاس Control و ControlGroup از کلاس پایه‌ای به نام AbstractControl مشتق شده‌اند و این کلاس پایه است که خواص مشترک یاد شده را به همراه دارد.

بنابراین برای کار ساده‌تر با یک فرم AngularJS 2.0، کل فرم را تبدیل به یک ControlGroup کرده و سپس هر کدام از المان‌های ورودی را تبدیل به یک Control مجزا می‌کنیم. کار برقراری این ارتباط، با استفاده از دایرکتیو ویژه‌ای به نام ngControl انجام می‌شود. بنابراین دایرکتیو ngControl، با نامی دلخواه و معین، به تمام المان‌های ورودی، انتساب داده شده‌است.
هرچند در این مثال نام ngControl‌ها با مقدار id هر کنترل یکسان درنظر گرفته شده‌است، اما ارتباطی بین این دو نیست. مقدار id جهت استفاده‌ی در DOM کاربرد دارد و مقدار ngControl توسط AngularJS 2.0 استفاده می‌شود. جهت رسیدن به کدهایی یکدست، بهتر است این نام‌ها را یکسان درنظر گرفت؛ اما هیچ الزامی هم ندارد.

برای بررسی جزئیات این اشیاء کنترل، در المان productName، یک متغیر محلی را به نام productName# تعریف کرده‌ایم و آن‌را به دایرکتیو ngControl انتساب داده‌ایم. این انتساب توسط ngForm انجام شده‌است. زمانیکه AngularJS 2.0 یک متغیر محلی تنظیم شده‌ی به ngForm را مشاهده می‌کند، آن‌را به صورت خودکار به ngControl همان المان ورودی متصل می‌کند. سپس این متغیر محلی را به متد log ارسال کرده‌ایم. این متد در کلاس کامپوننت جاری تعریف شده‌است و کار آن نمایش شیء Control جاری در کنسول developer tools مرورگر است.
<input ngControl="productName" id="productName" required
       #productName="ngForm"
       (change)="log(productName)"
       minlength="3"
       type="text" class="form-control"
       [(ngModel)]="productModel.productName"/>


همانطور که در تصویر مشاهده می‌کنید، عناصر یک شیء Control، در کنسول نمایش داده شده‌اند و در اینجا بهتر می‌توان خواصی مانند valid و امثال آن‌را که به همراه این کنترل وجود دارند، مشاهده کرد. برای مثال خاصیت dirty آن true است چون مقدار آن المان ورودی، تغییر کرده‌است.

بنابراین تا اینجا با استفاده از دایرکتیو ngControl، یک المان ورودی را به یک شیء Control متصل کردیم. همچنین نحوه‌ی تعریف یک متغیر محلی را در المانی و سپس ارسال آن را به کلاس متناظر با کامپوننت فرم، نیز بررسی کردیم.


افزودن اعتبارسنجی به فرم ثبت محصولات

به کنترل‌هایی که به صورت فوق توسط ngControl ایجاد می‌شوند، اصطلاحا implicitly created controls می‌گویند؛ یا به عبارتی ایجاد آن‌ها به صورت «ضمنی» توسط AngularJS 2.0 انجام می‌شود که نمونه‌ای از آن‌را در تصویر فوق نیز مشاهده کردید. این نوع کنترل‌های ضمنی، امکانات اعتبارسنجی محدودی را در اختیار دارند؛ که تنها سه مورد هستند:
الف) required
ب) minlength
ج) maxlength

این‌ها ویژگی‌های استاندارد اعتبارسنجی HTML 5 نیز هستند. نمونه‌ای از اعمال این موارد را با افزودن ویژگی required، به المان‌های فرم ثبت محصولات فوق، مشاهده می‌کنید.
سپس نیاز داریم تا خطاهای اعتبارسنجی را در مقابل هر المان ورودی نمایش دهیم.
<textarea ngControl="description" id="description" required
          #description="ngForm"
          rows="10" type="text" class="form-control"></textarea>
<div class="alert alert-danger" *ngIf="description.touched && !description.valid">
    Description is required.
</div>
پس از افزودن ویژگی required به یک المان، افزودن و نمایش خطاهای اعتبارسنجی، شامل سه مرحله‌ی زیر است:
الف) ایجاد یک div ساده جهت نمایش پیام خطای اعتبار سنجی
ب) افزودن یک متغیر محلی با # و تنظیم شده‌ی به ngForm، جهت دسترسی به شیء کنترل ایجاد شده
ج) استفاده از این متغیر محلی در دایرکتیو ساختاری ngIf* جهت دسترسی به خاصیت valid آن کنترل. بر مبنای مقدار این خاصیت است که تصمیم گرفته می‌شود، پیام اجباری بودن پر کردن فیلد نمایش داده شود یا خیر.
در اینجا یک سری کلاس بوت استرپ 3 هم جهت نمایش بهتر این پیام خطای اعتبارسنجی، اضافه شده‌اند.

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



بهبود شیوه نامه‌ی پیش فرض المان‌های ورودی اطلاعات در AngularJS 2.0

می‌خواهیم اگر اعتبارسنجی یک المان ورودی با شکست مواجه شد، یک حاشیه‌ی قرمز، در اطراف آن نمایش داده شود. این مورد را با توجه به اینکه AngularJS 2.0، شیوه نامه‌های ویژه‌ای را به صورت خودکار به المان‌ها اضافه می‌کند، می‌توان به صورت سراسری به تمام فرم‌ها اضافه کرد. برای این منظور فایل app.component.css واقع در ریشه‌ی پوشه‌ی app را گشوده و تنظیمات ذیل را به آن اضافه کنید:
.ng-touched.ng-invalid{
    border: 1px solid red;
}

ویژگی‌های اضافه شده‌ی در حالت شکست اعتبارسنجی؛ مانند ng-invalid


ویژگی‌های اضافه شده‌ی در حالت موفقیت اعتبارسنجی؛ مانند ng-valid



مدیریت چندین ویژگی اعتبارسنجی یک المان با هم

گاهی از اوقات نیاز است برای یک المان ورودی، چندین نوع اعتبارسنجی مختلف را تعریف کرد. برای مثال فرض کنید که ویژگی‌های required و همچنین minlength، برای نام محصول تنظیم شده‌اند. در این حالت ذکر productName.valid خیلی عمومی است و هر دو حالت اجباری بودن فیلد و حداقل طول آن‌را با هم به همراه دارد:
<div class="alert alert-danger" *ngIf="productName.touched && !productName.valid">
   Name is required.
</div>
بنابراین در این حالت از روش ذیل استفاده می‌شود:
<div *ngIf="productName.touched && productName.errors">
    <div class="alert alert-danger" *ngIf="productName.errors.required">
        Name is required.
    </div>
    <div class="alert alert-danger" *ngIf="productName.errors.minlength">
        Name should be minimum 3 characters.
    </div>
</div>
خاصیت errors نیز یکی دیگر از خواص شیء کنترل است. اگر نال بود، یعنی خطایی وجود ندارد و در غیراینصورت، به ازای هر نوع اعتبارسنجی تعریف شده، خواصی به آن اضافه می‌شوند. بنابراین ذکر productName.errors.required به این معنا است که آیا خاصیت errors، دارای کلیدی به نام required است؟ اگر بله، یعنی این فیلد هنوز پر نشده‌است.
همچنین چون در این حالت productName.touched نیاز است چندین بار تکرار شود، می‌توان آن‌را در یک div محصور کننده‌ی دو div مورد نیاز جهت نمایش خطاهای اعتبارسنجی قرار داد. به علاوه بررسی نال نبودن productName.errors نیز در div محصور کننده صورت گرفته‌است و دیگر نیازی نیست این بررسی را به ngIfهای داخلی اضافه کرد.

نکته 1
اگر علاقمند بودید تا جزئیات خاصیت errors را مشاهده کنید، آن‌را می‌توان توسط pipe توکاری به نام json به صورت موقت نمایش داد و بعد آن‌را حذف کرد:
 <div *ngIf="productName.touched && productName.errors">
  {{ productName.errors | json }}

نکته 2
بجای ذکر مستقیم عدد سه در «minimum 3 characters»، می‌توان این عدد را مستقیما از تعریف ویژگی minlength نیز استخراج کرد:
 Name should be minimum {{ productName.errors.minlength.requiredLength }} characters.


بررسی ngForm

شبیه به ngControl که یک المان ورودی را به یک کنترل AngularJS 2.0 متصل می‌کند، دایرکتیو دیگری نیز به نام ngForm وجود دارد که کل فرم را به شیء ControlGroup بایند می‌کند و برخلاف ngControl، نیازی به ذکر صریح آن وجود ندارد. هر زمانیکه AngularJS 2.0، المان استاندارد فرمی را در صفحه مشاهده می‌کند، این اتصالات را به صورت خودکار برقرار خواهد کرد.
ngForm دارای خاصیتی است به نام ngSumbit که از نوع EventEmitter است (نمونه‌ای از آن را در مبحث کامپوننت‌های تو در تو پیشتر ملاحظه کرده‌اید). بنابراین از آن می‌توان جهت اتصال رخداد submit فرم، به متدی در کلاس کامپوننت خود، استفاده کرد. متد متصل به این رخداد، زمانی فراخوانی می‌شود که کاربر بر روی دکمه‌ی submit کلیک کند:
 <form #f="ngForm" (ngSubmit)="onSubmit(f.form)">
همچنین در اینجا متغیر محلی f جهت دسترسی به شیء ControlGroup و ارسال آن به متد onSubmit تعریف شده‌است (شبیه به متغیرهای محلی دسترسی به ngControl که پیشتر جهت نمایش خطاهای اعتبارسنجی، اضافه کردیم).

پس از تعریف این رخداد و اتصال آن در قالب کامپوننت، اکنون می‌توان متد onSubmit را در کلاس آن نیز اضافه کرد.
onSubmit(form): void {
   console.log(form);
}
فعلا هدف از این متد، نمایش جزئیات شیء form دریافتی، در کنسول developer tools است.



غیرفعال کردن دکمه‌ی submit در صورت وجود خطاهای اعتبارسنجی

در قسمت بررسی ngForm، یک متغیر محلی را به نام f ایجاد کردیم که به شیء ControlGroup فرم جاری اشاره می‌کند. از این متغیر و خاصیت valid آن می‌توان با کمک property binding به خاصیت disabled یک دکمه، آن‌را به صورت خودکار فعال یا غیرفعال کرد:
<button [disabled]="!f.valid"
        type="submit" class="btn btn-primary">
    Submit
</button>
هر زمانیکه کل فرم از لحاظ اعتبارسنجی مشکلی نداشته باشد، دکمه‌ی submit فعال می‌شود و برعکس.



نمایش فرم افزودن محصولات توسط سیستم Routing

با نحوه‌ی تعریف مسیریابی‌ها در قسمت قبل آشنا شدیم. برای نمایش فرم افزودن محصولات، می‌توان تغییرات ذیل را به فایل app.component.ts اعمال کرد:
//same as before...
import { ProductFormComponent }  from './products/product-form.component';
 
@Component({
    //same as before…
    template: `
                //same as before…
                    <li><a [routerLink]="['AddProduct']">Add Product</a></li>
               //same as before…
    `,
    //same as before…
})
@RouteConfig([
    //same as before…
    { path: '/addproduct', name: 'AddProduct', component: ProductFormComponent }
])
//same as before...
ابتدا به RouteConfig، مسیریابی کامپوننت فرم افزودن محصولات اضافه شده‌است. سپس ماژول این کلاس در ابتدای فایل import شده و در آخر routerLink آن به قالب سایت و منوی بالای سایت اضافه شده‌است.



اتصال المان‌های فرم به مدلی جهت ارسال به سرور

برای اتصال المان‌های فرم به یک مدل، این مدل را به صورت یک خاصیت عمومی، در سطح کلاس کامپوننت فرم، تعریف می‌کنیم:
 productModel = <IProduct>{}; // creates an empty object of an interface
اگر از اینترفیسی مانند IProduct که در قسمت‌های قبل این سری تعریف شد، نیاز است شیء جدیدی ساخته شود، الزاما نیازی نیست تا یک کلاس جدید را از آن مشتق کرد و بعد متغیر new ClassName را تهیه کرد. در TypeScript می‌توان به صورت خلاصه از syntax فوق نیز استفاده کرد.
پس از تعریف خاصیت productModel، اکنون کافی است با استفاده از two-way data binding، آن‌را به المان‌های فرم نسبت دهیم. برای مثال:
<textarea ngControl="description" id="description" required
          #description="ngForm"
          rows="10" type="text" class="form-control"
          [(ngModel)]="productModel.description"></textarea>
در اینجا با استفاده از ngModel و انقیاد دو طرفه، کار اتصال به خاصیت توضیحات شیء محصول انجام شده‌است. اکنون بلافاصله تغییرات اعمالی به فرم، به مدل متناظر منعکس می‌شود و برعکس. این ngModel را به تمام المان‌های ورودی فرم متصل خواهیم کرد.
پس از تعریف یک چنین اتصالی، دیگر نیازی به مقدار دهی پارامتر onSubmit(f.form) نیست. زیرا شیء productModel، در متد onSumbit در دسترس است و این شیء همواره حاوی آخرین تغییرات اعمالی به المان‌های فرم است.

پس از اینکه فرم را به مدل آن متصل کردیم، فایل product.service.ts را گشوده و متد جدید addProduct را به آن اضافه کنید:
addProduct(product: IProduct): Observable<IProduct> {
    let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC
        let options = new RequestOptions({ headers: headers });
 
    return this._http.post(this._addProductUrl, JSON.stringify(product), options)
        .map((response: Response) => <IProduct>response.json())
        .do(data => console.log("Product: " + JSON.stringify(data)))
        .catch(this.handleError);
}
کار این متد، ارسال شیء محصول به یک اکشن متد برنامه‌ی ASP.NET MVC جاری است. با جزئیات کار با obsevables درمطلب «دریافت اطلاعات از سرور» پیشتر آشنا شده‌ایم.
نکته‌ی مهم اینجا است که content type پیش فرض ارسالی متد post آن، plain text است و در این حالت ASP.NET MVC شیء JSON دریافتی از کلاینت را پردازش نخواهد کرد. بنابراین نیاز است تا هدر content type را به صورت صریحی در اینجا ذکر نمود؛ در غیراینصورت در سمت سرور، شاهد نال بودن مقادیر دریافتی از کاربران خواهیم بود.
امضای سمت سرور متد دریافت اطلاعات از کاربر، چنین شکلی را دارد (تعریف شده در فایل Controllers\HomeController.cs):
 [HttpPost]
public ActionResult AddProduct(Product product)
{

اشیاء هدرها و تنظیمات درخواست، در متد addProduct سرویس ProductService، در ماژول‌های ذیل تعریف شده‌اند که باید به ابتدای فایل product.service.ts اضافه شوند:
 import { Headers, RequestOptions } from 'angular2/http';

پس از تعریف متد addProduct در سرویس ProductService، اکنون با استفاده از ترزیق این سرویس به سازنده‌ی کلاس فرم ثبت یک محصول جدید، می‌توان متد this._productService.addProduct را جهت ارسال productModel به سمت سرور، در متد onSubmit فراخوانی کرد:
//Same as before…
import { IProduct } from './product';
import { ProductService } from './product.service';
 
@Component({
//Same as before…
})
export class ProductFormComponent {
 
    productModel = <IProduct>{}; // creates an empty object of an interface
 
    constructor(private _productService: ProductService, private _router: Router) { }
 
    //Same as before… 

    onSubmit(form): void {
        console.log(form);
        console.log(this.productModel);
 
        this._productService.addProduct(this.productModel)
            .subscribe((product: IProduct) => {
                console.log(`ID: ${product.productId}`);
                this._router.navigate(['Products']);
            });
    }
}
همانطور که ذکر شد، از آنجائیکه شیء productModel حاوی آخرین تغییرات اعمالی توسط کاربر است، اکنون می‌توان پارامتر form متد onSubmit را حذف کرد.
در اینجا پس از فراخوانی متد addProduct، متد subscribe، در انتهای زنجیره، فراخوانی شده‌است. کار آن هدایت کاربر به صفحه‌ی نمایش لیست محصولات است. در اینجا this._router از طریق تزریق وابستگی‌های سرویس مسیریاب به سازنده‌ی کلاس، تامین شده‌است. نمونه‌ی آن‌را در قسمت «افزودن دکمه‌ی back با کدنویسی» مربوطه به مطلب آشنایی با مسیریابی، پیشتر مطالعه کرده‌اید.



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part10.zip


خلاصه‌ی بحث

فرم‌های template driven در AngularJS 2.0 به این نحو طراحی می‌شوند:
 1) ابتدا فرم HTML را به حالت معمولی آن طراحی می‌کنیم؛ با تمام المان‌های آن.
 2) به تمام المان‌های فرم، دیراکتیو ngControl را متصل می‌کنیم، تا AngularJS 2.0 آن‌را تبدیل به یک کنترل خاص خودش کند. کنترلی که دارای خواصی مانند valid و touched است.
 3) سپس برای دسترسی به این کنترل ایجاد شده‌ی به صورت ضمنی، یک متغیر محلی آغاز شده‌ی با # را به تمام المان‌ها اضافه می‌کنیم.
 4) اعتبارسنجی‌هایی را مانند required  به المان‌های فرم اضافه می‌کنیم.
 5) از متغیر محلی تعریف شده و ngIf* برای بررسی خواصی مانند valid و touched برای نمایش خطاهای اعتبارسنجی کمک گرفته می‌شود.
 6) پس از تعریف فرم، تعریف ngControlها، تعریف متغیر محلی شروع شده‌ی با # و افزودن خطاهای اعتبارسنجی، اکنون نوبت به ارسال این اطلاعات به سرور است. بنابراین رخداد ngSubmit را باید به متدی در کلاس کامپوننت جاری متصل کرد.
 7) اکنون که با کلیک بر روی دکمه‌ی submit فرم، متد onSubmit متصل به ngSubmit فراخوانی می‌شود، نیاز است بین المان‌های فرم HTML و کلاس کامپوننت، ارتباط برقرار کرد. این‌کار را توسط two-way data binding و تعریف ngModel بر روی تمام المان‌های فرم، انجام می‌دهیم. این ngModelها، به یک خاصیت عمومی که متناظر است با وهله‌ای از شیء مدل فرم، متصل هستند. بنابراین این مدل، در هر لحظه، بیانگر آخرین تغییرات کاربر است و از آن می‌توان برای ارسال اطلاعات به سرور استفاده کرد.
 8) پس از اتصال فرم به کلاس متناظر با آن، اکنون سرویس محصولات را تکمیل کرده و به آن متد HTTP Post را جهت ارسال اطلاعات سمت کاربر، به سرور، اضافه می‌کنیم. در اینجا نکته‌ی مهم، تنظیم content type ارسالی به سمت سرور است. در غیراینصورت فریم ورک سمت سرور قادر به تشخیص JSON بودن این اطلاعات نخواهد شد.
نظرات مطالب
یک نکته درباره Angular routeProvider
تمام مواردی که در routing مربوط به angularjs هستش در ui-router هم وجود دارد و مواردی مثل nested views و  multiple named views بهش اضافه شده و عملا در پروژه‌ها کاربردی‌تر خواهد بود.