Form — Reactive [Part 5]

Panusitt Khuenkham
Angular in Thailand
9 min readNov 9, 2017

> การจัดการฟอร์มด้วย Reactive

https://qph.ec.quoracdn.net/main-qimg-ef0e116da7fd48a80fe2a4695f545071

ความเดิมจากตอนที่แล้ววววววว [ตอนที่ 4]

หัวข้อที่เราจะสอนคร่าวๆ ก็จะมีดังนี้

  • What is Form — Reactive ?
  • Setup
  • Creating a Form in Code
  • Syncing HTML and Form
  • Submitting the Form
  • Adding Validation
  • Getting Access to Controls
  • Grouping Controls
  • Arrays of Form Controls (FormArray)
  • Outputting registering data
  • Resetting Forms

What is Form — Reactive?

>> Form — Reactive คืออะไร ?

แบบฟอร์มที่ถูกสร้างขึ้นโดยทางโปรแกรมและเป็น form ที่ซิงโครไนซ์(synchronized)กับ DOM โดยตรง

Setup

>> ติดตั้งและตรวจสอบให้พร้อมทำงาน

*ผมจะตัวอย่างเหมือนกับ Part 4 เพื่อไม่ให้เสียเวลา

Step 1 : สร้าง component ใหม่ชื่อว่า students-react-form

ng g c students-react-form --spec false

Step 2 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

<h3>Registering Student</h3>
<div class="row">
<div class="col-md-5">
<form>
<div id="user-data">
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text"
id="fullName"
class="form-control">
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
class="form-control">
</div>
<div class="form-group">
<div class="radio">
<label>
<input type="radio"
id="sexMale"
value="Male">
ชาย
</label>
<label>
<input type="radio"
id="sexFemale"
value="Female">
หญิง
</label>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text"
id="username"
class="form-control">
</div>
<button class="btn btn-default"
type="button">Suggest an Username</button>
<hr>
</div>
<div class="form-group">
<label for="secret">Secret Questions</label>
<select id="secret"
class="form-control">
<option value="pet">สัตว์เลี้ยงตัวแรกของคุณ?</option>
<option value="teacher">ครูคนแรกของคุณ?</option>
<option value="car">รถคันแรกของคุณ?</option>
</select>
</div>
<button class="btn btn-primary"
type="submit">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> ลงทะเบียน
</button>
</form>
</div>
<div class="col-md-7">
<h5>Output Data:</h5>
<hr>
<p>Full Name: </p>
<p>Username: </p>
<p>E-Mail: </p>
<p>Sex: </p>
<p>Secret: </p>
</div>
</div>
<hr>

Step 3 : เปิดไฟล์ app.module.ts แล้วเขียนโค้ดดังนี้

(1) เช็ค importimport {FormsModule, ReactiveFormsModule} from '@angular/forms';เพิ่ม ReactiveFormsModule ในส่วนของ imports: []*จำเป็นเพราะจะต้องใช้งาน Module นี้---------------------------------------------------(2) เพิ่ม Routingเพิ่ม {path: 'student-system-react-form', component: StudentsReactFormComponent}, ในส่วนของ RouterModule.forRoot([])*จำเป็นเพราะจะต้องใช้งาน Module นี้

Step 4 : เปิดไฟล์ app.component.html แล้วเขียนโค้ดดังนี้

เพิ่มเมนู<li routerLinkActive="active">
<a [routerLink]="['student-system-react-form']">
Student System Reactive
</a>
</li>

Step 5 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

สร้างตัวแปร
registerForm: FormGroup;
*อย่าลืม
import
{FormGroup} from '@angular/forms';

แล้วมารันดูผลลัพธ์กัน

เรียบร้อย พร้อมแล้ว ลุยต่อออออออออออ ^^

Creating a Form in Code

>> การสร้างฟอร์มด้วยโค้ด (.ts)

Step 6 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

ngOnInit() {
this.registerForm = new FormGroup({
'secret': new FormControl('car'),
'email': new FormControl(null),
'fullName': new FormControl(null),
'sex': new FormControl('Male'),
'username': new FormControl(null),
});
}
*อย่าลืม
import {FormControl, FormGroup} from '@angular/forms';

อธิบายเพิ่มเติม

*ควรเริ่มต้นทำงานก่อนที่จะทำเทมเพลต

ศึกษาเพิ่มเติมได้ที่ Lifecycle Hooks

Syncing HTML and Form

>> การซิงค์ HTML และแบบฟอร์ม

Step 7 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

(นอกสุด)FormGroup (ควบคุม)>>>> FormControl | FormArray
new FormControl(--set value default--)ตัวอย่างเช่น
'sex': new FormControl('Male') // set value default ไว้ที่ Male
หรือ
'fullName': new FormControl(null) // ไม่ต้องเซ็ตค่าอะไร

