Form — Template-Driven [Part 4]

Panusitt Khuenkham
Angular in Thailand
9 min readOct 8, 2017

> การจัดการฟอร์มด้วย Template-Driven

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

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

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

  • What is Form — Template-Driven ?
  • Creating design the Form registering
  • Submit and Using the Form (NgForm)
  • Form state
  • Accessing the Form with @ViewChild
  • Adding Validation to check User Input
  • Validation to check User Input by Form state
  • Outputting Validation Error Messages
  • Set Default Values with ngModel Property Binding
  • Grouping Form Controls
  • Setting and Patching Form Values
  • Outputting registering data
  • Resetting Forms

What is Form — Template-Driven ?

>> Form — Template-Driven คืออะไร ?

ถ้าแปลตรงๆ ก็คือการ ขับเคลื่อน Form ด้วย Template

แล้วใช้ ngModel เพื่อสร้างการผูกข้อมูล

ใน Form — Template-Driven เราสามารถกำหนดพฤติกรรม / การตรวจสอบความถูกต้องโดยใช้คำสั่งของ Angular และแอตทริบิวต์ในเทมเพลต

เรามาเริ่มกันเลยดีกว่า เพื่อให้เห็นภาพชัดเจนขึ้น

Creating design the Form registering

>> การสร้างฟอร์มลงทะเบียน

เรามาช่วยกัน Design ฟอร์มแบบเบสิก สำหรับลงทะเบียนกัน

ขาดเหลืออะไรค่อยไปต่อยอดเอาเองนะค้าบ :P

Step 1 : เปิดไฟล์ students.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" name="sex" id="sexMale" value="Male" checked>
ชาย
</label>
<label>
<input type="radio" name="sex" 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>
</div>
</div>
<hr>

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

เย้ๆ เมื่อเราได้ฟอร์มสำหรับลงทะเบียนที่ต้องการแล้วให้เรามาลุยกันต่อเลย

*Important!!! ตรวจสอบก่อนว่ามีการ Import หรือยัง

Step 2 : เปิดไฟล์ app.module.ts แล้วตรวจสอบดังนี้

Step 3 : ทีนี้เราจะต้องผูก element ให้ Angular Form มันรู้จักก่อนโดยที่

  • ตั้งชื่อให้กับ element (Attribute name)
  • เพิ่มคำสั่ง ngModel ภายใน element

***ngModel น่าจะคุ้นๆ นะครับ ผมพูดไว้ในเรื่องของ Databinding

ตัวอย่าง

// จากเดิม
<input type="text" id="fullName" class="form-control">
// เปลี่ยนแปลงเป็น
<input type="text"
id="fullName"
class="form-control"
name="fullName"
ngModel>

ก็ไปไล่ทำแบบนี้ให้ครบทุกๆ element เลยนะครับ

<h3>Registering Student</h3>
<div class="row">
<div class="col-md-5">
<form (ngSubmit)="onSubmit(f)" #f >
<div id="user-data">
<div class="form-group">
<label for="fullName">Full Name</label>
<input type="text"
id="fullName"
class="form-control"
name="fullName"
ngModel>
</div>
<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
class="form-control"
name="email"
ngModel>
</div>
<div class="form-group">
<div class="radio">
<label>
<input type="radio"
id="sexMale"
value="Male"
checked
name="sex"
ngModel>
ชาย
</label>
<label>
<input type="radio"
id="sexFemale"
value="Female"
name="sex"
ngModel>
หญิง
</label>
</div>
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text"
id="username"
class="form-control"
name="username"
ngModel>
</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"
name="secret"
ngModel>
<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>
</div>
</div>
<hr>

Submitting and Using the Form

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

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

<form (ngSubmit)="onSubmit(f)" #f="ngForm">(ngSubmit) = คำสั่ง Event onSubmitonSubmit(f) = Function onSubmit(f) โดยที่รับพารามิเตอร์ประเภท ngForm#f="ngForm" ก็คือ อ้างอิง form นี้ที่ชื่อว่า f ที่อยู่ในรูปของ Object NgForm

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

