Angular Form

Angular Form

Angular Form

Angular Form은 Template-driven Forms와 Ractive Forms를 제공한다.

Template-driven Forms

  • 간단한 form (Form Control 한두개)을 구성할 때 사용

Reactive Forms

  • 복잡한 form을 구성할 때 사용

Form은 Client side에서 중요한 역할을 한다. 사용자의 입력을 서버로 전송하고 페이지를 전환할 수 있는 element이다. Server는 많은 Client 요청을 처리하므로 form을 통해서 받은 사용자 입력의 유효성 검사(validation)를 Client side에서 하는 것이 기본이다.

Template-driven Forms

  • Template-driven Form은 Component template에서 Directive를 사용해서 Form을 구성하는 방식
  • 각 field의 형식이나 validation 검증 규칙을 모두 template에서 정의
  • Template-driven Form은 NgForm, NgModel, NgModelGroup Directive 중심으로 동작
    1
    2
    3
    4
    5
    6
    // app.module.ts
    import { FormsModule } from '@angular/forms';

    @NgModule({
    imports: [FormsModule],
    })

NgForm Directive

  • Template-driven Form 전체를 관리하는 Directive

  • root module에 FormsModule 추가하면 NgForm Directive 선언하지 않아도 모든 form element에 자동으로 적용되어 Template-driven Form으로 동작

  • HTML standard form의 validation을 사용하지 않고 Template-driven Form의 validation을 사용하려면 아래와 같이 form element에 novalidate Attribute를 추가한다. 최신 Angular는 FormsModule을 import하면 novalidate가 자동으로 추가된다.

    1
    <form novalidate></form>
  • 반대로 NgForm Directive를 사용하지 않고 HTML standard form을 사용하려면 ngNoForm을 추가한다.

    1
    <form ngNoForm></form>

HTML Standard Form과 Template-driven Form의 차이

  • HTML Standard Form은 submit button 클릭시 form data를 서버로 전송하고 페이지를 전환

  • NgForm Directive가 적용된 Template-driven Form은 submit event를 Intercept해서 submit의 기본 동작을 막음

    • 대신 submit시 ngSubmit event를 사용해서 onSubmit event handler 사용
1
2
3
> <form #f="ngForm" (ngSubmit)="onNgSubmit(f)" novalidate>
> </form>
>
  • #f="ngForm"으로 Native DOM 대신 NgForm Instance를 Template 참조 변수에 할당한다.
  • Native DOM 요소와 다르게 ngForm Instance는 ng-valid, ng-touched 등 유용한 프로퍼티를 제공한다.
  • NgForm Directive는 NgForm Instance 생성시 자신이 적용된 Form 요소의 값이나 validation 검증 산태를 추적할 수 있는 FormGroup Instance를 생성한 후 NgForm Instance의 property로 할당한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<form #f="ngForm" (ngSubmit)="onSubmit(f)">
<input
type="text"
placeholder="email"
name="email"
pattern="^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$"
ngModel
required
#email="ngModel"
/>
<input
type="password"
placeholder="password"
name="password"
pattern="^[a-zA-Z0-9]{4,10}$"
ngModel
required
#password="ngModel"
/>
<button [disabled]="f.invalid">Submit</button>
</form>

Form의 Input 요소들에 ngModel을 추가해야 Form의 Form Control로 등록된다.

Reactive Forms

  • Template이 아닌 Component class에서 form element의 상태를 관리하는 객체인 Form Model을 구성하는 방식
  • Template-driven Form 보다 복잡한 form을 구성할 때 사용
1
2
3
4
5
6
// app.module.ts
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
imports: [ReactiveFormsModule],
})
  • Reactive Form은 FormGroup, FormControl, FormArray를 중심으로 동작

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl } from '@angular/forms';

    @Component({
    selector: 'app-root',
    template: `
    <form [formGroup]="userForm">
    <input type="text" placeholder="userid" formControlName="userid" />
    <input type="password" placeholder="userpw" formControlName="userpw" />
    <button>Login</button>
    </form>
    `,
    styles: []
    })
    export class AppComponent implements OnInit {
    userForm: FormGroup;

    constructor() {}

    ngOnInit() {
    this.userForm = new FormGroup({
    userid: new FormControl(),
    userpw: new FormControl()
    });
    }
    }
    • FormGroup Instance가 template의 form을 관리하도록 mapping

      1
      <form [formGroup]="userForm"></form>
  • FormGroup의 FormControl을 생성해서 input element와 mapping

    1
    2
    <input type="text" placeholder="userid" formControlName="userid" />
    <input type="password" placeholder="userpw" formControlName="userpw" />
  • FormControl

    FormControl

  • FormControl 이름으로 getter 만들기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import { Component, OnInit } from '@angular/core';
    import { FormGroup, FormControl, Validators } from '@angular/forms';

    @Component({
    selector: 'app-root',
    template: `
    <form [formGroup]="userForm">
    <input type="text" placeholder="userid" formControlName="userid" />
    <em *ngIf="userid.errors?.required && userid.touched">아이디를 입력해주세요</em>
    <em *ngIf="userid.errors?.pattern && userid.touched">아이디를 형식에 맞게 입력해주세요</em>
    <input type="password" placeholder="userpw" formControlName="userpw" />
    <button>Login</button>
    </form>
    <pre>userForm.value: {{ userForm.value | json }}</pre>
    <pre>userForm.valid: {{ userForm.valid }}</pre>
    `,
    styles: []
    })
    export class AppComponent implements OnInit {
    userForm: FormGroup;

    constructor() {}

    ngOnInit() {
    this.userForm = new FormGroup({
    userid: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')]),
    userpw: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')])
    });

    console.dir(this.userForm);
    }

    get userid() {
    return this.userForm.get('userid');
    }
    }
  • 비밀번호 확인을 위한 Custom Validation method

    1
    2
    3
    4
    5
    <--! template -->  
    <div formGroupName="passwordGroup">
    <input type="password" placeholder="password" formControlName="password" />
    <input type="password" placeholder="confirm password" formControlName="confirmPassword" />
    </div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// component.ts
ngOnInit() {
this.userForm = new FormGroup({
userid: new FormControl('', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')]),
passwordGroup: new FormGroup(
{
password: new FormControl('', [
Validators.required,
Validators.pattern('[a-zA-Z]{4,10}')
]),
confirmPassword: new FormControl('', [
Validators.required,
Validators.pattern('[a-zA-Z]{4,10}')
])
},
PasswordValidator.match
)
});
}
1
2
3
4
5
6
7
8
9
10
// password-validator.ts
import { AbstractControl } from '@angular/forms';

export class PasswordValidator {
static match(passwordGroup: AbstractControl) {
const password = passwordGroup.get('password').value;
const confirmPassword = passwordGroup.get('confirmPassword').value;
return password === confirmPassword ? null : { match: { password, confirmPassword } };
}
}
  • AbstractControl 모든 Control의 조상

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // new FormGroup 대신 FormBuilder 사용 시
    ngOnInit() {
    this.userForm = this.fb.group({
    userid: ['', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')]],
    passwordGroup: this.fb.group(
    {
    password: ['', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')]],
    comfirmPassword: ['', [Validators.required, Validators.pattern('[a-zA-Z]{4,10}')]]
    },
    { validator: PasswordValidator.match }
    )
    });
    }
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×