การใช้งาน data model และ form model ร่วมกับ Reactive Form

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
ngonchanges reactive form form model data model

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ ngonchanges reactive form form model data model

ดูแล้ว 8,345 ครั้ง


เรามาต่อเนื้อหาเกี่ยวกับ Reactive form ซึ่งตอนนี้น่าจะเป็นตอนที่ 3 แล้ว เนื้อหาจะยังเป็นการ
ต่อเนื่องจากตอนที่แล้ว ดังนั้น สามารถทบทวนตอนที่แล้วได้ที่บทความ
 
การเพิ่ม FormControl เข้ามาใช้งานใน Reactive Form 
 
 

ทำความเข้าใน data model และ form model

ตอนนี้เราได้ฟอร์มที่แสดงรายการข้อมูลว่างเปล่า ซึ่งจริงๆ แล้ว StaffDetailComponent ควรจะต้องแสดงค่าของ staff
โดยอาจจะได้มากจาก server 
สำหรับข้อมูล staff ที่ได้มาจาก server จะเรียกว่า data model 
ส่วนโครงสร้างข้อมูลของ FormControl จะเรียกว่า form model
ซึ่ง component จะต้องทำการ copy ค่าของ staff จาก data model เข้าไปใน form model โดยมีเงื่อนไข
ความสัมพันธ์ที่สำคัญดังนี้
  1. ต้องเข้าใจว่า property ของ data model จับคู่กับ property ของ form model ได้อย่างไร
  2. การเปลี่ยนแปลงที่เกิดขึ้นใน DOM element จะมีผลกับค่าของ form model แต่ไม่ได้มีผลกับ data model โดย form control จะไม่มีการอัพเดทค่าของ data model
โครงสร้างของ form model และ data model ไม่จำเป็นจะต้องมีรูปแบบตรงกันโดยทั้งหมด เราสามารถที่จะใช้ข้อมูล
บางส่วนของ data model มาแสดง   แต่อย่างไรก็ตาม ถ้าหน้าตาของ form model และ data model ใกล้เคียง
กันมากเท่าไหร่ ก็จะทำให้เราสามารถจัดการได้ง่ายขึ้นตามไปด้วย
 
ในตัวอย่างที่ผ่านมาของเรา จะเห็นว่าหน้าตาของ data model แทบจะเหมือนกับ form model 
ดูส่วนของข้อมูลทั้งสองเทียบกันประกอบ
 

หน้าตาของ data model ใน ไฟล์ data-model.ts (บางส่วน)

export class Staff{
    constructor(
        public id:number,
        public name:string,
        public age:number,
        public gender:string,
        public vacation:Boolean,
        public education?:Education[]
    ){}
}
export class Education{
    constructor(
        public degree?:string,
        public discipline?:string,
        public year?:string
    ){}
}
 

หน้าตาของ form model ใน ไฟล์ staff-detail.component.ts (บางส่วน) 

this.staffForm = this.fb.group({// <-- the parent FormGroup
  name: ['',Validators.required], 
  education: this.fb.group({ // <-- the child FormGroup
    degree: '',
    discipline: '',
    year: ''
  }),
  age: '',
  gender: '',
  vacation: '' 
});
 
ความแตกต่างที่สำคัญของ model ทั้งสองคือ
1. ใน Staff class มี "id" แต่ใน form model ไม่มี เพราะว่าโดยทั่วไปแล้วเราจะไม่แสดง primary key ให้ผู้ใช้เห็น
2. ใน Staff class มี array ของ education  แต่ใน form model มีแค่ education เพียงอันเดียว ทางเลือกในการ
แก้ปํญหาสำหรับกรณีนี้ เราจะอธิบายในหัวข้อต่อไป
 
ถึงอย่างไรก็ตาม model ทั้งสองก็ถือว่ามีความใกล้เคียงกันอย่างมาก เราจะได้เห็นวิธีการจัดเรียงเพื่อให้ง่ายในการ
นำค่า property จาก data model ไปยัง form model โดยใช้คำสั่ง "patchValue" และ "setValue"
 