import { NgForm } from '@angular/forms/src/directives';onSubmit(form: NgForm) {
console.log(form);
}

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

ลองกดปุ่ม ลงทะเบียน เราก็จะได้ Object ของ NgForm แล้ว

ไหนนนนนนๆๆๆๆ ลองเปิดดู value กัน

ซึ่ง value ก็จะอยู่ในรูปของ Object เช่นกัน

!!??? แล้วค่าของ Object value มาได้ยังไง

คำตอบก็คือ >> มาจากที่เราได้ทำการ binding ข้อมูลไว้ ใน Step ที่ 3 ไงครับ

Form state

>> สถานะของฟอร์ม

เรามาดูกันว่า NgForm มี State อะไรให้เราเล่นได้บ้างกับฟอร์ม

มีไว้เพื่ออะไร ??!! ก็เผื่อว่า อนาคตได้เอาไปใช้กับ Logic อะไรบางอย่างไงฮะ

มาเริ่มมมมมมมมกัน

dirty    = สกปรก/เลอะdisabled = ก็คือ disable ไม่ใช้กรอก ไม่ให้กด อะไรแบบนี้pristine = ดั้งเดิม/แรกเริ่มtouched  = สัมผัส/แตะต้องvalid    = ถูกต้อง***โดยที่ทุกอย่างจะบอกในรูปของ Boolean >> true/false

เราลองมาดูกันว่ามันทำงานยังไง

ทดสอบรอบที่ 1 >> หลังจากรันโปรแกรมเสร็จ ให้กดปุ่ม ลงทะเบียน

ผลลัพท์ของสถานะที่ได้ก็คือ

dirty    = false
//false เพราะว่าฟอร์มนี้เรายังไม่ได้ยุ่งอะไรกับมันซึ่งก็เปรียบเสมือนว่ามันยังสะอาดอยู่
disabled = false
//false เพราะว่าฟอร์มนี้ไม่มีการ disabled
pristine = true
//true เพราะว่าฟอร์มนี้ยังอยู่เหมือนเดิมเหมือนต้นฉบับ เป๊ะๆ ไม่ได้ทำอะไรเลย
touched = false
//false เพราะว่าฟอร์มนี้ยังไม่ถูกสัมผัส
valid = true
//true เพราะว่าฟอร์มนี้ถูกต้องทุกอย่างเนื่องจากไม่มีการ Validate

ทดสอบรอบที่ 2 >> หลังจากรันโปรแกรมเสร็จ
ให้คลิกที่ช่อง Input Full Name(ไม่ต้องกรอกอะไรนะ) แล้วกดปุ่ม ลงทะเบียน

ผลลัพท์ของสถานะที่ได้ก็คือ

dirty    = false
//false เพราะว่าฟอร์มนี้เรายังไม่ได้ยุ่งอะไรกับมันซึ่งก็เปรียบเสมือนว่ามันยังสะอาดอยู่
disabled = false
//false เพราะว่าฟอร์มนี้ไม่มีการ disabled
pristine = true
//true เพราะว่าฟอร์มนี้ยังอยู่เหมือนเดิมเหมือนต้นฉบับ เป๊ะๆ ไม่ได้ทำอะไรเลย
*touched = true
//true เพราะว่าฟอร์มนี้ถูกสัมผัสไปแล้ว จังหวะที่เราคลิก Input นั่นไงค้าบ
valid = true
//true เพราะว่าฟอร์มนี้ถูกต้องทุกอย่างเนื่องจากไม่มีการ Validate

ทดสอบรอบที่ 3 >> หลังจากรันโปรแกรมเสร็จ
ให้กรอกค่าอะไรก็ได้ในช่อง Input Full Name แล้วกดปุ่ม ลงทะเบียน

ผลลัพท์ของสถานะที่ได้ก็คือ

