Angular Tutorial 5章

基本的なフォーム(P2602)

src/app/app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule} from '@angular/forms'  // formsModuleを追加
import { AppComponent }  from './app.component';
import { BookComponent }  from './book.component';
import { UserComponent }  from './user.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule ], // formsModuleを追加
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html',
})
export class AppComponent {
  user = {
    mail: 'hoge@example.com',
    passwd: '',
    name: '名無権兵衛',
    memo: 'メモ!',
    code: ''
  };

  show() {
    console.log('メールアドレス:' + this.user.mail);
    console.log('パスワード:' + this.user.passwd);
    console.log('名前(漢字):' + this.user.name);
    console.log('備考:' + this.user.memo);
  }
}

src/app/app.component.html

<form #myForm="ngForm" (ngSubmit)="show()" novalidate>
<div>
  <label for="mail">メールアドレス:</label><br />
  <input id="mail" name="mail" type="email"
    [(ngModel)]="user.mail" #mail="ngModel" required email />
  <span *ngIf="mail.errors?.required">メールアドレスは必須です。</span>

  <!--hasErrorメソッドの場合-->
  <!--<span *ngIf="mail.hasError('required')">メールアドレスは必須です。</span>-->

  <span *ngIf="mail.errors?.email">
    メールアドレスを正しい形式で入力してください。</span>
</div>
<div>
  <label for="passwd">パスワード:</label><br />
  <input id="passwd" name="passwd" type="password"
    [(ngModel)]="user.passwd"
    required minlength="6" #passwd="ngModel" />
  <!--<span *ngIf="passwd.errors?.required">
    パスワードは必須です。</span>-->
  <span *ngIf="passwd.errors?.required && passwd.dirty">
    パスワードは必須です。</span>
   <span *ngIf="passwd.errors?.minlength">
    パスワードは6文字以上で入力してください。</span>
</div>
<div>
  <label for="name">名前(漢字):</label><br />
  <input id="name" name="name" type="text" [(ngModel)]="user.name"
    required minlength="3" maxlength="10" #name="ngModel" />
  <span *ngIf="name.errors?.required">
    名前(漢字)は必須です。</span>

  <!--入力項目の単位でエラーの有無をチェック-->
  <!--<span *ngIf="name.invalid">
    名前(漢字)は必須です。</span>-->

  <span *ngIf="name.errors?.minlength">
    名前(漢字)は3文字以上で入力してください。</span>
  <span *ngIf="name.errors?.maxlength">
    名前(漢字)は10文字以内で入力してください。</span>
</div>
<div>
  <label for="code">コード</label><br />
  <input id="code" type="text" name="code" [(ngModel)]="user.code"
    required pattern="\d{6}" #code="ngModel" />
  <span *ngIf="code.errors?.required && passwd.dirty">コードは必須です。</span>
  <span *ngIf="code.errors?.pattern">数字6桁で入力してください</span>
</div>
<div>
  <label for="memo">備考:</label><br />
  <textarea id="memo" name="memo" rows="5" cols="30"
    [(ngModel)]="user.memo" maxlength="10" #memo="ngModel"></textarea>
  <span *ngIf="memo.errors?.maxlength">
    備考は10文字以内で入力してください。</span>
  <span *ngIf="memo.dirty">memoを変更しましたね。</span>
</div>
<div>
  <!-- <input type="submit" value="送信"
    [disabled]="myForm.invalid" /> -->

  <!--サブミット済みかどうかを判定-->
  <input type="submit" value="送信"
    [disabled]="myForm.invalid || myForm.submitted" />

    <!--pristine/dirtyプロパティを利用したリセットボタン-->
    <!--<input type="reset" value="リセット" [disabled]="myForm.pristine" />-->
    <!--<input type="reset" value="リセット" [disabled]="!myForm.dirty" />-->
</div>
</form>
<pre>{{myForm.value | json}}</pre>

src/app/app.component.css

input.ng-dirty.ng-invalid { background-color: red; }

ラジオボタン(P2763)

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items: Item[] = [
    {id: 1, name: "item1"},
    {id: 2, name: "item2"},
  ];
}

class Item {
  id: number;
  name: string;
}

src/app/app.component.html

