در قسمت قبل، فرمهای template driven را بررسی کردیم. همانطور که مشاهده کردید، این نوع فرمها، قابلیتهای اعتبارسنجی پیشرفتهای را به همراه ندارند. برای فرمهایی که نیاز به اعتبارسنجیهای سفارشی دارند، فرمهای model driven پیشنهاد میشوند که در این قسمت بررسی خواهند شد.
طراحی فرم ثبت نام کاربران در سایت با روش model driven
در این قسمت قصد داریم فرم ثبت نام کاربران را به همراه اعتبارسنجیهای پیشرفتهای پیاده سازی کنیم. به همین منظور، ابتدا پوشهی جدید App\users را به مثال سری جاری اضافه کنید و سپس سه فایل user.ts، signup-form.component.ts و signup-form.component.html را به آن اضافه نمائید.
فایل user.ts بیانگر مدل کاربران سایت است؛ با این محتوا:
export interface IUser { id: number; name: string; email: string; password: string;
}
قالب فرم یا signup-form.component.html، در حالت ابتدایی آن چنین شکل استانداردی را خواهد داشت و فاقد اعتبارسنجی خاصی است:
<form> <div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" /> </div> <div class="form-group"> <label form="email">Email</label> <input id="email" type="text" class="form-control" /> </div> <div class="form-group"> <label form="password">Password</label> <input id="password" type="password" class="form-control" /> </div> <button class="btn btn-primary" type="submit">Submit</button></form>
اکنون میخواهیم این فرم را به یک فرم AngularJS 2.0 ارتقاء دهیم. بنابراین نیاز است اشیاء Control و ControlGroup را ایجاد کنیم و اینبار نمیخواهیم AngularJS 2.0 مانند
قسمت قبلی، به صورت خودکار (و ضمنی)، این اشیاء را برای ما ایجاد کند. میخواهیم آنها را با کدنویسی (به صورت صریح) ایجاد کنیم تا بتوانیم بر روی آنها کنترل بیشتری داشته باشیم.
بنابراین ابتدا کلاس کامپوننت این فرم را در فایل signup-form.component.ts به نحو ذیل تکمیل کنید:
import { Component } from '@angular/core';
import { Control, ControlGroup, Validators } from '@angular/common';
@Component({ selector: 'signup-form', templateUrl: 'app/users/signup-form.component.html'
})
export class SignupFormComponent { form = new ControlGroup({ name: new Control('', Validators.required), email: new Control('', Validators.required), password: new Control('', Validators.required) }); onSubmit(): void { console.log(this.form.value); }
}
و همچنین پیامهای اعتبارسنجی اولیه را نیز به نحو زیر به فایل signup-form.component.html اضافه میکنیم:
<form [ngFormModel]="form" (ngSubmit)="onSubmit()"> <div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" ngControl="name"/> <label class="text-danger" *ngIf="!form.controls['name'].valid"> Username is required. </label> </div> <div class="form-group"> <label form="email">Email</label> <input id="email" type="text" class="form-control" ngControl="email" #email="ngForm"/> <label class="text-danger" *ngIf="email.touched && !email.valid"> Email is required. </label> </div> <div class="form-group"> <label form="password">Password</label> <input id="password" type="password" class="form-control" ngControl="password" #password="ngForm"/> <label class="text-danger" *ngIf="password.touched && !password.valid"> Password is required. </label> </div> <button class="btn btn-primary" type="submit">Submit</button></form>
توضیحات:
تفاوت مهم این فرم و اعتبارسنجیهایش با قسمت قبل، در ایجاد اشیاء Control و ControlGroup به صورت صریح است:
form = new ControlGroup({ name: new Control('', Validators.required), email: new Control('', Validators.required), password: new Control('', Validators.required)
});
کلاسهای Control، ControlGroup و Validators در ماژول angular/common@ تعریف شدهاند. بنابراین import متناظری نیز به ابتدای فایل اضافه شدهاست:
import { Control, ControlGroup, Validators } from '@angular/common';
یک نکته
اگر محل قرارگیری کلاسی را فراموش کردید، آنرا در مستندات AngularJS 2.0 ذیل قسمت API Review جستجو کنید. نتیجهی جستجو، به همراه نام ماژول کلاسها نیز میباشد.
خاصیت عمومی form که با new ControlGroup تعریف شدهاست، حاوی تعاریف صریح کنترلهای موجود در فرم خواهد بود. در اینجا سازندهی ControlGroup، یک شیء را میپذیرد که کلیدهای آن، همان نام کنترلهای تعریف شدهی در قالب فرم و مقدار هر کدام، یک Control جدید است که پارامتر اول آن یک مقدار پیش فرض و پارامتر دوم، اعتبارسنجی مرتبطی را تعریف میکند (این اعتبارسنجی معرفی شده، یک متد استاتیک در کلاس توکار Validators است).
بنابراین چون سه المان ورودی، در فرم جاری تعریف شدهاند، سه کلید جدید هم نام نیز در پارامتر ورودی ControlGroup ذکر گردیدهاند.
اکنون که خاصیت عمومی form، در کلاس کامپوننت فوق تعریف شد، آنرا در قالب فرم، به ngFormModel بایند میکنیم:
<form [ngFormModel]="form" (ngSubmit)="onSubmit()">
به این ترتیب به AngularJS 2.0 اعلام میکنیم که ControlGroup و Controlهای آنرا به صورت صریح ایجاد کردهایم و بجای وهلههای پیش فرض خود، از خاصیت عمومی form کلاس کامپوننت، این مقادیر را تامین کن.
مراحل بعد آن، با مراحلی که
در قسمت قبلبررسی کردیم، تفاوتی ندارند:
الف) در اینجا به هر المان موجود، یک ngControl نسبت داده شدهاست تا هر المان را تبدیل به یک کنترل AngularJS2 2.0 کند.
ب) به هر المان، یک متغیر محلی شروع شده با # نسبت داده شدهاست تا با اتصال آن به ngForm بتوان به ngControl تعریف شده دسترسی پیدا کرد.
البته اکنون میتوان از خاصیت form متصل به ngFormModel نیز بجای تعریف این متغیر محلی، به نحو ذیل استفاده کرد:
<label class="text-danger" *ngIf="!form.controls['name'].valid">
ج) از این متغیر محلی جهت نمایش یا عدم نمایش پیامهای خطای اعتبارسنجی، در ngIfهای تعریف شده، استفاده شدهاست.
د) و در آخر متد onSumbit موجود در کلاس کامپوننت را به رخداد ngSubmit متصل کردهایم. همانطور که ملاحظه میکنید اینبار دیگر پارامتری را به آن ارسال نکردهایم. از این جهت که خاصیت form موجود در سطح کلاس، اطلاعات کاملی را از اشیاء موجود در آن دارد و در متد onSubmit کلاس، به آن دسترسی داریم.
onSubmit(): void { console.log(this.form.value); }
this.form.value حاوی یک شیء است که تمام مقادیر پر شدهی فرم را به همراه دارد.
بنابراین تا اینجا تنها تفاوت فرم جدید تعریف شده با قسمت قبل، تعریف صریح ControlGroup و کنترلهای آن در کلاس کامپوننت و اتصال آن به ngFormModel است. به این نوع فرمها، فرمهای model driven هم میگویند.
نمایش فرم افزودن کاربران توسط سیستم Routing
با نحوهی تعریف مسیریابیها
در قسمت نهمآشنا شدیم. برای نمایش فرم افزودن کاربران، میتوان تغییرات ذیل را به فایل app.component.ts اعمال کرد:
//same as before...
import { SignupFormComponent } from './users/signup-form.component';
@Component({
//same as before…
template: `
//same as before… <li><a [routerLink]="['AddUser']">Add User</a></li>
//same as before…
`,
//same as before…
})
@RouteConfig([
//same as before…
{ path: '/adduser', name: 'AddUser', component: SignupFormComponent }
])
//same as before...
ابتدا به RouteConfig، مسیریابی کامپوننت فرم افزودن کاربران، اضافه شدهاست. سپس ماژول این کلاس در ابتدای فایل import شده و در آخر routerLink آن به قالب سایت و منوی بالای سایت اضافه شدهاست.
معرفی کلاس FormBuilder
روش دیگری نیز برای ساخت ControlGroup و کنترلهای آن با استفاده از کلاس و سرویس فرم ساز توکار AngularJS 2.0 وجود دارد:
import { Control, ControlGroup, Validators, FormBuilder } from '@angular/common';
form: ControlGroup;
constructor(formBuilder: FormBuilder) { this.form = formBuilder.group({ name: ['', Validators.required], email: ['', Validators.required], password: ['', Validators.required] });
}
کلاس و سرویس FormBuilder نیز در ماژول angular/common@ قرار دارد. برای استفادهی از آن، آنرا در سازندهی کلاس تزریق کرده و سپس از متد group آن استفاده میکنیم. نحوهی تعریف کلی اعضای آن با اعضای ControlGroup یکی است؛ با این تفاوت که اینبار بجای ذکر new Control، یک آرایه تعریف میشود که دقیقا اعضای آن، همان پارامترهای شیء کنترل هستند. این روش در کل خلاصهتر است و در آن تعریف چندین گروه مختلف، سادهتر میباشد. همچنین با روش تزریق وابستگیهای بکار رفتهی در این فریم ورک نیز سازگاری بیشتری دارد.
پیاده سازی اعتبارسنجی سفارشی
فرض کنید میخواهیم ورود نام کاربرهای دارای فاصله را غیر معتبر اعلام کنیم. برای این منظور فایل جدید usernameValidators.ts را به پوشهی app\users اضافه کنید؛ با این محتوا:
import { Control } from '@angular/common';
export class UsernameValidators { static cannotContainSpace(control: Control) { if (control.value.indexOf(' ') >= 0) { return { cannotContainSpace: true }; } return null; }
}
کلاس UsernameValidators میتواند شامل تمام اعتبارسنجیهای سفارشی خاصیت نام کاربری باشد. به همین جهت نام آن جمع است و به s ختم شدهاست.
هر متد پیاده سازی کنندهی یک اعتبار سنجی سفارشی در این کلاس، استاتیک تعریف میشود؛ با نام دلخواهی که مدنظر است.
پارامتر ورودی این متدهای استاتیک، یک وهله از شیء کنترل است که توسط آن میتوان برای مثال به خاصیت value آن دسترسی یافت و بر این اساس منطق اعتبارسنجی خود را پیاده سازی نمود. به همین جهت import آن نیز به ابتدای فایل جاری اضافه شدهاست.
خروجی این متد دو حالت دارد:
الف) اگر null باشد، یعنی اعتبارسنجی موفقیت آمیز بودهاست.
ب) اگر اعتبارسنجی با شکست مواجه شود، خروجی این متد یک شیء خواهد بود که کلید آن، نام اعتبارسنجی مدنظر است و مقدار این کلید، هر چیزی میتواند باشد؛ یک true و یا یک شیء دیگر که اطلاعات بیشتری را در مورد این شکست ارائه دهد.
برای مثال اگر اعتبارسنج توکار required با شکست مواجه شود، یک چنین شیءایی را بازگشت میدهد:
و یا اگر اعتبارسنج minlength باشکست مواجه شود، اطلاعات بیشتری را در قسمت مقدار این کلید بازگشتی، ارائه میدهد:
{
minlength : {
requiredLength : 3,
actualLength : 1
}
}
در کل اینکه چه چیزی را بازگشت دهید، بستگی به طراحی مدنظر شما دارد.
پس از پیاده سازی یک اعتبارسنجی سفارشی، برای استفادهی از آن، ابتدا ماژول آنرا به ابتدای ماژول signup-form.component.ts اضافه میکنیم:
import { UsernameValidators } from './usernameValidators';
پس از آن، شبیه به افزودن متد استاتیک توکار Validators.required، این متد جدید را به لیست اعتبارسنجیهای خاصیت name اضافه میکنیم. از آنجائیکه پیشتر المان دوم این آرایه مقدار دهی شدهاست، برای ترکیب چندین اعتبارسنجی با هم، از متد Validators.compose که آرایهای از متدهای اعتبارسنجی را قبول میکند، کمک خواهیم گرفت:
name: ['', Validators.compose([Validators.required, UsernameValidators.cannotContainSpace])],
و مرحلهی آخر، نمایش یک پیام اعتبارسنجی مناسب و متناظر با متد cannotContainSpace است. برای این منظور فایل signup-form.component.html را گشوده و تغییرات ذیل را اعمال کنید:
<div class="form-group"> <label form="name">Username</label> <input id="name" type="text" class="form-control" ngControl="name" #name="ngForm" /> <div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.required"> Username is required. </label> <label class="text-danger" *ngIf="name.errors.cannotContainSpace"> Username can't contain space. </label> </div></div>
همانطور که
در قسمت قبلنیز عنوان شد، چون اکنون به یک المان، بیش از یک اعتبارسنجی اعمال شدهاست، استفاده از خاصیت valid، بیش از اندازه عمومی بوده و باید از خاصیت errors استفاده کرد. به همین جهت این دو اعتبارسنجی را در یک div محصور کننده قرار میدهیم و در صورت وجود خطایی، خاصیت name.errors، دیگر نال نبوده و دو برچسب قرار گرفتهی در آن بر اساس شرطهای ngIf آن، پردازش خواهند شد.
نام خاصیت بازگشت داده شدهی در اعتبارسنجی سفارشی، به عنوان یک خاصیت جدید شیء errors قابل استفاده است؛ مانند name.errors.cannotContainSpace.
به عنوان تمرین ماژول جدید emailValidators.ts را افزوده و سپس اعتبارسنجی سفارشی بررسی معتبر بودن ایمیل وارد شده را تعریف کنید:
import {Control} from '@angular/common';
export class EmailValidators { static email(control: Control) { var regEx = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var valid = regEx.test(control.value); return valid ? null : { email: true }; }
}
در ادامه آنرا به لیست formBuilder.group افزوده و همچنین پیام اعتبارسنجی ویژهای را نیز به قالب فرم اضافه کنید (کدهای کامل آن، در فایل zip انتهای بحث موجود است).
یک نکته
اگر نیاز است از regular expressions مانند مثال فوق استفاده شود، میتوان از متد توکار Validators.pattern نیز استفاده کرد و نیازی به تعریف یک متد جداگانه برای آن وجود ندارد؛ مگر اینکه نیاز به بازگشت شیء خطای کاملتری با خواص بیشتری وجود داشته باشد.
اعتبارسنجی async یا اعتبارسنجی از راه دور (remote validation)
یک سری از اعتبارسنجیها را در سمت کلاینت میتوان تکمیل کرد؛ مانند بررسی معتبر بودن فرمت ایمیل وارده. اما تعدادی دیگر، نیاز به اطلاعاتی از سمت سرور دارند. برای مثال آیا نام کاربری در حال ثبت، تکراری است یا خیر؟ این نوع اعتبارسنجیها در ردهی async validation قرار میگیرند.
سازندهی شیء Control در AngularJS 2.0 که در مثالهای بالا نیز مورد استفاده قرار گرفت، پارامتر اختیاری سومی را نیز دارد که یک AsyncValidatorFn را قبول میکند:
control(value: Object, validator?: ValidatorFn, asyncValidator?: AsyncValidatorFn) : Control
پیاده سازی async validators، بسیار شبیه به سایر اعتبارسنجها هستند. اما از آنجائیکه نیاز به کار با سرور را دارند، استاتیک تعریف کردن آنها، سبب قطع شدن دسترسی آنها از context کلاس جاری شده و امکان تزریق وابستگیها را از دست خواهیم داد. برای مثال دیگر نمیتوان به سادگی، سرویس دریافت اطلاعات کاربران را در اینجا تزریق کرد. یک راه حل رفع این مشکل، تعریف همان متد اعتبارسنج در کلاس کامپوننت فرم است:
nameShouldBeUnique(control: Control) { let name: string = control.value; return new Promise((resolve) => { this._userService.isUserNameUnique(<IUser>{ "name": name }).subscribe( (result: IResult) => { resolve(
result.result ? null : { 'nameShouldBeUnique': true } ); }, error => { resolve(null); } ); });
}
و سپس فراخوانی آن به صورت ذیل، به عنوان سومین عنصر آرایهی تعریف شده:
this.form = _formBuilder.group({ name: ['', Validators.compose([ Validators.required, UsernameValidators.cannotContainSpace ]), (control: Control) => this.nameShouldBeUnique(control)],
در اینجا با استفاده از
arrow functions، امکان دسترسی به این متد تعریف شدهی در سطح کلاس، که استاتیک هم نیست، وجود خواهد داشت. به این ترتیب دیگر context کلاس را از دست ندادهایم و اینبار میتوان به this._userService، که آنرا در ادامه تکمیل خواهیم کرد، بدون مشکلی دسترسی یافت.
امضای متد nameShouldBeUnique تفاوتی با سایر متدهای اعتبارسنج نداشته و پارامتر ورودی آن، همان کنترل است که توسط آن میتوان به مقدار وارد شدهی توسط کاربر دسترسی یافت. اما تفاوت اصلی آن در اینجا است که این متد باید
یک شیء Promiseرا بازگشت دهد. یک
Promise، بیانگر نتیجهی یک عملیات async است. در اینجا دو حالت resolve و error را باید پیاده سازی کرد. در حالت error، یعنی عملیات async صورت گرفته با شکست مواجه شدهاست و در حالت resolve، یعنی عملیات تکمیل شده و اکنون میخواهیم نتیجهی نهایی را بازگشت دهیم. نتیجه نهایی بازگشت داده شدهی در اینجا، همانند سایر validators است و اگر نال باشد، یعنی اعتبارسنجی موفقیت آمیز بوده و اگر یک شیء را بازگشت دهیم، یعنی اعتبارسنجی با شکست مواجه شدهاست.
این Promise، از یک سرویس تعریف شدهی در فایل جدید user.service.ts استفاده میکند:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '@angular/http';
import { IUser } from './user';
import { IResult } from './result';
@Injectable()
export class UserService { private _checkUserUrl = '/home/checkUser'; constructor(private _http: Http) { } private handleError(error: Response) { console.error(error); return Observable.throw(error.json().error || 'Server error'); } isUserNameUnique(user: IUser): Observable<IResult> { let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC let options = new RequestOptions({ headers: headers }); return this._http.post(this._checkUserUrl, JSON.stringify(user), options) .map((response: Response) => <IResult>response.json()) .do(data => console.log("User: " + JSON.stringify(data))) .catch(this.handleError); }
}
با نحوهی تعریف سرویسهاو همچنین کار با سرور و
دریافت اطلاعات، در قسمتهای قبلی بیشتر آشنا شدیم. در اینجا یک درخواست get، به آدرس home/checkuser سرور، ارسال میشود. سپس نتیجهی آن در قالب اینترفیس IResult بازگشت داده خواهد شد. این اینترفیس را در فایل result.ts به صورت ذیل تعریف کردهایم:
export interface IResult {
result: boolean;
}
کدهای سمت سرور برنامه که کار بررسی یکتا بودن نام کاربری را انجام میدهند، به صورت ذیل در فایل Controllers\HomeController.cs تعریف شدهاند:
namespace MVC5Angular2.Controllers
{ public class HomeController : Controller { [HttpPost] public ActionResult CheckUser(User user) { var isUnique = new { result = true }; if (user.Name?.Equals("Vahid", StringComparison.OrdinalIgnoreCase) ?? false) { isUnique = new { result = false }; } return new ContentResult { Content = JsonConvert.SerializeObject(isUnique, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), ContentType = "application/json", ContentEncoding = Encoding.UTF8 }; } }
}
در اینجا اگر نام کاربری وارد شده مساوی Vahid بود، یک شیء anonymous، مطابق امضای اینترفیس IResult سمت کاربر (همان فایل result.ts عنوان شده) بازگشت داده میشود.
بنابراین تا اینجا مسیر سمت سرور home/checkuser تکمیل شدهاست. این مسیر توسط سرویس کاربر صدا زده شده و اگر نام کاربری وارد شده موجود باشد، نتیجهای را مطابق امضای قرارداد IResult سفارشی ما بازگشت میدهد.
پس از آن مجددا به فایل signup-form.component.ts مراجعه کرده و سرویس جدید UserService را به سازندهی آن تزریق کردهایم. همچنین قسمت providers این کامپوننت را هم جهت تکمیل اطلاعات تزریق کنندهی توکار AngularJS 2.0 مقدار دهی کردهایم. البته همانطور که
در مبحث تزریق وابستگیهانیز عنوان شد، اگر این سرویس قرار نیست در کلاس دیگری استفاده شود، نیازی نیست تا آنرا در بالاترین سطح ممکن و در فایل app.component.ts ثبت و معرفی کرد:
@Component({ selector: 'signup-form', templateUrl: 'app/users/signup-form.component.html', providers: [ UserService ]
})
export class SignupFormComponent { constructor(private _formBuilder: FormBuilder, private _userService: UserService) {
پس از ترزیق وابستگی private _userService: UserService، اکنون این سرویس به سادگی و به حالت متداولی در متد nameShouldBeUnique(control: Control) قابل دسترسی خواهد بود و از آن میتوان جهت اعتبارسنجیهای غیرهمزمان استفاده کرد.
اکنون که کدهای فعال سازی اعتبارسنجی از راه دور ما تکمیل شدهاست، به فایل signup-form.component.html مراجعه کرده و پیام مناسبی را نمایش خواهیم داد:
<div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.required"> Username is required. </label> <label class="text-danger" *ngIf="name.errors.cannotContainSpace"> Username can't contain space. </label> <label class="text-danger" *ngIf="name.errors.nameShouldBeUnique"> This username is already taken. </label></div>
در ادامه اگر برنامه را اجرا کنید، با ورود نام کاربری Vahid، یک چنین پیام خطایی، مشاهده خواهد شد:
![]()
نمایش پیام loading در حین انجام اعتبارسنجی از راه دور
شاید بد نباشد که در حین انجام عملیات اعتبارسنجی از راه دور و ارسال درخواستی به سرور و بازگشت نتیجهی آن، یک پیام loading را نیز نمایش داد. برای انجام اینکار نیاز است تغییرات ذیل را به فایل signup-form.component.html اضافه کنیم:
<input id="name" type="text" class="form-control" ngControl="name" #name="ngForm" /><div *ngIf="name.control.pending"> Checking server, Please wait ...</div>
در اینجا یک div جدید را ذیل المان ورود نام کاربری اضافه کردهایم. همچنین نحوهی نمایش آنرا با دسترسی به متغیر name# و کنترل منتسب، به آن مدیریت میکنیم. اگر عملیات async ایی بر روی این کنترل در حال اجرا باشد، Promise تعریف شده، وضعیت pending را بازگشت میدهد. به همین جهت میتوان از این خاصیت، جهت نمایش دادن یا مخفی کردن عبارت و یا تصویری استفاده کرد.
اعتبارسنجی ترکیبی در حین submit یک فرم
فرض کنید میخواهید منطقی را که حاصل اعتبارسنجی تمام فیلدهای فرم است (و نه هر کدام به تنهایی)، در حین submit آن اعمال کنید. برای مثال آیا ترکیب نام کاربری و کلمهی عبور شخصی در حین login معتبر است یا خیر؟ در این حالت پس از بررسیهای لازم در متد onSubmit، میتوان با استفاده از متد find شیء form، به یکی از کنترلهای فرم دسترسی یافت و سپس با استفاده از متد setErrors، خطای اعتبارسنجی سفارشی را به آن اضافه کرد:
onSubmit(): void { console.log(this.form.value); this.form.find('name').setErrors({ invalidData : true });
}
سپس در سمت قالب این کامپوننت، نحوهی نمایش این اعتبارسنجی سفارشی، همانند قبل است:
<div *ngIf="name.touched && name.errors"> <label class="text-danger" *ngIf="name.errors.invalidData"> Check the inputs.... </label></div>
اتصال المانهای فرم به مدلی جهت ارسال به سرور
اکنون که دسترسی به خاصیت this.form را داریم و این خاصیت توسط [ngFormModel] به تمام اشیاء تعریف شدهی در فرم و تغییرات آنها دسترسی دارد، میتوان از آن برای دسترسی به شیءایی که حاوی مدل فرم است، استفاده کرد. برای نمونه در مثال فوق، خاصیت value آن، چنین خروجی را دارد:
{ name="VahidN", email="email@site.com", password="123"}
بنابراین برای ارسال اطلاعات این فرم به سرور، تنها کافی است این شیء را ارسال کنیم. به همین جهت در فایل user.service.ts، به کلاس سرویس کاربران، متد addUser را اضافه میکنیم:
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '@angular/http';
import { IUser } from './user';
import { IResult } from './result';
@Injectable()
export class UserService { private _addUserUrl = '/home/addUser'; constructor(private _http: Http) { } private handleError(error: Response) { console.error(error); return Observable.throw(error.json().error || 'Server error'); } addUser(user: IUser): Observable<IUser> { let headers = new Headers({ 'Content-Type': 'application/json' }); // for ASP.NET MVC let options = new RequestOptions({ headers: headers }); return this._http.post(this._addUserUrl, JSON.stringify(user), options) .map((response: Response) => <IUser>response.json()) .do(data => console.log("User: " + JSON.stringify(data))) .catch(this.handleError); }
}
کدهای سمت سرور آن در فایل Controllers\HomeController.cs نیز چنین شکلی را میتوانند داشته باشند:
[HttpPost]
public ActionResult AddUser(User user)
{ user.Id = 1; //todo: save user and get id from db return new ContentResult { Content = JsonConvert.SerializeObject(user, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), ContentType = "application/json", ContentEncoding = Encoding.UTF8 };
}
و پس از آن کدهای متد onSubmit فایل signup-form.component.ts برای ارسال این شیء به صورت ذیل خواهند بود:
onSubmit(): void { console.log(this.form.value); /*this.form.find('name').setErrors({ invalidData : true });*/ this._userService.addUser(<IUser>this.form.value) .subscribe((user: IUser) => { console.log(`ID: ${user.id}`); });
}
![]()
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: (این کدها مطابق نگارش RC 1 هستند)MVC5Angular2.part11.zip
خلاصهی بحث
برای اینکه بتوان کنترل بیشتری را بر روی المانهای فرم داشت، ابتدا سرویس FormBuilder را در سازندهی کلاس کامپوننت فرم تزریق میکنیم. سپس با استفاده از متد group آن، المانهای فرم را به صورت کلیدهای شیء پارامتر آن تعریف میکنیم. در اینجا میتوان اعتبارسنجیهای توکار AngularJS 2.0 را که در کلاس پایهی Validators مانند Validators.required وجود دارند، تعریف کرد. با استفاده از متد compose آنها را ترکیب نمود و یا پارامتر سومی را جهت اعتبارسنجیهای async اضافه نمود. در این حالت شیء form تعریف شده به صورت [ngFormModel] به قالب فرم متصل میشود و از تغییرات آن آگاه خواهد شد.