*dirty    = true
//true เพราะว่าฟอร์มนี้มีการเขียน/กรอกแล้วซึ่งก็เปรียบเสมือนว่ามันเลอะแล้วนะแบบนี้
disabled = false
//false เพราะว่าฟอร์มนี้ไม่มีการ disabled
*pristine = false
//false เพราะว่าฟอร์มนี้มีการเปลี่ยนแปลงไปแล้วซึ่งมันก็สัมพันกับ dirty นั่นเอง
*touched = true
//true เพราะว่าฟอร์มนี้ถูกสัมผัสไปแล้ว จังหวะที่เราคลิก Input นั่นไงค้าบ
valid = true
//true เพราะว่าฟอร์มนี้ถูกต้องทุกอย่างเนื่องจากไม่มีการ Validate

แต่ละรอบจะเห็นว่า สถานะเปลี่ยนแปลงไปในบางตัว ขึ้นอยู่กับการใช้งาน

มาถึงตอนนี้พอจะเริ่มเข้าใจ Form state กันบ้างแล้วนะจ้ะ^^

หลักๆ ก็จะมีประมาณนี้ครับ ซึ่งถ้าถามว่าแล้วเอาไปทำอะไร

ก็คิดซะว่า เผื่อได้ใช้มันละกันครับในอนาคต …. อิอิ

Accessing the Form with @ViewChild

>> การเข้าถึงฟอร์มด้วย @ViewChild

ซึ่งก่อนหน้านี้ เราเข้าถึงฟอร์มโดยส่งพารามิเตอร์ NgForm เข้ามา
พร้อมกับ Event onSubmit ใน Step ที่ 4

ทีีนี้เราลองเปลี่ยนมาใช้คำสั่ง @ViewChild ว่ามันใช้งานยังไง

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

// จากเดิม เราส่งค่าพารามิเตอร์ f
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
// เปลี่ยนแปลงเป็น
<form (ngSubmit)="onSubmit()" #f="ngForm">

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