<form #myForm="ngForm" (ngSubmit)="show()" novalidate>
  <ng-container *ngFor="let item of items; index as i">
    <label>
      <input type="radio" name="animal"
        [(ngModel)]="selected"
        [value]="item.name" [checked]="selected == item.name"
        (change)="show(i)">{{item.name}}
    </label><br />
  </ng-container>
</form>

チェックボックス(P2775)

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  datas: Data[] = [
    {item: {id: 1, name: "item1"}, selected: false},
    {item: {id: 2, name: "item2"}, selected: false}
  ]
}

class Item {
  id: number;
  name: string;
}

class Data {
  item: Item
  selected: boolean;
}

src/app/app.component.html

<form #myForm="ngForm" (ngSubmit)="show()" novalidate>
  <ng-container *ngFor="let data of datas; index as i">
    <label>
      <input type="checkbox" name="animal{{i}}"
        [(ngModel)]="datas[i].selected"
        [value]="data.item.name"
        (change)="show()">{{data.item.name}}
    </label><br />
  </ng-container>
</form>

選択ボックス(P2783)

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  selected = 'hamster';
  data = [
    { label: '犬', value: 'dog', disabled: false },
    { label: '猫', value: 'cat', disabled: false },
    { label: 'ハムスター', value: 'hamster', disabled: false },
    { label: '金魚', value: 'fish', disabled: true },
    { label: '亀', value: 'turtle', disabled: false }
  ];
  show() {
    console.log('選択した値' + this.selected);
  }
}

src/app/app.component.html

<form #myForm="ngForm">
  <select name="animal" [(ngModel)]="selected" (change)="show()">

  <!--リストボックスの生成-->
  <!--<select name="animal" size="3"
    [(ngModel)]="selected" (change)="show()" multiple>-->

    <option value="">ペットを選択してください</option>
    <option *ngFor="let item of data"
      [value]="item.value" [disabled]="item.disabled"
      [selected]="item.value === selected">{{item.label}}</option>
  </select>
</form>

選択ボックスをグループ化(P2796)

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  selected = '';
  data = {
    '哺乳類': [
        { label: '犬', value: 'dog', disabled: false },
        { label: '猫', value: 'cat', disabled: false },
        { label: 'ハムスター', value: 'hamster', disabled: false },
      ],
    '魚類': [
        { label: '金魚', value: 'fish', disabled: true },
        { label: '鯉', value: 'carp', disabled: false },
        { label: '熱帯魚', value: 'tropical fish', disabled: false },
      ],
    '爬虫類': [
        { label: '亀', value: 'turtle', disabled: false },
        { label: 'トカゲ', value: 'lizard', disabled: false },
        { label: 'ヘビ', value: 'snake', disabled: false }
      ]
  };

  keys(obj: Object) {
    return Object.keys(obj);
  }

  show() {
    console.log('現在値:' + this.selected);
  }
}

src/app/app.component.html

<form #myForm="ngForm">
  <select name="animal" [(ngModel)]="selected" (change)="show()">
    <option value="">ペットを選択してください</option>
    <optgroup *ngFor="let group of keys(data)" label={{group}}> // 定義したkeysメソッドを実行
      <option *ngFor="let item of data[group]"
        [value]="item.value" [disabled]="item.disabled"
        [selected]="item.value === selected">{{item.label}}
      </option>
    </optgroup>
  </select>
</form>

文字数カウント(P2844)

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  template: `
    <form>
      <textarea cols="70" rows="5" name="tweet"
        [(ngModel)]="tweet" (input)="setcolor()"></textarea>
      <div [ngStyle]="myStyle">{{count}}</div>
    </form>
  `
})
export class AppComponent {
  max = 10;
  tweet= '';
  count = this.max;
  myStyle = { color: '#00f', fontWeight: 'normal' };

  setcolor() {
    this.count = this.max - this.tweet.length;
    if (this.count > 5) {
      this.myStyle = { color: '#00f', fontWeight: 'normal' }
    } else if (this.count > 0) {
      this.myStyle = { color: '#f0f', fontWeight: 'normal'  };
    } else {
      this.myStyle = { color: '#f00', fontWeight: 'bold' };
    }
  }
}

テキストボックスの内容を ; で区切る(P2854)

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

@Component({
  selector: "my-app",
  template: `
    <form>
    <label for="mail">メールアドレス:</label>
    <textarea id="mail" name="mail" type="text"
      [ngModel]="emails.join(';')"
      (ngModelChange)="emails=$event.split(';')">
    </textarea>
    </form>
    <ul>
      <li *ngFor="let email of emails">{{email}}</li>
    </ul>
  `
})
export class AppComponent {
  emails: String[] = [];
}