ให้เราจัดรูปแบบของ "education" FormGroup ใหม่ให้สั้นและดูชัดเจนขึ้น รวมทั้ง ทำการ import ข้อมูล
จาก data-model.ts มาใช้งาน เราจะได้ไฟล์ staff-detail.component.ts เป็นดังนี้
 

ไฟล์ staff-detail.component.ts

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { Education, Staff, degrees } from './data-model';

@Component({
  selector: 'staff-detail',
  templateUrl:'/app/components/staff-detail.component.html'
})
export class StaffDetailComponent  {
  staffForm: FormGroup; 
  degrees = degrees;

  constructor(private fb: FormBuilder) { 
    this.createForm();
  }

  createForm() {
    this.staffForm = this.fb.group({// <-- the parent FormGroup
      name: ['',Validators.required], 
      education: this.fb.group(new Education()),
      age: '',
      gender: '',
      vacation: '' 
    });
  }    

}
 
 

การตั้งค่า form model ด้วย setValue และ patchValue

ในการกำหนดค่าใน form model เราสามารถสร้าง form control พร้อมกับกำหนดค่าพร้อมกันได้ ตามรูปแบบ
 

ไฟล์ staff-detail.component.ts (บางส่วน)

this.staffForm = this.fb.group({// <-- the parent FormGroup
  name: ['',Validators.required], 
  education: this.fb.group(new Education()),
  age: '',
  gender: '',
  vacation: '' 
});
 
จากตัวอย่าง เช่น เราสร้าง form control ชื่อ "name" และกำหนดค่าเป็นค่าว่างไปในทันที พร้อมๆ กับที่สร้าง
form control   ซึ่งเรายังสามารถที่จะกำหนดค่าเริ่มต้นหรือทำการรีเซ็ตค่าโดยใช้คำสั่ง setValue และ
patchValue ได้อีกด้วย
 

การใช้งานคำสั่ง setValue

เมื่อใช้คำสั่ง setValue เราสามารถกำหนดค่าของ form control ทั้งหมดพร้อมกันในทีเดียว โดยการส่งผ่าน
ข้อมูล data model object ที่มี property ตรงกับ  form model ที่กำหนดใน FormGroup
 
รูปแบบที่ใช้จะเป็นลักษณะดังนี้
 
this.staffForm.setValue({
  name:    this.staff.name,
  education: this.staff.education[0] || new Education()
});
 
คำสั่ง setValue จะทำการตรวจสอบ data object ทั้งหมดก่อนแล้วค่อยทำการกำหนดค่าให้กับ form control ใดๆ
 
กรณีที่ data object ไม่ตรงกับรูปแบบโครงสร้างของ FormGroup หรือไม่ครบตรงตามจำนวนของ control ในกลุ่ม
ก็จะไม่ทำการกำหนดค่าให้กับ form control   และอาจจะมีข้อความ error ขึ้น ซึ่งจะช่วยได้มากในการตรวจสอบว่า
เราพิมพ์ข้อมูลถูกต้องหรือไม่ หรือเราจัดกลุ่มข้อมูลได้ถูกต้องหรือไม่ ในขณะที่ถ้าใช้ patchValue จะไม่มีการแจ้ง
error ในลักษณะดังกล่าว 
 
หรืออีกนัยหนึ่งก็คือ คำสั่ง setValue จะแจ้ง errror ออกมาชัดเจนเมื่อมีข้อผิดพลาดเกิดขึ้น
 
จะสังเกตเห็นว่าเราสามารถใช้ staff แทบทั้งหมดในการกำหนดเป็นค่า argument เข้าไปในคำสั่ง setValue เพราะว่า
มีโครงสร้างเหมือนกับใน FormGroup
 
เราสามารถแสดง education หรือระดับการศึกษาแรกรายการเดียวของ staff ได้ หรือไม่ถ้าไม่มีการระบุระดับการศึกษา
เราก็สามารถใช้การกำหนดเงื่อนเพื่อกำหนดให้สามารถตั้งค่าระดับการศึกษาขึ้นมาใหม่ได้ ตามรูปแบบตัวอย่าง
ด้านล่าง
 