@ViewChild('f') registerForm: NgForm;onSubmit() {
console.log(this.registerForm);
}
// อธิบายเพิ่มเติม
@ViewChild(reference element หรือ #) ชื่อตัวแปร: ประเภทตัวแปร;

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

จะเห็นว่าเราสามารถเข้าถึงฟอร์มได้ด้วยคำสั่ง @ViewChild

เผื่อว่าอนาคตอยากใช้หลายๆ ฟอร์มใน Event เดียว

ซึ่งต่างจาก Step 4 ที่ต้องส่งค่าพารามิเตอร์เข้ามาพร้อมกับ Event function

คำถามแล้วจะใช้แบบไหนดี??! จะใช้แบบไหนก็ได้ครับ ขึ้นอยู่กับสถานการณ์นั้นๆ

Adding Validation to check User Input

>> เพิ่มการตรวจสอบความถูกต้องเพื่อตรวจสอบ User Input

หรือการทำ Validate นั่นเอง

เรามาลองทำการ Validate ช่องกรอก Full Name และ E-Mail กัน

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

เพิ่ม Attribute required ใน element

หลังจากเพิ่มแล้วเราลองมาดู Form state กัน จำได้อยู่ไหมน๊าาาาา ???

รันเสร็จกดปุ่ม ลงทะเบียน เลยครับ

จะเห็นว่า Form state valid = false

เพราะว่า Form นี้มีการตรวจสอบความถูกต้องนั่นเอง

เอาหล่ะทีนี้เราก็จะเอา Form state ที่เราเรียนมาก่อนหน้า มาลองประยุกต์บน UI ดู

ก่อนอื่นอ่านตรงนี้สักนิ๊ดดดดดดดดดดดดด

คำสั่งที่ใช้ได้กับ Form Template-Driven

สามารถตรวจสอบได้จากเว็บของ Angular เลยครับ

https://angular.io/api?type=directive

ตรวจสอบคลาส Validators

https://angular.io/api/forms/Validators

อย่างตอนนี้ผมได้ลองเอา email มาประยุกต์ใช้งาน

Validation to check User Input by Form state

>> การตรวจสอบความถูกต้องด้วยประยุกต์เข้ากับ Form state

มาเริ่มกันเลย

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

เพิ่ม disabled ใน button เพื่อจะให้กดได้ ก็ต่อเมื่อข้อมูลถูกต้อง

<button class="btn btn-primary"
type="submit"
[disabled]="!f.valid">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> ลงทะเบียน
</button>

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

สังเกตุว่าเมื่อโปรแกรมรันมาเริ่มแรก ปุ่มลงทะเบียน จะถูก Disabled ไว้

ต่อไปลองกรอกข้อมูลช่อง Full Name และ E-Mail ให้ถูกต้อง

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

ทีนี้เรามาเพิ่ม CSS Style ให้มันดูดีกว่านี้หน่อย

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

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

หลังจากนั้นมาลองดูผลลัพธ์กัน

จะเห็นว่าเราได้เพิ่ม Styte เข้าไป เพื่อให้แสดงกรอบสีแดงเมื่อกรอกไม่ถูกต้อง

หากกรอกถูกต้องแล้ว กรอบสีแดงก็จะหายไป ปุ่มลงทะเบียน ก็จะสามารถกดได้

แล้ว Class .ng-invalid.ng-touched มาจากไหน ตรง angular จะเพิ่มให้เอง ถ้ามีการกระทำอะไรสักอย่างกัน form

จริงๆ สามารถกด F12 ดูได้

>> ตอน run โปรแกรมแล้ว ใหม่ๆ สดๆ ร้อนๆ

>> หลังมีการตรวจสอบ หรือตอนที่มีการ Validate

เมื่อกี้ผมทำการคลิกที่ช่อง Full Name แล้วก็ไม่ได้กรอกอะไร มันก็เลยขึ้น Validate ว่า ช่องนี้ จำเป็นต้องกรอกนะ เพราะคุณ สัมผัส หรือ touched ไปแล้วววว

สังเกตุว่าตอนแรก มันเป็น class untouched พอเราคลิกปุ๊บ มันก็จะเปลี่ยนเป็น touched ซึ่งตรง class พวกนี้เอง เราเลยนำไปประยุกต์ใช้กับการ Validate นั่นเอง

Outputing Validation Error Messages

>> การแสดงข้อความยืนยันความผิดพลาด

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

<div class="form-group">
<label for="email">E-Mail</label>
<input type="email"
id="email"
class="form-control"
name="email"
ngModel
required
email
#email="ngModel">
<span class="help-block" *ngIf="!email.valid && email.touched">Please enter a valid email!</span>
</div>

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

ก็จะมีข้อความขึ้นมาแสดงหากกรอกไม่ถูกต้อง

แต่ถ้ากรอกถูกต้องข้อความก็จะหายไป

Set Default Values with ngModel Property Binding

>> Set default ให้กับ element ด้วยการผูกคุณสมบัติ ngModel

อย่างตอนนี้จะเห็นว่าเรา default checked ไว้ที่เพศชาย

แต่ผลลัพธ์กลับไม่ default ให้ ดังภาพด้านล่าง

นั่นหมายความว่าเรา default แบบนั้นไม่ได้

เราจึงต้องมาใช้ ngModel ในการผูกข้อมูล ให้ตัว Angular Form มันรู้จัก

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

defaultSex = 'Male';
defaultSecret = 'pet';

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

// เขียนเพิ่มเติมดังรูปด้านบน[ngModel]="defaultSex"[ngModel]="defaultSecret"

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

โหววววววววว ทำไมง่ายขนาดนี้เนี้ยยยยยยยยยย เย้ๆๆๆๆๆ

Grouping Form Controls

>> การจัดกลุ่มข้อมูลฟอร์ม

ตอนนี้ถ้าเรากดปุ่ม ลงทะเบียน เราจะได้ข้อมูล value แบบนี้

value: Object {
email: "test@test.com",
fullName: "bamossza",
secret: "pet",
sex: "Male",
username: ""
}

แต่ผมต้องการจัดกลุ่มข้อมูลใหม่แบบนี้…

value: Object {
secret: "pet"
userData: {
email: "test@test.com",
fullName: "bamossza",
username: "",
sex: "Male"
}
}

มาเริ่มกันนนนนนนนนนนนนนเลย^^

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

<div id="user-data" ngModelGroup="userData">

*โดย element ที่ต้องการ Group จะต้องภายใต้ ngModelGroup

<div id="user-data" ngModelGroup="userData">// element ที่ต้องการ group >> ngModel</div>

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

เพียงเท่านี้ เราก็จะได้ข้อมูลที่จัดกลุ่มพร้อมใช้งานแล้ววววววค้าบบบ

Setting and Patching Form Values

>> การตั้งค่าและการแก้ไขค่าของฟอร์ม

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

suggestUserName () {
const suggestedName = 'Superuser';
this.registerForm.setValue({
secret: 'car',
userData: {
email: '',
fullName: '',
sex: 'Male',
username: suggestedName,
}
});
}

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

<button class="btn btn-default"
(click)="suggestUserName()"
type="button">Suggest an Username</button>

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

>> หลังจากรันโปรแกรมเสร็จให้กดปุ่ม Suggest an Username

หรือจะอีกวิธี

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

this.registerForm.form.patchValue({
userData: {
username: suggestedName
}
});

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

>> หลังจากรันโปรแกรมเสร็จให้กดปุ่ม Suggest an Username

อธิบายโปรแกรมเพิ่มเติม

ทั้ง 2 คำสั่งนี้ต่างกันตรงที่....// เซ็ตค่าให้กับทุก element >> ngModel >> ทุกตัว
this.registerForm.setValue({});
// เซ็ตค่าให้กับบาง element >> ngModel >> บางตัว
this.registerForm.form.patchValue({});
***โดยใช้วิธีการเข้าถึงฟอร์มด้วย @ViewChild นั่นเองงงงงงงค้าบบบ

นี่ไงค้าบบบบบ เผื่อสงสัยว่า this.registerForm มาจากไหนนนนน^^แฮ่

ลุยกันต่อออออ อีก 2 หัวข้อ ใกล้จบแล้ววววววจ้าาา

Outputting registering data

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

โจทย์ก็คือ เมื่อกดปุ่ม ลงทะเบียน แล้ว ให้แสดงข้อมูลตรงนี้……..

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

user = {
secret: '',
userData: {
email: '',
fullName: '',
sex: '',
username: '',
}
};
submitted = false;
onSubmit() {
this.submitted = true;

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

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

Step 18 : เปิดไฟล์ students.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>
</div>

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

>> หลังจากรันโปรแกรมเสร็จลองกรอกข้อมูลแล้วกดปุ่ม ลงทะเบียน

ใกล้จบแล้ววววววววววววววโว้ยยยยยยยยยยยยยย ป่ะลุยๆๆๆๆๆๆๆๆๆๆ

Resetting Forms

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

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

ก็คืองี้ หลังจากที่กด ลงทะเบียน เรียบร้อยแล้ว ให้ reset form ด้วย

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

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

>> หลังกดปุ่ม ลงทะเบียน

สังเกตุว่า Form จะถูก Reset ใหม่หมดเลย ไม่มีแม้กระทั่งการ Set Default

*** แต่ถุงงงง เฮ้ยย!! ถึงกระนั้น หากเราต้องการ Set Default ก็สามารถทำได้

โดยการนำคำสั่ง setValue({}) ที่เรียนก่อนหน้า Step ที่ 15 มาประยุกต์ใช้ จ้าาาาาาา

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

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

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

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

Reference
https://angular.io
https://cli.angular.io
https://github.com/angular/angular-cli
https://angular.io/api?type=directive
https://angular.io/api/forms/Validators

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

--

--