ファイルアップロード(P2889)

src/app/app.module.ts

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

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

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

src/app/app.module.ts

import { Component } from '@angular/core';
import { Http, Headers, RequestOptions } from "@angular/http";

@Component({
  selector: "my-app",
  template: `
    <form>
      <input id="upfile" name="upfile"  type="file" #fl
        accept="image/*" (change)="upload(fl.files)" />
    </form>
  `
})
export class AppComponent {
  constructor(private http:Http) {}

  upload(list: any) {
    if (list.length <= 0) { return; }

    let f = list[0];
    let data = new FormData();
    data.append('upfile', f, f.name);

    this.http.post('app/upload.php', data)
      .subscribe(
        data => console.log(data),
        error => console.log(error)
      );
  }
}

モデル駆動型のフォーム(P2906)

src/app/app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule }   from '@angular/forms'; // 追加

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

@NgModule({
  imports:      [ BrowserModule, ReactiveFormsModule ], // ReactiveFormsModule を追加
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

formControl

入力要素の初期値とバリデーションを管理

formControlGroup

formControlをまとめる

src/app/app.component.ts

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

@Component({
  selector: "my-app",
  templateUrl: './app.component.html' 
})
export class AppComponent {
  // formControleオブジェクト作成
  mail = new FormControl('hoge@example.com', [
    Validators.required,
    Validators.email
  ]);
  passwd = new FormControl('',[
    Validators.required,
    Validators.minLength(6)
  ]);
  name = new FormControl('名無権兵衛',[
    Validators.required,
    Validators.minLength(3),
    Validators.maxLength(10)
  ]);

  // 初期値と状態を指定
  /*
  name = new FormControl(
  {
    value: '名無権兵衛',
    disabled: true
  },
  [
    Validators.required,
    Validators.minLength(3),
    Validators.maxLength(10)
  ]);
  */

  memo  = new FormControl('メモ',[
    Validators.maxLength(10)
  ]);

  // builderを使ってformControlをグループ化
  myForm = this.builder.group({
    mail: this.mail,
    passwd: this.passwd,
    name: this.name,
    memo: this.memo
  });

  constructor(private builder: FormBuilder) { } // FormBuilderをbuilerに注入してる

  show() {
    console.log('メールアドレス:' + this.mail.value);
    console.log('パスワード:' + this.passwd.value);
    console.log('名前(漢字):' + this.name.value);
    console.log('備考:' + this.memo.value);
    console.log('すべて:');
    console.log(this.myForm.value);
  }
}

src/app/app.component.html

<form [formGroup]="myForm" (ngSubmit)="show()">
<div>
  <label for="mail">メールアドレス:</label><br />
  <input id="mail" name="mail" type="email"
    [formControl]="mail" />
  <span *ngIf="mail.errors?.required">メールアドレスは必須です。
  </span>
  <span *ngIf="mail.errors?.email">
    メールアドレスを正しい形式で入力してください。</span>
</div>
<div>
  <label for="passwd">パスワード:</label><br />
  <input id="passwd" name="passwd" type="password"
    [formControl]="passwd" />
  <span *ngIf="passwd.errors?.required">
    パスワードは必須です。</span>
   <span *ngIf="passwd.errors?.minlength">
    パスワードは6文字以上で入力してください。</span>
</div>
<div>
  <label for="name">名前(漢字):</label><br />
  <input id="name" name="name" type="text" [formControl]="name" />
  <span *ngIf="name.errors?.required">
    名前(漢字)は必須です。</span>
  <span *ngIf="name.errors?.minlength">
    名前(漢字)は3文字以上で入力してください。</span>
  <span *ngIf="name.errors?.maxlength">
    名前(漢字)は10文字以内で入力してください。</span>
</div>
<div>
  <label for="memo">備考:</label><br />
  <textarea id="memo" name="memo" rows="5" cols="30"
    [formControl]="memo"></textarea>
  <span *ngIf="memo.errors?.maxlength">
    備考は10文字以内で入力してください。</span>
</div>
<div>
  <input type="submit" value="送信" 
    [disabled]="myForm.invalid" />
</div>
</form>