จากตอนที่แล้ว เราได้รู้จักกับการตรวจสอบฟอร์ม และวิธีการที่ Angular ใช้ในการ
กำหนดสภาวะเงื่อนไขข้อมูลของฟอร์มและ element ในฟอร์มไปแล้วนั้น
(ทบทวนเนื้อหาได้ที่)
การตรวจสอบ สภาวะเงื่อนไขข้อมูลของ Form ใน Angular เบื้องต้น
https://www.ninenik.com/content.php?arti_id=777 via @ninenik
Angular ได้ใช้ directive ที่ชื่อ ngForm และ ngModel ในการจัดการ form control อย่างเช่น
ตรวจสอบว่าฟอร์มนั้นพร้อมสำหรับส่งข้อมูลแล้วหรือไม่ เช็คว่า element ในฟอร์มมีการกำหนด
ค่าหรือผ่านการตรวจสอบข้อมูลแล้วหรือไม่ ในเนื้อหาต่อไปนี้ เราจะมาศึกษาเพิ่มเติม
เกี่ยวกับการอ้างอิง และ เรียกใช้งาน control แต่และตัวในฟอร์ม
เริ่มต้นในส่วนของแท็ก <form>
จากที่เคยอธิบายมาแล้วว่า เมื่อมีการ import "FormsModule" มาใช้งานใน app ของเรา
แท็ก <form> จะมีการใช้งาน ngForm directive อัตโนมัติ โดยเราไม่จำเป็นต้องกำหนด
selector เข้าไปในแท็ก <form>
แต่ถ้าสมมติเราจะทำการกำหนด selector เข้าไปก็สามารถกำหนดเป็น โดยใช้
ngForm หรือ [ngForm] ยกตัวอย่างเช่น
<form ngForm>
ยกตัวอย่างข้างต้นเพื่อให้เห็นว่า selector คือ อะไรอยู่ตรงไหน
โดย selector นี้ถ้าเรายังไม่ได้ใช้อ้างอิงกับตัวแปรใดๆ ก็ไม่ต้องเพิ่มเข้ามา จำไว้แค่เพียงว่า
ในแท็ก <form> จะมี "ngForm" selector นี้อยู่เสมอ
แท็ก <form> รองรับ "ngSubmit" event หรือการตรวจจับเมื่อมีการส่งข้อมูลฟอร์ม เราสามารถ
กำหนดให้ไปเรียกทำงานคำสั่งใดๆ ที่กำหนดใน component ได้
ในที่นี้เราให้ทำงานคำสั่งที่เราเคยสร้างไว้แล้วในบทความก่อนหน้าที่ชื่อ doSubmitStaff()
เราก็กำหนดเป็น
<form (ngSubmit)="doSubmitStaff()">
การทำงานคือเมื่อฟอร์มตรวจสอบความถูกต้องของข้อมูลเรียบร้อยแล้ว ก็จะไปทำงานคำสั่ง
doSubmitStaff() ใน component class ที่เรากำหนดไว้
ต่อไปเรามาลองกำหนดตัวแปร template variable ให้กับแท็ก <form> กันดู
ตัวแปร template variable เราได้ศึกษามาแล้วในบทความ
(ทบทวนเนื้อหาได้ที่)
การรับค่า input จาก user ใน Angular App เบื้องต้น
https://www.ninenik.com/content.php?arti_id=769 via @ninenik
ที่หัวข้อ "การรับค่าจาก user โดยอ้างอิงจาก template variable"
เราลองกำหนด และแสดงค่าดู ดังรูปประกอบ
<pre>{{myform}}</pre> <form #myform (ngSubmit)="doSubmitStaff()" >
เรากำหนด template variable เป็นตัวแปรแบบ local อ้างอิงใช้งานเฉพาะใน template
โดยจากตัวอย่างเราลองแสดงค่า ก็จะได้เป็น [object HTMLFormElement]
ซึ่งตัวแปร myform ที่เรากำหนด ก็จะอ้างอิงถึง <form> Object
แต่เนื่องจากเราจะใช้งานกับ ngForm directive เพื่อจะอ้างอิงตัวแปร instance ของ form control
ที่ Angular สร้างขึ้น ให้เรากำหนดค่าให้กับตัวแปร myform ให้เท่ากับ ngForm ดังนี้
<pre>{{myform.value | json}}</pre> <pre>{{myform.valid}}</pre> <form #myform="ngForm" (ngSubmit)="doSubmitStaff()" >
จะเห็นว่าตัวแปร myfrom ของเรา จะมีค่าเป็น instance Object ของ form control
มี property ต่างๆ อยู่ด้านใน
จากตัวอย่างเราจะยกมาอธิบาย 2 อัน ที่จำเป็นในการใช้งาน คือ property ชื่อ value และ valid
myform.value จะได้ค่าเป็น Object
{ "name": "", "department": "" }
เป็น Object มีเก็บ property เป็นชื่อของ element ในฟอร์มที่มีการใช้งาน ngModel และ
กำหนด "name" attribute และก็ค่าของ property แต่ละอัน ข้างต้นแต่ละค่าเป็นค่าว่าง
จริงๆ จะต้องมี property 3 อัน คือจะมี age ด้วย แต่เนื่องจากตัวโค้ดเรายังใช้การกำหนด
การเชื่อมข้อมูลแบบไปกลับระหว่างฟอร์มกับตัวแปรใน component อยู่ และในการแสดงฟอร์ม
กรณีเพิ่ม staff ใหม่เราไม่ได้กำหนด age ด้วยทำให้ ไม่แสดง "age" property ใน Object ข้างต้น
สังเกต เราใช้วิธีกำหนด filter ให้กับ expression ด้านบน ในการแสดงข้อมูล
{{myform.value | json}}
การทำงานส่วนนี้หมายถึง ให้แสดง Object ที่ชื่อ myform.value ออกมาในรูปแบบข้อความ json
property ตัวที่ 2 ของ ตัวแปร myfrom คือ valid คือจะเป็นประเภท boolean เก็บค่า true หรือ
false นั่นหมายถึง ข้อมูลในฟอร์มผ่านการตรวจสอบแล้วหรือไม่
{{myform.valid}}
จากรูปคำสั่งนี้จึงแสดงเป็นค่า false คือ myform ยังไม่ผ่านการตรวจสอบ
เราสามารถใช้ค่า myform.valid ไปใช้ในการกำหนดปุ่ม Submit ให้ ไม่สามารถกดได้
โดยไปกำหนดเป็นค่าของ "disabled" attribute ดังรูป
<button type="submit" class="btn btn-sm btn-success" [disabled]="!myform.valid" >Submit</button>
disabled="true" คือ กดยังไม่ได้
disabled="!false" นิเสธคือค่าตรงข้าม กำกับด้วย !
ซึ่งค่าตรงข้ามกับ false คือ true
[disabled]="!myform.valid"
ดังนั้นค่า myform.valid จะต้องเท่ากับ false ปุ่มถึงกดไม่ได้
หมายความว่าถ้าฟอร์มยังไม่ผ่านการตรวจสอบ ก็ยังไม่สามารถกดปุ่ม Submit ได้ แบบนี้เป็นต้น
เมื่อเรารู้แล้วว่า เราสามารถใช้งานตัวแปร template variable หรือตัวแปรแบบ local อ้างอิง
ค่าของข้อมูลในฟอร์มได้ เราก็ไม่จำเป็นต้องใช้การเชื่อมข้อมูลแบบ Two-way
ระหว่างฟอร์มกับตัวแปรใน component แล้ว แต่เราจะใช้เป็น
- การส่งค่าข้อมูลจากตัวแปร local เข้ามาในคำสั่ง เพื่อใช้งานใน component
แทน สำหรับกรณีเพิ่มข้อมูล staff ใหม่
- ใช้การเชื่อมข้อมูลแบบ One-way กรณีแก้ไขข้อมูล
โดยเราสามารถส่งค่าตัวแปร myform เข้าไปในคำสั่งเมื่อทำการส่งข้อมูล ดังนี้
<form #myform="ngForm" (ngSubmit)="doSubmitStaff(myform)" >
จะเห็นว่าเราส่งตัวแปร myform เข้าไปในคำสั่ง doSubmitStaff()
ต่อไปเราจะมาแก้ไขไฟล์ staff-form.component.ts โดยใช้ค่าตัวแปรที่ส่งมา ในการเพื่ม
รายการ staff ใหม่ และอัพเดทรายการ staff เป็นดังนี้
ไฟล์ staff.form.component.ts
import { Component } from '@angular/core'; import { Staff } from './staff'; @Component({ moduleId: module.id, selector: 'staff-form', templateUrl: `./staff-form.component.html`, styleUrls: ['./staff-form.component.css'] }) export class StaffFormComponent { // กำนหด array เก็บแผนกของ staff departments = [ 'Accounting','IT Support','Marketing' ]; // รายชื่อ staff เบืองต้นที่มีอยู่ ในที่นี้สมมติสร้างมา 3 คนไว้แล้ว staffs = [ new Staff(1,'John Smoke','IT Support',27), new Staff(2,'Linda Pink','Accounting',30), new Staff(3,'Lisa Mour','Marketing',33) ]; // สร้าง staff แบบเดี่ยวที่เราจะใช้กับฟอร์ม แบบ One-way data binding staff = new Staff(1,'',''); // ให้เป็นค่าว่างไป editmode = false; // กำหนดอยู่ในโหมดแก้ไข หรือไม่ เบื้องต้นเป็น false editID:number; // ไอดี staff ที่จะแก้ไข ตัวอย่างเราใช้เป็น index ของ array แทน showlist = false; // กำหนดสถานะการโชว์ลิสรายการ staff ไว้ใช้ร่วมกัน hidden attribute newstaff = true; // กำหนดสถานะการโชว์ฟอร์มของ staff ไว้ใช้ร่วมกัน hidden attribute // กำหนดคำสั่ง ให้แสดง ลิสรายการ staff doShowList(){ this.showlist = false; this.newstaff = true; } // กำหนดคำสั่ง ให้แสดงฟอร์ม staff doShowNewStaff(){ this.showlist = true; this.newstaff = false; this.staff = new Staff(1,'',''); // รีเซ็ตค่า staff ใน ฟอร์ม this.editmode = false; // ไม่ใช่โหมดแก้ไข เป็นโหมดเพิ่มข้อมูลใหม่ } // กำหนดคำสั่ง ให้ลบรายการ staff โดยรับค่า id ที่ต้องการลบ แต่ในที่นี้เราใช้ index ของ array deleteStaff(id:number){ this.staffs.splice(id, 1); // ลบ array รายการที่่มี index ตรงกับค่า id ที่ส่งมา } // กำหนดคำสั่งแสดงฟอร์ม staff พร้อมดึงข้อมูง โดยรับค่า id ที่ต้องแสดง ในที่นี้เราใช้ index ของ array doShowEditStaff(id:number){ this.doShowNewStaff(); // แสดงฟอร์ม staff this.staff = this.staffs[id]; // ดึงข้อมูลจาก staffs อิงจาก index ของ array this.editmode = true; // อยู่ในโหมดแก้ไขข้อมูล this.editID = id; // เก็บค่า index ของ array ที่จะแก้ไข } // กำหนดคำสั่งสำหรับเพิ่ม หรือแก้ไข staff doSubmitStaff(f:NgForm){ let numID = this.staffs.length; // นับจำนวน staff ที่มีอยู่ let keyID = this.editID; // index ของ array ที่ต้องการแก้ไข if(this.editmode==false){// ไม่ใช่โหมดแก้ไข เป็นโหมดเพิ่มข้อมูลใหม่ // ในการทดสอบนี้เราทำาร push array ของ object staff ใหม่ this.staffs.push( new Staff( numID+1, f.value.name, f.value.department, f.value.age ) ); }else{ // แต่ถ้าเป็นโหมดแก้ไข เราก็ให้อัพเดทข้อมูล staffs array ตาม index ที่ส่งมา this.staffs[keyID].name = f.value.name; this.staffs[keyID].department = f.value.department; this.staffs[keyID].age = f.value.age; } this.doShowList(); // หลังจากเพิ่ม หรือแก้ไขรายการแล้ว ให้แสดงลิสรายการ staff } }
ดูในส่วนคำสั่ง doSubmitStaff() ที่เราแก้ไข สังเกตว่าเรากำหนด parameter เพิ่มเข้ามาให้มี
type เป็น NgForm ใช้ตัวแปรชื่อ f ดังนี้
doSubmitStaff(f:NgForm)
และในส่วนของการนำค่าไปใช้สำหรับเพิ่มข้อมูลหรืออัพเดทข้อมูล
เราก็อ้างอิงตัวแปร ที่ส่งค่ามาเป็นดังนี้
- f.value.name
- f.value.department
- f.value.age
เนื่องจากเราจะไม่ได้มีการใช้งานการเชื่อมข้อมูลแบบ Two-way ในฟอร์มแล้ว ดังนั้น
ให้เราแก้ไขไฟล์ staff-form.component.html ในส่วนของฟอร์มเป็นดังนี้
ไฟล์ staff-form.component.html
<div [hidden]="newstaff" class="container"> <pre>{{myform.value | json}}</pre> <pre>{{staff | json}}</pre> <div> <form #myform="ngForm" (ngSubmit)="doSubmitStaff(myform)" > <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control input-sm" id="name" required [ngModel]="staff.name" name="name" > </div> <div class="form-group"> <label for="age">Age</label> <input type="text" class="form-control input-sm" id="age" [ngModel]="staff.age" name="age"> </div> <div class="form-group"> <label for="department">Department</label> <select class="form-control input-sm" id="department" required [ngModel]="staff.department" name="department"> <option *ngFor="let department of departments" [value]="department">{{department}}</option> </select> </div> <button type="submit" class="btn btn-sm btn-success" [disabled]="!myform.valid" >Submit</button> <button type="button" class="btn btn-sm btn-danger" (click)="doShowList()" >Cancel</button> </form> </div> </div>
สิ่งที่เราแก้ไขคือ การใช้งาน ngModel จากเดิมเรากำหนดในรูปแบบ
[(ngModel)]
ซึ่งเป็นรูปแบบการกำหนดการเชื่อมต่อแบบ Two-way คือถ้าเราเปลี่ยนค่าในฟอร์มแล้ว
ค่าตัวแปรใน component จะเปลี่ยนด้วย กลับกันถ้าเราเปลี่ยนค่าตัวแปรใน component
ค่าที่แสดงในฟอร์มจะเปลี่ยนด้วย
โดยเปลี่ยนมาใช้เป็น
[ngModel]
ซึ่งเป็นรูปแบบการกำหนดการเชื่อมต่อแบบ One-way คือถ้าเปลี่ยนค่าในฟอร์แล้ว
ค่าตัวแปรใน component จะไม่เปลี่ยนไปด้วย แต่ค่าในฟอร์มจะเปลี่ยนแปลงเฉพาะกรณีตัวแปรใน
component เปลี่ยนแปลงเท่านั้น
ข้อสังเกต: การกำหนดโดยใช้ [] จะเป็นการเชื่อมข้อมูลแบบ One-way ส่วนการกำหนดแบบ [()]
จะเป็นการเชื่อมข้อมูลแบบ Two-way
เรามาดูตัวอย่างจากรุปภาพประกอบ
สมมติเราทำการแก้ไขข้อมูล staff คนแรก ดังนี้
นี้คือส่วนของโค้ดที่เราใช้สำหรับทดสอบ debug ค่า
<pre>{{myform.value | json}}</pre> <pre>{{staff | json}}</pre>
ส่วนแรกคือข้อมูลจากฟอร์มหน้า template
ส่วนที่สองเป็นข้อมูลจาก component ตัวแปร staff
หน้านี้เป็นหน้าแก้ไขข้อมูล ข้อมูลจะตรงกัน
ที่นี้เราจะลองเปลี่ยนค่า name ดู ดังรูป
จะเห็นว่าเมื่อเราเพิ่มคำว่า Edit ต่อเข้าไปในชื่อ
ค่าของ name ในส่วนแรก ซึ่งเป็นค่าจากตัวแปร local ของฟอร์มจะมีการเปลี่ยนแปลง
แต่ค่าของ name ในตัวแปร staff ที่อยู่ใน component ไม่ได้เปลี่ยนแปลงไปด้วย
ทั้งนี้ก็เพราะเราใช้การเชื่อมข้อมูลแบบ One-way แทน Two-way
ค่าของ name ใน component จะเปลี่ยนแปลงเมื่อเราทำการ Submit และส่งค่าไปอัพเดท
การใช้งานตัวแปร Template varable กับ ngModel
นอกจากแท็ก <form> ที่เราสามารถกำหนดตัวแปร template variable แบบ local ให้มีค่า
เท่ากับ instance ของ form control โดยใช้ ngForm ตามที่อธิบายมาแล้วข้างต้น
เรายังสามารถกำหนดตัวแปร template variable แบบ local ให้กับ element อื่นๆ ในฟอร์ม
ได้เหมือนกัน โดยจะให้มีค่าเท่ากับ instance Object ของ form control ที่ได้จาก
ngModel สามารถกำหนด ได้ดังนี้ ตัวอย่างเรากำหนดให้กับ name เป็นตัวอย่าง ดังนี้
<input type="text" class="form-control input-sm" id="name" required [ngModel]="staff.name" name="name" #name="ngModel" >
เรากำหนดตัวแปร name ด้วย #name ซึ่งเป็นตัวแปร template variable ใช้งานใน template
โดยให้มีค่าเท่ากับ ngModel ดังนั้นเราจะได้ตัวแปร name ที่เป็นตัวแปรแบบ local
มี property ชื่อ "value" และ "valid"
ตัวแปร name เป็น instance Object ของ form control ที่เราสามารถนำค่าใช้งานได้ โดย
- name.value คือค่าของ element นั้นๆ
- name.valid คือค่าของการตรวจสอบของ element นั้น มีค่าเป็น true หรือ false
ค่าตัวแปรนี้ เราสามารถนำไปใช้ประโยชน์ใน template ได้เช่น สมมติเราต้องการแสดงข้อความว่า
"Please fill your name!" ถ้าไม่มีการกรอกข้อมูล และ
ซ่อนข้อความนี้ ถ้ามีการกรอกข้อมูล หรือ name.valid เป็น true
ตัวอย่าง
<div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control input-sm" id="name" required [ngModel]="staff.name" name="name" #name="ngModel" > <div class="alert alert-danger" [hidden]="name.valid"> Please fill your name! </div> </div>
จะเห็นว่าเราสร้าง <div> ขึ้นมาแสดงด้านล่างของ <input> name และใช้ "hidden" attribute
ในการซ่อน อ้างอิงค่าจากตัวแปร instance Object ที่อยู่ในตัวแปร name ที่เรากำหนด
[hidden]="name.valid"
ซ่อนเมื่อมีค่าเป็น true แสดงเมื่อมีค่าเป็น false
นั่นคือข้อความแจ้งเตือน แสดง เมื่อ name.valid เป็น false หรือ ไม่ผ่านการตรวจสอบ
และข้อความแจ้งซ่อน เมื่อ name.valid เป็น true หรือ ผ่านการตรวจสอบแล้ว ไม่ต้องแจ้งเตือน
ดูรูปประกอบ กรณียังไม่กรอกชื่อ
กรณีเรากรอกชื่อแล้ว ไม่ขึ้นข้อความแจ้งเตือน
ทั้งหมดก็คือ การใช้งานฟอร์มใน Angular เบื้องต้น เป็นพื้นฐานที่สำคัญ ที่สามารถต่อยอดและประยุกต์
ใช้งานเพิ่มเติมในอนาคตได้ พยายามทบทวนและทำความเข้าใจให้คล่อง จะเป็นประโยชน์อย่างมาก
เนื้อหาตอนต่อไปน่าจะเกี่ยวกับเรื่องใหม่แล้ว รอติดตาม