ลองมารันดูผลลัพธ์กัน

Submitting the Form

>> การส่งและการใช้แบบฟอร์ม

Step 8 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

(ngSubmit)="onSubmit()"

Step 9 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

onSubmit() {
console.log(this.registerForm);
}

มาดูผลลัพธ์กัน โดยการทดสอบกรอกข้อมูลแล้ว กดปุ่มลงทะเบียน

จะเห็นได้ว่า ค่า value จะเป็น Object

และค่าภายใน Object ก็คือ Object FormControl ที่เรา new ขึ้นมานั่นเอง

Adding Validation

>> การเพิ่มการตรวจสอบ

Step 10 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

this.registerForm = new FormGroup({
'secret': new FormControl('car'),
'email': new FormControl(null, [
Validators.required,
Validators.email]),
'fullName': new FormControl(null, Validators.required),
'sex': new FormControl('Male'),
'username': new FormControl(null),
});
ใน FormControl พารามิเตอร์ ตัวที่ 2 จะเป็นการตรวจสอบ โดยดังนี้
หากตรวจสอบพื้นฐานปกติ ก็ใส่แค่ Validators.required
*แต่!! หากมีการตรวจสอบมากกว่า 1 การตรวจสอบ
สามารถเขียนเป็นลักษณะของ Array ได้ [Validators.required, Validators.email]

มาดูผลลัพธ์กัน

จะเห็นได้ว่า Full Name ถูก Validate อยู่ โดยสังเกตุได้จาก Class ng-invalid

แต่หากกรอกข้อมูลลงไป

จะเห็นได้ว่า Full Name เป็น Class เป็น ng-valid

ทีนี้ก็สามารถนำไปประยุกต์ได้แล้ววววววววววววววววววววววว

Getting Access to Controls

>> การเข้าถึงตัวควบคุม

Step 11 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

<span
*ngIf="!registerForm.get('fullName').valid && registerForm.get('fullName').touched"
class="help-block">Please enter a valid Full Name!</span>
---------------------------------
*registerForm.get('--formControlName--').valid
หากเป็น template-driven
เราจะต้องทำ #fullname="ngModel" อะไรแบบนี้
เป็นไงบ้างครับ รู้สึกเขียนง่ายขึ้นบ้างไหม^^ very easy

มารันดูผลลัพธ์กัน

> ลองคลิกที่ช่อง Full Name ไม่ต้องกรอกอะไร จากนั้นไปคลิกที่อื่น

span tag — help-block จะแสดงขึ้นมาทันทีเมื่อเราไม่กรอก ^^

ทีนี้เรามาเพิ่ม span tag — help-block ทุกที่เลย แล้วก็ใส่ css border ให้สวยหรูหน่อย

Step 12 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

<h3>Registering Student</h3>
<div class="row">
<div class="col-md-5">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div id="user-data">
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text"
id="fullName"
formControlName="fullName"
class="form-control">
<span
*ngIf="!registerForm.get('fullName').valid && registerForm.get('fullName').touched"
class="help-block">Please enter a valid Full Name!</span>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
formControlName="email"
class="form-control">
<span
*ngIf="!registerForm.get('email').valid && registerForm.get('email').touched"
class="help-block">Please enter a valid E-Mail!</span>
</div>
<div class="form-group">
<div class="radio">
<label>
<input type="radio"
id="sexMale"
value="Male"
formControlName="sex">
ชาย
</label>
<label>
<input type="radio"
id="sexFemale"
value="Female"
formControlName="sex">
หญิง
</label>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text"
id="username"
class="form-control"
formControlName="username">
<span
*ngIf="!registerForm.get('username').valid && registerForm.get('username').touched"
class="help-block">Please enter a valid Username!</span>
</div>
<button class="btn btn-default"
type="button">Suggest an Username
</button>
<hr>
</div>
<div class="form-group">
<label for="secret">Secret Questions</label>
<select id="secret"
class="form-control"
formControlName="secret">
<option value="pet">สัตว์เลี้ยงตัวแรกของคุณ?</option>
<option value="teacher">ครูคนแรกของคุณ?</option>
<option value="car">รถคันแรกของคุณ?</option>
</select>
</div>
<span *ngIf="!registerForm.valid && registerForm.touched"
class="help-block">Please enter a valid</span>
<button
class="btn btn-primary"
type="submit"
[disabled]="!registerForm.valid">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> ลงทะเบียน
</button>
</form>
</div>
<div class="col-md-7">
<h5>Output Data:</h5>
<hr>
<p>Full Name: </p>
<p>Username: </p>
<p>E-Mail: </p>
<p>Sex: </p>
<p>Secret: </p>
</div>
</div>
<hr>

Step 13 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

>>> เพิ่ม validators.required ตรง username ด้วย

