www.itsci.mju.ac.th/sayan HANDLING FORMS IN ANGULAR APPS SAYAN UNANKARD 2/2559 8
www . i t s c i .m ju . ac . t h / sayan
HANDLING FORMS IN ANGULAR APPS
SAYAN UNANKARD2/2559
8
ANGULAR AND FORMS
<form><label>name</label><input type=“text” name=“stuname”><label>email</label><input type=“text” name=“stuemail”><button type=“submit”>Save</button></form>
2
{value : {
stuname : ‘sayan’,stuemail : ‘[email protected]’
}valid : true
}
2 APPROACHES
• Template-Driven โดยที่ Angular จะสรุปจาก (infers) form objects จาก DOM
• Reactive โปรแกรมเมอร์ต้องเขียนโครงสร้างด้วย Typescript เพื่อไปจัดการ DOM เอง
3
TEMPLATE-DRIVEN
ต้องการสร้างหน้าจอส าหรับกรอกข้อมูลดังนี้
4
TEMPLATE-DRIVEN
ในส่วนของ app.component.html สร้าง tag form โดยที่ไม่ต้องระบุ action และ method ดังนี้
5
<form>
<div id="user-data">
<label for="username">Username</label>
<input type="text" id="username" class="form-control">
<button class="btn btn-default" type="button">Suggest an
Username</button>
<label for="email">Mail</label>
<input type="email" id="email" class="form-control">
<label for="secret">Secret Questions</label>
<select id="secret" class="form-control">
<option value="pet">Your first Pet?</option>
<option value="teacher">Your first teacher?</option>
</select>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
หมายเหตุ css ไม่ได้ก าหนดลงไปในตัวอย่าง
TEMPLATE-DRIVEN
ในการควบคุม Form สามารถก าหนด attribute โดยระบุ ngModel และ name เข้าไปใน DOM objects ดังนี้
6
<input type="text" id="username" class="form-control"
ngModel name="username">
<input type="email" id="email" class="form-control“
ngModel name="email">
<select id="secret" class="form-control"
[ngModel]="defaultQuestion" name="secret">
<option value="pet">Your first Pet?</option>
<option value="teacher">Your first teacher?</option>
</select>
TEMPLATE-DRIVEN
ในส่วนของปุ่ม submit สามารถก าหนดให้เรียกใช้ method ที่สร้างขึ้นมาได้ ดังนี้
เมื่อมีการกดปุ่ม submit ให้ไปเรียกใช้ method onSubmit() โดยส่งค่าตัวแปร frm เข้าไป ซึ่งตัวแปร frm ถูกก าหนดค่าให้เป็น ngForm
Method onSubmit() ใน ไฟล์app.component.ts
7
<form (ngSubmit)="onSubmit(frm)" #frm="ngForm">
import { Component} from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
onSubmit(frm : NgForm){
console.log(frm);
}
}
TEMPLATE-DRIVEN
8
TEMPLATE-DRIVEN
ตัวแปรประเภท local reference โดยใช้ @ViewChild แทนการส่งพารามิเตอร์ใน onSubmit()
โดยเพิ่มตัวแปร @ViewChild() ในไฟล์ app.component.ts
9
<form (ngSubmit)="onSubmit()" #frm="ngForm">
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
@ViewChild('frm') signfrm : NgForm;
onSubmit(){
console.log(this.signfrm);
}
}
VALIDATION TO CHECK USER INPUT
สามารถก าหนดรูปแบบการตรวจสอบการป้อนข้อมูลของผู้ใช้ได้ โดยการเพิ่ม attribute เช่น required หรือ email เข้าไปใน INPUT objects
ในกรณีที่กรอกข้อมูลไม่ถูกต้องตามรูปแบบที่ก าหนด attribute ที่ชื่อว่า valid จะมีค่าเป็น false
10
<input type="text" id="username" class="form-control"
ngModel name="username" required >
<input type="email" id="email" class="form-control“
ngModel name="email" required email >
VALIDATION TO CHECK USER INPUT
11
VALIDATION TO CHECK USER INPUT
โดยในส่วนของ DOM elements จะมีการเพิ่ม attributes เช่น ng-invalid ใน input object นั้น ๆ ที่ผู้ใช้กรอกข้อมูลไม่ตรงรูปแบบ
12
VALIDATION TO CHECK USER INPUT
สามารถก าหนดให้ปุ่ม submit disabled ถ้าผู้ใช้กรอกข้อมูลไม่ถูกต้องตามรูปแบบที่ก าหนด
อีกทั้งยังสามารถก าหนดข้อความแจ้งเตือนผู้ใช้ได้ดังนี้
เพิ่มตัวแปร reference #email และใช้ค าสั่ง ngIf ในการตรวจสอบ properties ของ email ว่า valid หรือไม่ และ email เคยเข้าไปกรอกแล้วหรือไม่
13
<button class="btn btn-primary" type="submit"
[disabled]="!frm.valid">Submit</button>
<label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email"
required email #email="ngModel">
<span class="help-block" *ngIf="!email.valid && email.touched">
Please enter a valid email!</span>
VALIDATION TO CHECK USER INPUT
14
SET DEFAULT VALUE
การก าหนดค่าเริ่มต้นของ dropdown list ก็สามารถท าได้โดยการก าหนด property ดังนี้
15
<select id="secret"
class="form-control" [ngModel]="defaultQuestion" name="secret">
<option value="pet">Your first Pet?</option>
<option value="teacher">Your first teacher?</option>
</select>
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
defaultQuestion = 'pet';
@ViewChild('frm') signfrm : NgForm;
...
}
USING NGMODEL 2-WAYS BINDING
ในกรณีที่ต้องการเพิ่มค าตอบของค าถามลับ (Secret Question) สามารถก าหนดให้เป็น 2-way binding ได้ โดยใช้ ngModel ดังนี้
16
<div class="form-group">
<textarea name="questionAnswer" rows=3 class="form-control"
[(ngModel)]="answer"></textarea>
</div>
<p>Your reply : {{answer}}</p>
import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
...
export class AppComponent {
defaultQuestion = 'pet';
@ViewChild('frm') signfrm : NgForm;
answer = '';
...
}
USING NGMODEL 2-WAYS BINDING
17
HANDLING RADIO BUTTON
การจัดการ Radio button สามารถท าได้เช่นเดียวกับ input อื่น ๆ โดยสามารถก าหนดค่าดังนี้
Loop แสดง radio buttom ตามค่าใน genders array
18
export class AppComponent {
@ViewChild('frm') signfrm : NgForm;
defaultQuestion = 'pet';
answer = '';
genders = ['male', 'female'];
onSubmit(){
console.log(this.signfrm);
}
}
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" name="gender" ngModel [value]="gender" required>
{{ gender }}
</label>
</div>
BUTTON CLICK
การจัดการเมื่อมีการคลิกปุ่มที่ไม่ใช่ submit สามารถสร้าง method แล้วเรียกใช้ด้วยค าส่ัง (click) ได้ดังนี้
ค าสั่ง this.signfrm.form.patchValue() เป็นการเปลี่ยนแปลงค่าเฉพาะส่วนที่ต้องการ เพื่อไม่ให้กระทบกับส่วนอื่น ๆ ที่กรอกข้อมูลไปแล้ว
19
suggestUserName() {
const suggestedName = 'Superuser';
this.signfrm.form.patchValue({
username: suggestedName
});
}
}
<button class="btn btn-default" type="button"
(click)="suggestUserName()">Suggest an Username</button>
USING FORM DATA
การเรียกใช้งานข้อมูลจาก Form object สามารถท าได้โดยการประกาศตัวแปร เพื่อเก็บค่าข้อมูลจาก Form ได้ดังนี้ โดยที่ชื่อตัวแปรไม่จ าเป็นต้องตรงกับชื่อของ form objects
การอ่านค่าจาก Form สามารถท าได้โดยอ้างตัวแปร this.ชื่อตัวแปร form.value.ช่ือ form object
20
user = {
username: '',
email: '',
secretQuestion: '',
answer: '',
gender: ''
};
onSubmit(){
this.submitted = true;
this.user.username = this.signfrm.value.username;
this.user.email = this.signfrm.value.email;
this.user.secretQuestion = this.signfrm.value.secret;
this.user.answer = this.signfrm.value.questionAnswer;
this.user.gender = this.signfrm.value.gender;
this.signfrm.reset();
}
USING FORM DATA
สามารถเรียกดูค่าจากตัวแปรที่รับค่ามาจาก form object ได้โดยแสดงผ่านทางหน้าจอ ดังนี้
21
<hr>
<div class="row" *ngIf="submitted">
<div class="col-xs-12">
<h3>Your Data</h3>
<p>Username: {{ user.username }}</p>
<p>Mail: {{ user.email }}</p>
<p>Secret Question: Your first {{ user.secretQuestion }}</p>
<p>Answer: {{ user.answer }}</p>
<p>Gender: {{ user.gender }}</p>
</div>
</div>
EXERCISE
22
จงสร้างหน้าจอต่อไปนี้โดยใช้ Angular4
REACTIVE APPROACH
Reactive approach เป็นอีกวิธีการในการจัดการ Form ใน Angular
23
<form>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username"
class="form-control">
</div>
<div class="form-group">
<label for="email">email</label>
<input type="text" id="email"
class="form-control">
</div>
<div class="radio"
*ngFor="let gender of genders">
<label>
<input type="radio"
[value]="gender">{{ gender }}
</label>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
REACTIVE APPROACH
ในส่วนของ app.module.ts การท างานด้วย Reactive ไม่จ าเป็นต้องใช้ FormsModule แต่จะใช้ ReactiveFormsModule แทน
24
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
ReactiveFormsModule
],
...
})
REACTIVE APPROACH
การก าหนดค่าระหว่าง HTML และ Typescript สามารถท าได้ดังนี้ที่ไฟล์ app.component.ts ประกาศตัวแปร signupForm เป็นตัวแปรประเภท FormGroupจากนั้นสร้าง method ngOnInit() ส าหรับก าหนดค่าเริ่มต้นของ FormControl ต่าง ๆ ในหน้าจอ
25
export class AppComponent implements OnInit {
genders = ['male', 'female'];
signupForm: FormGroup;
ngOnInit() {
this.signupForm = new FormGroup({
'username': new FormControl(null),
'email': new FormControl(null),
'gender': new FormControl('male')
});
}
}
REACTIVE APPROACH
ในส่วนของหน้าจอ html จะต้องระบุชื่อของ form control ให้ตรงกันกับในคลาส
26
<form [formGroup]="signupForm">
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" formControlName="username"
class="form-control">
</div>
<div class="form-group">
<label for="email">email</label>
<input type="text" id="email" formControlName="email"
class="form-control">
</div>
<div class="radio" *ngFor="let gender of genders">
<label>
<input type="radio" formControlName="gender"
[value]="gender">{{ gender }}
</label>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
REACTIVE APPROACH
ในกรณีที่ต้องการเพิ่มการเรียกใช้เมื่อมีการกดปุ่ม submit สามารถก าหนด property ของ form ได้ดังนี้
27
<form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
onSubmit(){
console.log(this.signupForm);
}
REACTIVE APPROACH FOR VALIDATION
การก าหนด Validation ส าหรับตรวจสอบเงื่อนไขการกรอกข้อมูลสามารถท าได้โดยก าหนด Validator เช่น Validator.required หรือ Validator.email เป็นต้น
ในกรณีที่ต้องการแสดงข้อความแจ้งเตือนสามารถก าหนดได้ดังนี้
28
ngOnInit() {
this.signupForm = new FormGroup({
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validators.required, Validators.email]),
'gender': new FormControl('male')
});
}
<span *ngIf="!signupForm.get('email').valid &&
signupForm.get('email').touched"
class="help-block">Please enter a valid email!</span>
ARRAY OF FORM CONTROL
ในกรณีที่ต้องการสร้าง Form Control ที่มีลักษณะ Dynamic ตามความต้องการของผู้ใช้ เช่น หากผู้ใช้ต้องการเพิ่มขึ้นมูลงานอดิเรก (Hobby) สามารถที่จะกรอกได้หลายรายการ
ขั้นตอนที่ 1 สร้าง formArrayName ชื่อว่า hobbies โดยมีปุ่ม Add และ loop แสดง Textbox ตามจ านวนครั้งที่กดปุ่ม
29
<div formArrayName="hobbies">
<h4>Your Hobbies</h4>
<button class="btn btn-default" type="button"
(click)="onAddHobby()">Add Hobby</button>
<div class="form-group" *ngFor="let hobbyControl of
signupForm.get('hobbies').controls; let i = index">
<input type="text" class="form-control" [formControlName]="i">
</div>
</div>
ARRAY OF FORM CONTROL
ที่ method ngOnInit() เพิ่มตัวแปร hobbies มีชนิดเป็น FormArray
ขั้นตอนท่ี 2 เพิ่ม method ส าหรับกดปุ่ม Add Hobby
30
ngOnInit() {
this.signupForm = new FormGroup({
'username': new FormControl(null, Validators.required),
'email': new FormControl(null, [Validators.required, Validators.email]),
'gender': new FormControl('male'),
'hobbies': new FormArray([])
});
}
onAddHobby() {
const control = new FormControl(null, Validators.required);
(<FormArray>this.signupForm.get('hobbies')).push(control);
}
31
ARRAY OF FORM CONTROL
ในส่วนของการน าค่าไปใช้งานสามารถท าได้ ดังนี้
32
<hr>
<div class="row" *ngIf="submitted">
<div class="col-xs-12">
<h3>Your Data</h3>
<p>Username: {{ signupForm.value.username }}</p>
<p>Mail: {{ signupForm.value.email }}</p>
<p>Gender: {{ signupForm.value.gender }}</p>
<p>Hobbies: </p>
<div *ngFor="let hobby of signupForm.value.hobbies">
{{hobby}}
</div>
</div>