آشنایی با فرم های واکنشی انگولار — از صفر تا صد

۴۸۲ بازدید
آخرین به‌روزرسانی: ۱۱ شهریور ۱۴۰۲
زمان مطالعه: ۷ دقیقه
آشنایی با فرم های واکنشی انگولار — از صفر تا صد

فرم‌های واکنشی (Reactive) یک رویکرد مبتنی بر مدل برای مدیریت ورودی‌های فرم هستند که مقادیرشان در طی زمان تغییر می‌یابند. با این که فرم های واکنشی انگولار ممکن است در نگاه نخست پیچیده به نظر برسند، اما زمانی که با آن‌ها کاملاً آشنا شوید برایتان جالب خواهند بود. فرم‌های واکنشی کاملاً قدرتمند هستند. قصد ما در این مقاله آن است که بخش کوچکی از مهم‌ترین موارد مرتبط با آن‌ها را به شما نشان دهیم تا بتوانید از آن‌ها در عمل و در اپلیکیشن‌های واقعی استفاده کنید.

قبل از هر چیز برای استفاده از آن‌ها باید ماژول ReactiveFormsModule را در ماژول مطلوب خود ایمپورت کنید:

1import { ReactiveFormsModule } from ‘@angular/forms’;

کنترل فرم

منظور از «کنترل فرم» (Form Control) کلاسی است که مقدار و حالت اعتبارسنجی هر مشخصه فرم را نگهداری می‌کند.

در واقع هر ورودی در فرم با یک کنترل فرم مرتبط خواهد بود.

1const myFormControl = new FormControl();

زمانی که یک کنترل فرم ایجاد می‌کنید، می‌توانید آرگومان‌هایی به سازنده ارسال کنید. در ادامه یک مقداردهی کنترل فرم را می‌بینید که نکات مهمی را نمایش می‌د‌‌هد.

1const myFormControl = new FormControl(
2    { value: 'My String Value', disabled: true },
3    [ Validators.required, Validators.maxLength(30)]
4);

پارامتر اول به نام FormState است که در آن مقدار اولیه کنترل فرم و این که باید در ابتدا غیرفعال باشد یا نه را تعیین می‌کنیم.

پارامتر دوم آرایه‌ای از «اعتبارسنج‌ها» (Validators) است که مشخص می‌سازد کنترل فرم نامعتبر است و اعتبارسنجی آن تأیید نشده است. برای نمونه در مثال فوق، کنترل فرم یک فیلد الزامی است و بیشینه طول آن 30 است. اگر مقدار ورودی این الزام را رعایت نکرده باشد، کنترل فرم به صورت نامعتبر (invalid) تعیین می‌شود. زمانی که این الزام رعایت شده باشد، معتبر (valid) خواهد بود.

انگولار برخی اعتبارسنج‌های پیش‌ساخته دارد که اغلب نیازها را برآورده می‌کنند و می‌توانید در این صفحه (+) مشاهده کنید.

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

گروه فرم

«گروه فرم» (Form Group) شامل گروهی از کنترل‌های فرم است. این گروه می‌تواند شامل گروه‌های تو در تو نیز باشد. همچنین ممکن است شامل اطلاعاتی در مورد حالت اعتبارسنجی گروه به طور کلی باشد.