education: this.staff.education[0] || new Education()
 
ตอนนี้อย่าเพิ่งงงว่า staff มาจากไหน โค้ดที่ผ่านมาไม่เห็นพูดถึงส่วนนี้มาก่อนเลย ตัวแปร staff จะเกิดจากการใช้งาน
@Input propperty ซึ่งเราจะได้เห็นรายละเอียดเพิ่มเติม จุดนี้ให้โฟกัสที่รูปแบบการใช้งาน setValue ก่อน
 
 

การใช้งานคำสั่ง patchValue 

เมื่อใช้คำสั่ง patchValue เราสามารถที่จะกำหนดให้กับ form control เฉพาะที่้ต้องการใน FormGroup ต่างจาก setValue
ที่กำหนดค่าให้กับ form control ทั้งหมด    การใช้ patchValue จะส่งค่าข้อมูลจาก data model object ที่มี property 
ตรงกันกับ form model ใน FormGroup
 
รูปแบบที่ใช้จะเป็นลักษณะดังนี้
 
this.staffForm.patchValue({
  name: this.staff.name
});
ในตัวอย่างจะเป็นการกำหนดให้เฉพาะ "name" form control 
ด้วยวิธี patchValue จะค่อนข้างยืดหยุ่นในการจัดการกับข้อมูล data model ที่มีความแตกต่างกับ form model มาก
แต่จะไม่เหมือนกับ setValue ตรงที่คำสั่ง patchValue จะไม่มีการตรวจสอบ control ที่ไม่ตรงกันและไม่มีการแจ้ง
error ใดๆ ซึ่งอาจจะไม่ช่วยในการจัดการกับ error ทีอาจจะเกิดขึ้น กล่าวคือเราต้องตรวจสอบ error เอง ถ้ามี
 

เราจะกำหนดค่าให้กับ form model เมือใด (ngOnChange)

ตอนนี้เราได้รู้จักรูปแบบการกำหนดค่าให้กับ form model ไปแล้ว แต่ยังไม่รู้ว่าจะกำหนดค่าในตอนไหน คำตอบก็คือ
ขึ้นอยู่กับว่า component ได้รับข้อมูล data model ตอนไหน
 
ก่อนเราลงไปในรายละเอียด เรากลับมาสร้างรูปแบบจำลองสถานการณ์ของการทำงานกันก่อน
รูปแบบคือ เราจะมี รายการชื่อของ staff ให้เลือก เพื่อเราคลิกเลือก รายการ staff ใดๆ ก็ให้ทำการส่ง
staff object นั้นๆ เข้าไปใน reative form ซึ่ง staff object ก็คือ data model ข้อมูลของ staff ที่เรา
จะส่งเข้าไปแสดงในฟอร์ม ไปแสดงใน form model 
ดังนั้นให้เราสร้าง component ขึ้นมาอีกอันหนึ่งชื่อว่า staff-list.component.ts ไว้ในโฟลเดอร์ components
มีรูปแบบอย่างง่ายดังนี้ 
 

ไฟล์ staff-list.component.ts


 
 
import { Component } from '@angular/core';

import { Staff, staffs } from './data-model';