'username': new FormControl(null, Validators.required),

Step 14 : เปิดไฟล์ students-react-form.component.css แล้วเขียนโค้ดดังนี้

input.ng-invalid.ng-touched {
border: 1px solid red;
}

แล้วมารันดูผลลัพธ์กัน

ได้แล้วววววว ลองไปประยุกต์ต่อเลยนะครับ

Grouping Controls

>> การจัดกลุ่มการควบคุม

Step 15 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

จากเดิม

เปลี่ยนแปลงเป็น

ตอนนี้สิ่งที่เราจะทำก็คือ การจัด Group ข้อมูล ใช้ชื่อว่า userData

Step 16 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

>> 1) สร้าง div tag ขึ้นมาครอบ formControlName ที่เราจะ Group
>> 2) เดิม registerForm.get('fullName') ที่นี้เราทำ Group แล้ว
ก็ต้องเขียนแบบนี้ registerForm.get('userData.fullName')
เพราะว่า fullName อยู่ภายใต้ userData อีกที ดังรูปด้านล่าง
<h3>Registering Student</h3>
<div class="row">
<div class="col-md-5">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div formGroupName="userData">
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text"
id="fullName"
formControlName="fullName"
class="form-control">
<span
*ngIf="!registerForm.get('userData.fullName').valid && registerForm.get('userData.fullName').touched"
class="help-block">Please enter a valid Full Name!</span>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
formControlName="email"
class="form-control">
<span
*ngIf="!registerForm.get('userData.email').valid && registerForm.get('userData.email').touched"
class="help-block">Please enter a valid E-Mail!</span>
</div>
<div class="form-group">
<div class="radio">
<label>
<input type="radio"
id="sexMale"
value="Male"
formControlName="sex">
ชาย
</label>
<label>
<input type="radio"
id="sexFemale"
value="Female"
formControlName="sex">
หญิง
</label>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text"
id="username"
class="form-control"
formControlName="username">
<span
*ngIf="!registerForm.get('userData.username').valid && registerForm.get('userData.username').touched"
class="help-block">Please enter a valid Username!</span>
</div>
<button class="btn btn-default"
type="button">Suggest an Username
</button>
<hr>
</div>
<div class="form-group">
<label for="secret">Secret Questions</label>
<select id="secret"
class="form-control"
formControlName="secret">
<option value="pet">สัตว์เลี้ยงตัวแรกของคุณ?</option>
<option value="teacher">ครูคนแรกของคุณ?</option>
<option value="car">รถคันแรกของคุณ?</option>
</select>
</div>
<span *ngIf="!registerForm.valid && registerForm.touched"
class="help-block">Please enter a valid</span>
<button
class="btn btn-primary"
type="submit"
[disabled]="!registerForm.valid">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> ลงทะเบียน
</button>
</form>
</div>
<div class="col-md-7">
<h5>Output Data:</h5>
<hr>
<p>Full Name: </p>
<p>Username: </p>
<p>E-Mail: </p>
<p>Sex: </p>
<p>Secret: </p>
</div>
</div>
<hr>

มาดูผลลัพธ์กัน

Arrays of Form Controls (FormArray)

>> อาร์เรย์ของตัวควบคุมฟอร์ม (FormArray)

Step 17 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

ngOnInit() {
this.registerForm = new FormGroup({
'userData': new FormGroup({
'email': new FormControl(null, [
Validators.required,
Validators.email]),
'fullName': new FormControl(null, Validators.required),
'sex': new FormControl('Male'),
'username': new FormControl(null, Validators.required),
}),
'secret': new FormControl('car'),
'hobbies': new FormArray([])
});
}

onAddHobby() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.registerForm.get('hobbies')).push(control);
}
getHobbies() {
return (<FormArray>this.registerForm.get('hobbies')).controls;
}

Step 18 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

<h3>Registering Student</h3>
<div class="row">
<div class="col-md-5">
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
<div formGroupName="userData">
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text"
id="fullName"
formControlName="fullName"
class="form-control">
<span
*ngIf="!registerForm.get('userData.fullName').valid && registerForm.get('userData.fullName').touched"
class="help-block">Please enter a valid Full Name!</span>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
formControlName="email"
class="form-control">
<span
*ngIf="!registerForm.get('userData.email').valid && registerForm.get('userData.email').touched"
class="help-block">Please enter a valid E-Mail!</span>
</div>
<div class="form-group">
<div class="radio">
<label>
<input type="radio"
id="sexMale"
value="Male"
formControlName="sex">
ชาย
</label>
<label>
<input type="radio"
id="sexFemale"
value="Female"
formControlName="sex">
หญิง
</label>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text"
id="username"
class="form-control"
formControlName="username">
<span
*ngIf="!registerForm.get('userData.username').valid && registerForm.get('userData.username').touched"
class="help-block">Please enter a valid Username!</span>
</div>
<button class="btn btn-default"
type="button">Suggest an Username
</button>
<hr>
</div>
<div class="form-group">
<label for="secret">Secret Questions</label>
<select id="secret"
class="form-control"
formControlName="secret">
<option value="pet">สัตว์เลี้ยงตัวแรกของคุณ?</option>
<option value="teacher">ครูคนแรกของคุณ?</option>
<option value="car">รถคันแรกของคุณ?</option>
</select>
</div>
<div class="form-group">
<div formArrayName="hobbies">
<label>Your Hobbies</label>
<div class="form-group">
<button
class="btn btn-default"
type="button"
(click)="onAddHobby()">Add Hobby</button>
</div>

