Carpe Diem

備忘録

Angular2でのForm 〜Template Driven編〜

概要

AngularのFormの書き方は主に2つあります。

1つはTemplate Drivenなやり方。
もう1つはModel Driven(Reactive Form)なやり方。

フォームの作り方を調べた時にどっちがどっちの情報なのか分からず混乱したのでまとめてみました。
今回はTemplate Drivenの方を紹介します。

環境

  • angular-cli 1.0.0-beta.25.5
  • Angular 2.4.3

完成品

今回の成果物はこちら

github.com

書き方

Submitボタンを押したらconsole.log()に吐くようなフォームを作ってみます。

template.component.html

<form #f="ngForm">
  <label>Firstname:</label>
  <input type="text" name="first" ngModel>

  <label>Lastname:</label>
  <input type="text" name="last" ngModel>

  <button type="submit" (click)="loginForm(f.value)">Submit</button>
  <div>
    <span>{{ f.value | json }}</span>
  </div>
</form>

ポイント

  • #f="ngForm"でフォームの値を変数として保持する
  • ↑の変数に値を保持させるために、inputの中でnamengModelをつける。fの各フィールド名はnameとして登録される
  • f.valueで全フィールドを見ることができる
  • 変数をView(HTML)からModel(Component)に渡すため、関数の引数にf.valueを渡す

template.component.ts

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

@Component({
  selector: 'app-template',
  templateUrl: './template.component.html',
  styleUrls: ['./template.component.css']
})
export class TemplateComponent implements OnInit {
  constructor() { }

  ngOnInit() {
  }

  loginForm(value: any) {
    console.log(value);
  }
}

今回の書き方だと特に必要な対応はないです。

app.component.html

<h1>
  {{title}}
</h1>
<app-template></app-template>

ポイント

  • <app-template></app-template>を追記

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';  // here
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { TemplateComponent } from './template/template.component';

@NgModule({
  declarations: [
    AppComponent,
    TemplateComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,  // here
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

ポイント

  • FormsModuleをimportします。

動作確認

f:id:quoll00:20170122104541p:plain

文字を入力してみます。

f:id:quoll00:20170122104557p:plain

Viewで設定していた

  <div>
    <span>{{ f.value | json }}</span>
  </div>

によって、f.valueの値が即時反映されています。注意なのはsubmitを押すまではModelには渡されないところです。
submitで関数の引数に渡されて初めてModelで処理することができます。

ngModelの書き方の違い

今回はngModelだけを書いていたのですが、調べてみると分かるようにngModelの書き方は

  • ngModel
  • [ngModel]
  • [(ngModel)]

と複数あります。これらの違いを説明していきます。


ngModel

<form #f="ngForm">
  ...
    <input type="text" name="name" ngModel>
  ...
  <button type="submit" (click)="loginForm(f.value)">Submit</button>
</form>

これは先程説明したように、入力した文字列がf.valueに入ります。 ただし、Component側には行かないので関数の引数にfを渡す必要があります。


[ngModel]

Model -> Viewへの単方向バインディングのやり方です。

<form #f="ngForm">
  ...
    <input type="text" name="name" [ngModel]="user.name">
  ...
  <button type="submit" (click)="loginForm(f.value)">Submit</button>
</form>

これはComponent側(Model側)のデータが初期値として入ります。 ただし、変更してもf.valueは変わりますが、Component内のデータuserは変わりません。 なのでComponent側に渡したければ関数の引数として渡す必要があります。

「Component側(Model側)のデータが初期値として入ります」といったように、この書き方の場合はまずComponent側に以下のように変数を用意する必要があります。

class User {
  first: string;
  last: string;
}

@Component({
  selector: 'app-template-2',
  templateUrl: './template-2.component.html',
  styleUrls: ['./template-2.component.css']
})
export class Template2Component implements OnInit {
  public user: User;  // here

  constructor() { }

  ngOnInit() {
    this.user = new User();
  }

  loginForm(value: any) {
    console.log(value);
  }
}

動作確認をしてみます。

f:id:quoll00:20170122112051p:plain

入力してみると

f:id:quoll00:20170122112104p:plain

f.valueは変わっていますが、Model側のuserは変わらないことがわかります。


[(ngModel)]

Model <-> Viewの双方向バインディングのやり方です。

<form #f="ngForm">
  ...
    <input type="text" name="name" [(ngModel)]="user.name">
  ...
  <button type="submit" (click)="loginForm()">Submit</button>
</form>

これはComponent側(Model側)のデータが初期値としてf.valueに入ります。 また変更すれば即時にComponent側のデータが変更されます。 なので関数の引数として渡す必要がありません。

Component側は先程の[ngModel]と変わりません。loginForm()の引数を削ればOKです。

動作確認をすると

f:id:quoll00:20170122112652p:plain

入力してみると

f:id:quoll00:20170122112703p:plain

f.valueuserも変わっています。

このようにModel側に即座にデータがバインドされるので、関数の引数として渡す必要がありません。

ソース