@Component({
  selector: 'staff-list',
  template: `
    <ul class="nav nav-pills">
        <li class="active" *ngFor="let staff of staffs">
            <a [style.cursor]="'pointer'" 
            (click)="select(staff)">
            {{staff.name}}
            </a>
        </li>
    </ul>  
    <div *ngIf="selecteStaff">
    <staff-detail [staff]="selecteStaff"></staff-detail>
    </div>
  `,
})
export class StafListComponent  {
    staffs:Staff[];
    selecteStaff:Staff;    
    constructor(){
        this.staffs = staffs;
    }
    select(staff:Staff){
        this.selecteStaff = staff;
    }


}
 
 
จะไม่ขออธิบายทั้งหมด เนื่องจากเป็นรูปแบบที่คุ้นเคยกันแล้ว หากศึกษาตั้งแต่ต้น ลักษณะการทำงาน
คือเราทำการลิสต์รายการ staffs ไว้แล้วกำหนด event เมื่อ คลิก ที่รายชื่อ staff ใด ก็ให้นำรายการชื่อ staff
ส่งค่าเป็น staff object ไปไว้ในค่า "selectSaff" ผ่านคำสั่ง select() และเมื่อค่า "selectStaff" มีค่า 
<div> ที่มีรายการ <staff-detail> directive ก็จะแสดง พร้อมกับส่งค่า selectStaff เข้าไปผ่าน [staff] input
property ซึงเราจะต้องไปกำหนด input property ไว้ใน StaffDetailComponent ด้วย
 
อย่าลืม ทำการ import StaffListComponent รวมถึงประกาศใช้งานใน declarations ในไฟล์ app.module.ts
 
ต่อมาให้เรามาปรับไฟล์ app.component.ts เพื่อเรียกใช้งาน <staff-list> directive จะได้เป็นดังนี้

ไฟล์ app.component.ts 


 
 
import { Component } from '@angular/core';
 
@Component({
  selector: 'my-app',
  template: `
    <div class="container">
    <br>  
    <staff-list></staff-list>
    </div>
  `,
})
export class AppComponent  {

}
 
กลับมาที่ StaffDetailComponent โดยความสัมพันธ์จาก StaffListComponent เมื่อ รายชื่อ staff ถูกคลิกเลือก
staff object ก็จะถูกส่งเข้ามายัง StaffDetailComponent ผ่าน @Input property ที่ชื่อ [staff] ตามที่เรา
ได้กำหนดการเชื่อมข้อมูลด้วย input property ไว้ โดยทุกๆ ครั้งที่มีการเลือก staff ที่ต้องการ เราสามารถกำหนด
ค่าของ form model ผ่านคำสั่ง setValue เมื่อคำสั่ง ngOnChanges() ทำงาน 
 
มาดูรูปโครงสร้างของ lifecycle hook เพื่อทำความเข้าใน การทำงานของคำสั่ง ngOnChanges() คร่าวๆ 
 
 
เราจะได้ทำความรู้จักเพิ่มเติมเกี่ยวกับ lifecycle hook ใน Angular ต่อๆ ไป
 
Angular จะเรียกใช้คำสั่งตามวัฐจักรการทำงาน เมื่อ directive และ component ถูกสร้าง เปลี่ยนแปลงค่า และ
ถูกทำลาย เกิดขึ้น
 
อย่างกรณีที่เรากำลังใช้งาน เมื่อ input property มีการเปลี่ยนแปลงเกิดขึ้น Angular ก็จะทำการเรียกใช้งานคำสั่ง
ngOnChanges() ณ จุดนี้ เราจึงใช้เป็นจุดที่จะกำหนดค่าให้กับ form model ผ่านคำสั่ง  setValue หรือ patchValue
ตามที่ได้กล่าวมา รูปแบบก็จะเป็นลักษณะดังนี้
 
ngOnChanges(staff: Staff){
  this.staffForm.setValue({
    name:    this.staff.name,
    education: this.staff.education[0] || new Education(),
    age:this.staff.age,
    gender:this.staff.gender,
    vacation:this.staff.vacation
  });
}
 
เราจะได้ไฟล์ staff-detail.component.ts ที่มีการ import ngOnChange และ Imput class เข้ามาใช้งาน
รวมทั้ง มีการกำหนด input property ใน component class ที่ชื่อ staff เป็นดังนี้
 

ไฟล์ staff-detail.component.ts

import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

import { Education, Staff, degrees } from './data-model';

@Component({
  selector: 'staff-detail',
  templateUrl:'/app/components/staff-detail.component.html'
})
export class StaffDetailComponent{
  @Input() staff:Staff;
  
  staffForm: FormGroup; 
  degrees = degrees;

  constructor(private fb: FormBuilder) { 
    this.createForm();
  }
   