<div class="form-group"
*ngFor="let hobby of getHobbies(); let i = index">
<input type="text" class="form-control" [formControlName]="i">
</div>
</div>
</div>
<span *ngIf="!registerForm.valid && registerForm.touched"
class="help-block">Please enter a valid</span>
<button
class="btn btn-primary"
type="submit"
[disabled]="!registerForm.valid">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> ลงทะเบียน
</button>
</form>
</div>
<div class="col-md-7" *ngIf="submitted">
<h5>Output Data:</h5>
<hr>
<p>Full Name: {{ user.userData.fullName }}</p>
<p>Username: {{ user.userData.username }}</p>
<p>E-Mail: {{ user.userData.email }}</p>
<p>Sex: {{ user.userData.sex }}</p>
<p>Secret: {{ user.secret }}</p>
<p>Your hobbies: {{ user.hobbies }}</p>
</div>
</div>
<hr>

แล้วมารันดูผลลัพธ์กัน

Outputting registering data

>> การแสดงข้อมูลการลงทะเบียน

Step 18 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

// ประกาศตัวแปร
user = {
secret: '',
userData: {
email: '',
fullName: '',
sex: '',
username: '',
},
hobbies: []
};
submitted = false;
// ฟังก์ชั่น onSubmit
onSubmit() {
this.submitted = true;

const userData = this.user.userData;
const registerForm = this.registerForm.value;

this.user.secret = registerForm.secret;
this.user.hobbies = registerForm.hobbies;
userData.email = registerForm.userData.email;
userData.fullName = registerForm.userData.fullName;
userData.sex = registerForm.userData.sex;
userData.username = registerForm.userData.username;
}

Step 19 : เปิดไฟล์ students-react-form.component.html แล้วเขียนโค้ดดังนี้

<div class="col-md-7" *ngIf="submitted">
<h5>Output Data:</h5>
<hr>
<p>Full Name: {{ user.userData.fullName }}</p>
<p>Username: {{ user.userData.username }}</p>
<p>E-Mail: {{ user.userData.email }}</p>
<p>Sex: {{ user.userData.sex }}</p>
<p>Secret: {{ user.secret }}</p>
<p>Your hobbies: {{ user.hobbies }}</p>
</div>

มาดูผลลัพธ์กัน

Resetting Forms

>> การรีเซ็ตฟอร์ม

สิ่งที่เราจะทำตอนนี้ก็คือ หลังจากกดลงทะเบียนแล้ว ให้รีเซ็ตฟอร์ม ใหม่ด้วย

Step 20 : เปิดไฟล์ students-react-form.component.ts แล้วเขียนโค้ดดังนี้

onReset() {
this.registerForm.reset();
this.registerForm.patchValue({
userData: {
sex: 'Male'
},
secret: 'car'
});
const x = <FormArray>this.registerForm.controls.hobbies;
x.controls = [];
}

แล้วมาดูผลลัพธ์กัน

>> ก่อนกดลงทะเบียน

>> หลังจากกดลงทะเบียน

— — — — — — — -^^ — — — — — — — — :P.

ถ้าให้ผมแนะนำ ผมขอแนะนำให้ใช้ Reactive Form นะครับ

แล้วก็ได้สอนการสร้าง Reactive Form ให้กับนักเรียน (พี่ AkeDev) ของเราด้วย

บทความนี้อาจจะดูเยอะไปหน่อย แต่ก็ขอขอบคุณที่ท่านอ่านจนจบ

หวังว่าเป็นบทความที่มีความรู้ และสามารถพาท่านเรียนรู้ได้จนเข้าใจ

ตัวอย่างโค้ดโปรเจค ดูได้ที่นี่_CLICK

Reference:
https://angular.io
https://cli.angular.io
https://github.com/angular/angular-cli
https://angular.io/guide/reactive-forms
https://angular.io/api/forms/Validators

หากมีข้อผิดพลาดประการใดต้องขออภัยมา ณ ที่นี้ด้วยนะครับ
Thank you so much.

--

--