فرم ها (Forms)
اینجا «فرم ها در انگولار» را مثل فرم مدرسه می بینیم. «فرم (Form)» یعنی جایی برای ورود داده. دو سبک داریم: «قالب محور (Template-driven)» و «واکنشی (Reactive)». سپس اعتبارسنجی را مثل نگهبان ورودی می گذاریم. در پایان، دکمه ارسال فقط وقتی مجاز است که فرم درست باشد.
اصول فرم ها در یک نگاه
فرم قالب محور با [(ngModel)] کار می کند. فرم واکنشی با FormGroup و FormControl ساخته می شود. ساده ها را قالب محور بزن. پیچیده ها را واکنشی بساز. ماژول ها را هم درست وارد کن.
سینتکس پایه قالب محور
<form #f="ngForm">
<input name="name" [(ngModel)]="name">
</form>
سینتکس پایه واکنشی
form = new FormGroup({ name: new FormControl('') });
<form [formGroup]="form">
<input formControlName="name">
</form>
نکته: برای بایندینگ داده و رخدادها، صفحه لیست ها و روتر را ببین. همچنین خود فرم ها در انگولار را مرجع کن.
فرم قالب محور (Template-driven)
شروعش سریع است. حس HTML عادی می دهد. با [(ngModel)] مقدار را می گیری. با #f="ngForm" وضعیت کلی را می خوانی. سپس در ارسال، مقدار را نمایش بده.
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<h3>Forms</h3>
<form #f="ngForm" (ngSubmit)="onSubmit()">
<label>
Name:
<input name="name" [(ngModel)]="name" placeholder="Enter your name">
</label>
<button type="submit">Submit</button>
</form>
<p>Value: {{ name }}</p>
<p *ngIf="submitted">Submitted!</p>
`
})
export class App {
name = '';
submitted = false;
onSubmit() {
this.submitted = true;
}
}
bootstrapApplication(App);
هشدار: روی کنترل های قالب محور، name یکتا بگذار. ثبت نشدن، ایراد می سازد.
المان های رایج HTML در فرم
ورودی های متن، ایمیل، شماره، تکست اریا، چک باکس، رادیو و سلکت همه بایند می شوند. برای آبجکت ها در سلکت از [ngValue] استفاده کن.
<input name="email" type="email" [(ngModel)]="model.email">
<textarea name="bio" [(ngModel)]="model.bio"></textarea>
<label><input type="checkbox" name="agree" [(ngModel)]="model.agree"> Agree</label>
<label><input type="radio" name="color" [value]="'red'" [(ngModel)]="model.color"> Red</label>
<label><input type="radio" name="color" [value]="'blue'" [(ngModel)]="model.color"> Blue</label>
<select name="pet" [(ngModel)]="model.pet">
<option [ngValue]="{ id: 1, name: 'Cat' }">Cat</option>
<option [ngValue]="{ id: 2, name: 'Dog' }">Dog</option>
</select>
<input type="file" (change)="onFiles($event)">
رادیو با مقدار غیررشته ای
<label><input type="radio" name="size" [ngValue]="1" [(ngModel)]="model.size"> Small</label>
<label><input type="radio" name="size" [ngValue]="2" [(ngModel)]="model.size"> Medium</label>
Select چندانتخابی
<select name="tags" [(ngModel)]="model.tags" multiple>
<option [ngValue]="'news'">News</option>
<option [ngValue]="'tech'">Tech</option>
<option [ngValue]="'sports'">Sports</option>
</select>
ورودی عددی و تبدیل مقدار
<input type="number" name="age" [ngModel]="age" (ngModelChange)="age = $any($event)">
compareWith برای Select آبجکتی
<select name="pet" [(ngModel)]="model.pet" [compareWith]="byId">
<option [ngValue]="{ id: 1, name: 'Cat' }">Cat</option>
<option [ngValue]="{ id: 2, name: 'Dog' }">Dog</option>
</select>
byId = (a: any, b: any) => a?.id === b?.id;
اعتبارسنجی (Validation)
قانون ها را با required و minlength و email بگذار. سپس خطاها را وقتی dirty یا touched یا ارسال شد، نشان بده. دکمه ارسال را وقتی نامعتبر است غیرفعال کن.
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, FormsModule],
template: `
<h3>Forms Validation</h3>
<form #f="ngForm" (ngSubmit)="onSubmit()" novalidate>
<label>
Name:
<input name="name" [(ngModel)]="model.name" required minlength="3" #name="ngModel">
</label>
<div *ngIf="name.invalid && (name.dirty || name.touched || submitted)" style="color:crimson">
<small *ngIf="name.errors && name.errors['required']">Name is required.</small>
<small *ngIf="name.errors && name.errors['minlength']">Name must be at least 3 characters.</small>
</div>
<label>
Email:
<input name="email" [(ngModel)]="model.email" email required #email="ngModel">
</label>
<div *ngIf="email.invalid && (email.dirty || email.touched || submitted)" style="color:crimson">
<small *ngIf="email.errors && email.errors['required']">Email is required.</small>
<small *ngIf="email.errors && email.errors['email']">Email must be valid.</small>
</div>
<button type="submit" [disabled]="f.invalid">Submit</button>
</form>
<p *ngIf="submitted">Submitted: {{ model | json }}</p>
`
})
export class App {
model = { name: '', email: '' };
submitted = false;
onSubmit() {
this.submitted = true;
}
}
bootstrapApplication(App);
فرم های واکنشی (Reactive)
در کد، درخت کنترل می سازی. سپس با [formGroup] و formControlName به قالب وصل می کنی. برای فرم های بزرگ، این روش بسیار قابل اعتماد است.
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<h3>Reactive Forms</h3>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label>
Name
<input formControlName="name" placeholder="Your name">
</label>
<div *ngIf="form.controls.name.invalid && (form.controls.name.dirty || form.controls.name.touched || submitted)" style="color:crimson">
<small *ngIf="form.controls.name.errors && form.controls.name.errors['required']">Name is required.</small>
<small *ngIf="form.controls.name.errors && form.controls.name.errors['minlength']">Min 3 characters.</small>
</div>
<label>
Email
<input formControlName="email" placeholder="you@example.com">
</label>
<div *ngIf="form.controls.email.invalid && (form.controls.email.dirty || form.controls.email.touched || submitted)" style="color:crimson">
<small *ngIf="form.controls.email.errors && form.controls.email.errors['required']">Email is required.</small>
<small *ngIf="form.controls.email.errors && form.controls.email.errors['email']">Email must be valid.</small>
</div>
<label>
<input type="checkbox" formControlName="newsletter">
Subscribe to newsletter
</label>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
<p>Status: {{ form.status }}</p>
<p>Value: {{ form.value | json }}</p>
<p *ngIf="submitted" style="color: seagreen;">Submitted!</p>
`
})
export class App {
fb = new FormBuilder();
submitted = false;
form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
newsletter: [false]
});
onSubmit() {
this.submitted = true;
}
}
bootstrapApplication(App);
جمع بندی سریع
- ساده؟ قالب محور؛ پیچیده؟ واکنشی.
- خطاها را دیرتر نشان بده، نه فوری.
- ارسال را وقتی نامعتبر است، ببند.
- فرم ها در انگولار را مرجع نگه دار.