  createForm() {
    this.staffForm = this.fb.group({// <-- the parent FormGroup
      name: ['',Validators.required], 
      education: this.fb.group(new Education()),
      age: '',
      gender: '',
      vacation: '' 
    });
  }    

  ngOnChanges(staff: Staff){
    this.staffForm.setValue({
      name:    this.staff.name,
      education: this.staff.education[0] || new Education(),
      age:this.staff.age,
      gender:this.staff.gender,
      vacation:this.staff.vacation
    });
  }
  

}
 
เรามาทดสอบผลลัพธ์ที่ได้ดู เริ่มต้น จะแสดงรายชื่อ staff มาให้เราเลือก ตามที่เรากำหนดในไฟล์ data-model.ts
 
 

 
 
มาเราคลิกที่ "Manop" รายการข้อมูลของ Manop ก็จะมาแสดงใน Reactive Form และถ้าคลิกที่
"Jubjang" รายการข้อมูลก็จะเป็นของ Jubjang 

 

 
 
และนี้คือวิธีการนำข้อมูลจาก data model มาแสดงใน form model ซึ่งถ้าหากเราทำการแก้ไขข้อมูลในฟอร์ม
ส่วนของค่าใน form model ก็จะเปลี่ยนแปลงค่าไปตามที่เราแก้ไข แต่ส่วนของ data model จะไม่มีการเปลี่ยนแปลง
ใดๆ เกิดขึ้น
 

การรีเซ็ตค่าของ Form

เราควรทำการรีเซ็ตฟอร์มทุกครั้งเมื่อมีการเปลี่ยนแปลงค่าเกิดขึ้น ทั้งนี้เก็เพื่อทำการล้างค่าของ form control ที่
ได้กำหนดไปแล้วให้เป็นค่าว่างและให้ฟอร์มอยู่ในสภาวะ pristine (สภาวะที่บ่องบอกว่าฟอร์มนี้ยังไม่เกิดอะไรขึ้น
ยังไม่มีใครทำอะไร) โดยวางคำสั่ง reset() ไว้ด้านบนสุดในส่วนของ ngOnChanges ในลักษณะดังนี้
 
ngOnChanges(staff: Staff){
  this.staffForm.reset();
  this.staffForm.setValue({
    name:    this.staff.name,
    education: this.staff.education[0] || new Education(),
    age:this.staff.age,
    gender:this.staff.gender,
    vacation:this.staff.vacation
  });
}
 
หรือเราจะใช้วิธีแบบกำหนดค่าเข้าไปในลักษณะเดียวกับการใช้คำสั่ง setValue ก็ได้ คือทำหน้าที่ reset ค่า
แล้วก็ setValue  เป็นดังนี้
 
ngOnChanges(staff: Staff){
  this.staffForm.reset({
    name:    this.staff.name,
    education: this.staff.education[0] || new Education(),
    age:this.staff.age,
    gender:this.staff.gender,
    vacation:this.staff.vacation
  });
}
 
ตอนนี้เราได้รู้จักวิธีการนำข้อมูลจาก data model มาแสดงใน form model ของ reactive form แล้ว
รูปแบบของ data model ในตัวอย่าง เราใช้วิธีการดึงค่าจากไฟล์ data-model.ts ซึ่งเป็นรูปแบบอย่างง่าย
และเป็นวิธีการที่เหมาะสมสำหรับการทดสอบเพื่อดูแนวทางการทำงาน แต่โดยปกติหรือในการใช้งานจริง
เราจะใช้วิธีการสร้างเป็น service แล้วทำการดึงข้อมูลจาก server มาใช้งาน ซึ่งคงจะได้ศึกษาเพิ่มเติมต่อไป
 
ถึงตอนนี้ บทความเกี่ยวกับ Reactive Form ของเรายังไม่จบ ยังมีหัวข้อเกี่ยวกับ การใช้งาน FormArray 
ให้เราได้ศึกษาต่อ ซึ่งเป็นตอนที่เนื่องต่อจากบทความนี้ รอดติดตาม

 


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