1const formGroup = new FormGroup({
2    'name': new FormControl('', [ Validators.required ]),
3    'email': new FormControl('', [ Validators.required ]),
4    'address': new FormGroup({
5        'zipCode': new FormControl(''),
6        'street': new FormControl(''),
7        'number': new FormControl('')
8    )
9});

برای نمونه در مثال فوق، حالت گروه فرم تنها زمانی معتبر خواهد بود که مشخصه‌های نام و ایمیل پر شده باشند. با ایجاد یک گروه فرم در کامپوننت می‌توانیم آن را به یک فرم در HTML متصل کنیم. مثال زیر روش انجام این کار را نشان می‌دهد:

1<form [formGroup]="formGroup" >
2  <h1>User Profile Form</h1>
3  <input required
4    type="text"
5    placeholder="Full Name"
6    formControlName="name"
7  />
8  <input required
9    type="text"
10    placeholder="E-mail"
11    formControlName="email"
12  />
13  <div formGroupName="address" >
14    <input required
15      type="text"
16      placeholder="Zipcode"
17      formControlName="zipcode"
18    />
19    <input required
20      type="text"
21      placeholder="Street"
22      formControlName="street"
23    />
24    <input required
25      type="number"
26      placeholder="Number"
27      formControlName="number"
28    />
29  </div>
30</form>

چنان که در مثال فوق می‌بینید، ابتدا فرم را به گروه فرم ایجادشده اتصال داده‌ایم. سپس برای هر ورودی یک گروه کنترل اختصاص داده‌ایم. از آنجا که یک گروه فرم تو در تو داریم، یک منطقه برای آن به نام formGroupName اختصاص می‌دهیم و اجازه می‌دهیم که انگولار بداند که کنترل‌های فرم در این ناحیه درون «گروه فرم تو در تو» قرار دارند.

خواندن از مقدار کنترل فرم

با استفاده از متد get در گروه‌های فرم می‌توان یک کنترل فرم خاص را انتخاب کرده و مقدار آن را خواند یا برخی از متدهایش را فراخوانی کرد. در ادامه مقدار یک کنترل فرم خوانده شده و سپس برخی متدهایی که می‌توان استفاده کرد مورد برسی قرار گرفته‌اند.

1const inputtedName = formGroup.get('name').value;
2const inputtedZipCode = formGroup.get('address.zipCode').value;

ضمناً در فایل HTML:

1<p>Inputted Name: {{ formGroup.get('name').value }}</p>
2<p>Inputted ZIP: {{ formGroup.get('address.zipCode').value }}</p>

آرایه فرم

«آرایه فرم» (Form Array) یک آرایه از کنترل‌های فرم و گروه‌های فرم و حتی آرایه‌های فرم دیگر است. این یک ساختار مقدماتی اما ضروری برای مدیریت آرایه‌ها با فرم‌های انگولار است. فرض کنید می‌خواهید شماره تلفن‌های کاربران را گردآوری کنید و به وی اجازه می‌دهید که یک شماره اولیه و سپس یک شماره دوم نیز وارد کند. بدین ترتیب می‌توانید آرایه فرم phones را در گروه فرم خود اعلان کنید:

1const formGroup = new FormGroup({
2    'phones': new FormArray([
3        new FormGroup({
4            'number': new FormControl('', [ Validators.required ]),
5            'type': new FormControl('Primary')
6        }),
7        new FormGroup({
8            'number': new FormControl(''),
9            'type': new FormControl('Secondary')
10        })
11    ])
12});

سپس نشانه‌گذاری (markup) را تنظیم کنید، و یک حلقه تعریف کنید که برای هر عنصر در آرایه فرم عمل می‌کند:

1<form [formGroup]="formGroup" >
2  <h1>User Phones</h1>
3  <div class="phones"
4    *ngFor="let phoneGroup of formGroup.get('phones')['controls'];
5            let i = index"
6    formArrayName="phones"
7  >
8    <ng-container [formGroupName]="i" >
9      <p>Phone Type: {{ phoneGroup.get('type').value }}</p>
10      <input
11        type="tel"
12        placeholder="Phone number"
13        formControlName="number"
14      />
15    </ng-container>
16  </div>
17</form>

به‌ روز رسانی مقادیر فرم

متدهای setValue و patchValue در فرم‌های واکنشی انگولار امکان تعیین مقادیر کنترل‌های گروه فرم را می‌دهد. هنگام استفاده از setValue باید یک شیء کامل ارائه کنید که به دنبال همه مشخصه‌های گروه فرم می‌گردد و مقادیر کنترل فرم را بازنویسی می‌کند. اگر یک شیء شامل همه مشخصه‌های گروه فرم عرضه نشود کار خواهد کرد. به مثال زیر توجه کنید:

1const formGroup = new FormGroup({
2    'name': new FormControl('', [ Validators.required ]),
3    'email': new FormControl('', [ Validators.required ]),
4    'address': new FormGroup({
5        'zipCode': new FormControl(''),
6        'street': new FormControl(''),
7        'number': new FormControl('')
8    )
9});
10const updatedUserInfo = {
11  'name': 'Leonardo Giroto',
12  'email': 'leonardo@email.com',
13  'address': {
14    'zipCode': '22630-010',
15    'street': 'Avenida Lucio Costa',
16    'number': 9999
17  }
18};
19formGroup.setValue( updatedUserInfo );

از سوی دیگر متد patchValue می‌تواند برای بازنویسی هر مجموعه‌ای از مشخصه‌ها که در گروه فرم تغییر یافته‌اند استفاده شود. از این رو می‌توانید تنها برخی از فیلدها را برای به‌روزرسانی مقادیر کنترل‌های فرم ارائه کنید.

در ادامه مثالی از کاربرد آن را می‌بینید که از آن برای به‌روزرسانی تنها بخش آدرس گروه فرم استفاده کرده‌ایم:

1const formGroup = new FormGroup({
2    'name': new FormControl('', [ Validators.required ]),
3    'email': new FormControl('', [ Validators.required ]),
4    'address': new FormGroup({
5        'zipCode': new FormControl(''),
6        'street': new FormControl(''),
7        'number': new FormControl('')
8    )
9});
10const newUserAddress = {
11  'zipCode': '01306-001',
12  'street': 'Rua Avanhandava',
13  'number': 999
14};
15formGroup.patchValue({
16  'address': newUserAddress
17});

باید توجه داشته باشید که متد setValue نه تنها برای گروه‌های فرم به صورت یک کلیت استفاده می‌شود بلکه روی کنترل‌های فرم نیز اعمال می‌شود. از این رو می‌توانید از آن برای به‌روزرسانی مقدار یک کنترل فرم منفرد با فراخوانی مستقیمش روی آن کنترل فرم به صورت زیر استفاده کنید:

1formGroup.get('email').setValue('new_email@email.com');

حالت اعتبارسنجی فرم

زمانی که گروه فرم با اعتبارسنج‌ها، چنان که پیش‌تر نشان دادیم، ایجاد می‌شود، شامل اطلاعاتی در مورد حالت اعتبارسنجی فرم خواهد بود که مطابق مقادیری است که کنترل‌های فرم نگهداری می‌کنند. برای نمونه اگر اعتبارسنج‌های required روی کنترل‌های فرم وجود داشته باشند، و همه مقادیر خالی باشند، فرم به صورت invalid تعیین می‌شود.

1const isFormValid = formGroup.valid;
2const isEmailValid = formGroup.get('email').valid;

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

قابلیت جالب دیگر گزینه manually set erros است. اگر بر حسب تصادف لازم باشد که گردش کاری خود را طوری تنظیم کنید که یک کنترل فرم صرف‌نظر از حالت اعتبارسنج‌ها نامعتبر شود، روش انجام آن به صورت زیر است:

1formGroup.get('email').setErrors({ 'incorrect': true });

این تغییر موجب به‌روزرسانی حالت اعتبارسنجی کنترل فرم می‌شود و به تبع آن حالت گروه‌های فرم نیز تغییر پیدا می‌کند. ضمناً می‌توانید خطاهای کنترل‌های فرم را نیز با null کردنشان به صورت زیر پاک کنید:

1formGroup.get('email').setErrors(null);

dirty و touched

احتمالاً دوست ندارید قبل از آن که به کاربر امکان ویرایش بدهید، خطایی در فرم نمایش دهید. بررسی وضعیت‌های dirty و touched از نمایش خطاها تا زمانی که یکی از دو اتفاق زیر نیفتاده است جلوگیری می‌کند: مقدار تغییر یابید، کنترل dirty شود یا عنصر کنترل فرم تار شود و کنترل به صورت touched تنظیم شود.

بررسی حالت‌های dirty و touched کنترل‌های فرم کاملاً سرراست است و به صورت زیر انجام می‌یابد:

1const isDirty = formGroup.get('email').dirty;
2const isTouched = formGroup.get('email').touched;

رصد تغییرات

یکی از جالب‌ترین قابلیت‌های فرم‌های واکنشی امکان «اشتراک» (subscribe) در تغییرات گروه فرم یا کنترل فرم است. ایده کار کاملاً ساده است. ما روی یک عنصر مشترک می‌شویم و هر بار که مقداری تغییر یابد یک تابع callback تحریک می‌شود که می‌توانیم منطقی برای آن تعیین کنیم. روش تعیین اشتراک به صورت زیر است:

1formGroup.valueChanges.subscribe((val) => {
2   // The form has changed. Insert here your logic!
3});

یک مثال عملی و مفید برای این قابلیت، حل مشکل یکپارچه‌سازی API جستجوی آدرس است. فرض کنید یک ورودی از کاربر دارید که کد پستی خود را ارائه کرده است. می‌توانید با افزودن subscription به کنترل مربوطه به محض این که کد پستی معتبری دریافت کردید API جستجوی آدرس را فراخوانی کنید و با استفاده از آن فیلدهای آدرس دیگر را که شامل نشانی، شهر، استان و غیره می‌شود را پر کنید:

1formGroup.get(
2  'address.zipCode'
3).valueChanges.subscribe(async (val) => {
4  const fullAddress = await _service.getFullAddress(val);
5  formGroup.get('address.street').setValue(fullAddress.street);
6});

اعتبارسنج‌های سفارشی

چنان که پیش‌تر اشاره کردیم امکان ایجاد اعتبارسنج‌های سفارشی برای مقاصد خاص وجود دارد. یک اعتبارسنج در واقع تابعی است که یک کنترل فرم را به عنوان پارامتر می‌گیرد و در صورتی که معتبر باشد، مقدار null و در صورتی که چنین نباشد، شیء خطا را بازگشت می‌دهد.

در ادامه مثالی از یک اعتبارسنج سفارشی می‌بینید. این اعتبارسنج برای این منظور ساخته شده است که بررسی کند آیا شماره CPF ارائه شده (سند شناسایی در برزیل) معتبر است یا نه.

1public static CPFValidator(control) {
2  if (!control || !control.value) return null;
3  return isCPFValid(control.value) ?
4    null : { 'invalidCnpj': 'invalid cpf' };
5}
6function isCPFValid(cpf) {
7  const regex = /[.-\s]/g;
8  let strCPF = cpf.replace(regex, '');
9  let Soma = 0;
10  let Resto;
11  for (let i = 1; i <= 9; i++) {
12    Soma = Soma + parseInt(strCPF.substring(i - 1, i)) * (11 - i);
13    Resto = (Soma * 10) % 11;
14  }
15  if ((Resto === 10) || (Resto === 11))
16    Resto = 0;
17  if (Resto !== parseInt(strCPF.substring(9, 10)))
18    return false;
19  Soma = 0;
20  for (let i = 1; i <= 10; i++) {
21    Soma = Soma + parseInt(strCPF.substring(i - 1, i)) * (12 - i);
22    Resto = (Soma * 10) % 11;
23  }
24  if ((Resto === 10) || (Resto === 11))
25    Resto = 0;
26  if (Resto !== parseInt(strCPF.substring(10, 11)))
27    return false;
28  return true;
29}

شیوه استفاده از آن در یک اعلان کنترل فرم نیز به صورت زیر است:

1const formGroup = new FormGroup({
2    'name': new FormControl('', [ Validators.required ]),
3    'cpf': new FormControl('',
4        [ Validators.required, CPFValidator ]
5    )
6})

فرم‌های واکنشی انگولار بسیار قدرتمند هستند و مستندات آن (+) بسیار کامل است. با این که برخی اوقات لازم است برای یافتن موارد مورد نظر در آن عمیق شویم، اما در اغلب موارد می‌توانید چیزی را که به دنبالش هستید به همراه مثال‌هایی پیدا کنید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
itnext
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *