Reactive Forms
Reactive Forms — form holati TypeScript kodida FormControl, FormGroup, FormArray orqali boshqariladi. Murakkab validatsiya, dinamik field'lar va test qilish uchun qulayroq.
ReactiveFormsModule import
Nima bu?
Reactive formlar uchun ReactiveFormsModule import qilinadi. Bu formControl, formGroup, formArray directive'larini va FormBuilder ni faollashtiradi. Template-driven FormsModule dan alohida.
Kod misoli
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-search',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<input [formControl]="query" placeholder="Qidirish..." />
@if (query.invalid && query.touched) {
<span>Kamida 2 belgi kiriting</span>
}
`,
})
export class SearchComponent {
query = new FormControl('', { validators: [Validators.minLength(2)] });
}
Imtihonda
Savol: Reactive formda name attribute kerakmi?
Javob: Yo'q. [formControl] yoki formControlName TypeScript ob'ektiga bog'langan — DOM name shart emas.
Yodlash uchun
FormControl — bitta field
Nima bu?
FormControl — bitta input maydonining holati va qiymati. value, valid, dirty, touched, errors property'lari bor. Template'da [formControl]="ctrl" yoki FormGroup ichida formControlName="field".
Kod misoli
import { FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-email-input',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<input [formControl]="email" type="email" />
<p>Qiymat: {{ email.value }}</p>
<p>Valid: {{ email.valid }}</p>
<p>Xato: {{ email.errors | json }}</p>
<button (click)="email.setValue('test@mail.com')">To'ldirish</button>
<button (click)="email.reset()">Tozalash</button>
`,
})
export class EmailInputComponent {
email = new FormControl('', {
validators: [Validators.required, Validators.email],
nonNullable: true, // reset() bo'sh string qaytaradi, null emas
});
}
Imtihonda
Savol: setValue va patchValue farqi FormControl'da?
Javob: FormControl uchun ikkalasi ham deyarli bir xil — to'liq qiymat o'rnatadi. Farq FormGroup da ko'rinadi: setValue barcha field talab qiladi, patchValue qisman yangilaydi.
Yodlash uchun
FormGroup — fields to'plami
Nima bu?
FormGroup — bir nechta FormControl (yoki ichki FormGroup/FormArray) ni birlashtiradi. [formGroup]="form" va formControlName="fieldName" bilan template bog'lanadi. Butun form valid/dirty holati group darajasida ham mavjud.
Kod misoli
@Component({
selector: 'app-user-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="save()">
<input formControlName="firstName" />
<input formControlName="lastName" />
<input formControlName="email" type="email" />
<button type="submit" [disabled]="form.invalid">Saqlash</button>
</form>
`,
})
export class UserFormComponent {
form = new FormGroup({
firstName: new FormControl('', Validators.required),
lastName: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email]),
});
save(): void {
if (this.form.valid) {
console.log(this.form.value);
// { firstName: '...', lastName: '...', email: '...' }
}
}
// Typed access
get emailControl(): FormControl {
return this.form.get('email') as FormControl;
}
}
Imtihonda
Savol: form.get('email') nima qaytaradi?
Javob: AbstractControl | null. FormControl sifatida ishlatish uchun cast yoki typed FormGroup (v14+) kerak.
Yodlash uchun
FormArray — dinamik fields
Nima bu?
FormArray — dinamik ro'yxat field'lar (telefon raqamlari, mahsulot qatorlari). FormControl yoki FormGroup elementlarini saqlaydi. push(), removeAt(), at(index) metodlari bilan boshqariladi.
Kod misoli
@Component({
selector: 'app-phone-list',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div [formGroup]="form">
<div formArrayName="phones">
@for (phone of phones.controls; track $index) {
<div>
<input [formControlName]="$index" placeholder="+998..." />
<button type="button" (click)="removePhone($index)">O'chirish</button>
</div>
}
</div>
<button type="button" (click)="addPhone()">Raqam qo'shish</button>
</div>
`,
})
export class PhoneListComponent {
form = new FormGroup({
phones: new FormArray<FormControl<string>>([
new FormControl('', { nonNullable: true, validators: Validators.required }),
]),
});
get phones(): FormArray {
return this.form.get('phones') as FormArray;
}
addPhone(): void {
this.phones.push(
new FormControl('', { nonNullable: true, validators: Validators.required })
);
}
removePhone(index: number): void {
this.phones.removeAt(index);
}
}
Imtihonda
Savol: FormArray template'da qanday iterate qilinadi?
Javob: formArrayName="phones" + @for (ctrl of phones.controls; track $index) + [formControlName]="$index". Index track muhim — DOM qayta yaratilishini oldini oladi.
Yodlash uchun
FormBuilder — qisqa yaratish usuli
Nima bu?
FormBuilder (fb) — FormGroup, FormControl va FormArray yaratishni soddalashtiradi. [value, validators] tuple sintaksisi qisqaroq kod beradi. nonNullable varianti Angular 14+ typed forms bilan ishlaydi.
Kod misoli
import { FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-product-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<input formControlName="name" />
<input formControlName="price" type="number" />
<div formArrayName="tags">
@for (tag of tags.controls; track $index) {
<input [formControlName]="$index" />
}
</div>
<button type="submit">Saqlash</button>
</form>
`,
})
export class ProductFormComponent {
private fb = inject(FormBuilder);
form = this.fb.nonNullable.group({
name: ['', [Validators.required, Validators.minLength(2)]],
price: [0, [Validators.required, Validators.min(0)]],
tags: this.fb.nonNullable.array<string>(['']),
});
get tags(): FormArray {
return this.form.controls.tags;
}
submit(): void {
console.log(this.form.getRawValue()); // disabled field'lar ham
}
}
Imtihonda
Savol: getRawValue() va value farqi?
Javob: value — disabled control'larni o'z ichiga olmaydi. getRawValue() — barcha control qiymatlarini qaytaradi (disabled ham).
Yodlash uchun
updateOn: 'blur' | 'submit' | 'change'
Nima bu?
Validatorlar va holat yangilanishi qachon trigger bo'lishini belgilaydi:
change(default) — har keystroke'dablur— field fokus yo'qotgandasubmit— faqat form submit bo'lganda
Performance va UX uchun muhim — kam validatsiya = tezroq form.
Kod misoli
@Component({
selector: 'app-heavy-form',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="username" />
<!-- blur'da validate -->
<input formControlName="search" />
<!-- faqat submit'da validate -->
<input formControlName="notes" />
<button type="submit">Yuborish</button>
</form>
`,
})
export class HeavyFormComponent {
private fb = inject(FormBuilder);
form = new FormGroup({
username: new FormControl('', {
validators: Validators.required,
updateOn: 'blur',
}),
search: new FormControl('', {
validators: [Validators.minLength(3)],
updateOn: 'change', // default — har harfda
}),
notes: new FormControl('', {
validators: Validators.maxLength(500),
updateOn: 'submit',
}),
});
// Butun form uchun
formGroup = this.fb.group(
{ title: ['', Validators.required] },
{ updateOn: 'blur' }
);
onSubmit(): void {
this.form.markAllAsTouched();
if (this.form.valid) { /* ... */ }
}
}
Imtihonda
Savol: Async validator bilan updateOn: 'change' muammosi?
Javob: Har keystroke'da HTTP so'rov ketadi — performance muammo. Async validator uchun updateOn: 'blur' yoki debounce qo'yish kerak.
Yodlash uchun
