albatrosary's blog

UI/UXとエンタープライズシステム

@Input and @Output of Angular2

ようやく触り始めてるAngular2です。Angular2に定義されている@Input@Outputについて触ってみている、ぱっと見キモい。どんな振る舞いをしているのかイマイチわからないのでメモ程度に備忘します。@Input@Outputは梱包しているComponentと梱包されているComponent(ダジャレではない)の関係をまず定義する必要があります。

f:id:albatrosary:20160414133717p:plain:w200

@Input@Outputも梱包されているdetail-appへ定義する

  • @Inputは「my-app」から属性経由で値を「detail-app」が受け取るパターン
  • @Outputは「detail-app」から「my-app」へイベントを送るパターン

f:id:albatrosary:20160414134057p:plain:w400

@Inputが扱うのは文字列で@Outputはイベント通知できるPrimise的なもので、でないと「Uncaught (in promise): TypeError: this.directive_1_0.hoge.subscribe is not a function」というエラーを投げる。

具体的には One framework. - Angular 2 にあるサンプルを見るとだいたい分かる。

@Input サンプル

「my-app」に「detail-app」を定義し、bank-nameaccount-idの値を入れる。それをコンポーネント「detail-app」に渡す。

f:id:albatrosary:20160414142239p:plain:w500

具体的には次の通り:

(my-app)

@Component({
  selector: 'my-app',
  template: `
    <detail-app bank-name="RBC" account-id="4747"></detail-app>
  `,
  directives: [DetailApp]
})

受け取る「detail-app」は次のように定義する。

(detail-app)

@Component({
  selector: 'detail-app',
  template: `
    <div>Bank Name: {{bankName}}</div>
    <div>Account Id: {{id}}</div>
    <div>normalizedBankName: {{normalizedBankName}}</div>
  `
})

class DetailApp {
  @Input('bank-name') bankName: string;
  @Input('account-id') id: string;
  normalizedBankName: string;
  constructor() {
    this.normalizedBankName = 'ほげ'
  }
}

通常「{{}}」で値を表示する場合はnormalizedBankNameのように使うが@Inputを使って属性で定義された値を取得できる。

@Input('bank-name') bankName: string;について、@Input(…)内で定義されているのがカスタムタグで定義したときの属性の書き方(つまり<detail-app bank-name="RBC">)で、その隣の「bankName: string」がコンポーネントで利用するときの変数(つまり<div>Bank Name: {{bankName}}</div>)になる。

@Output サンプル

「my-app」に「detail-app」を定義し「detail-app」に発火用イベントを定義する。発火したイベントの具体的な処理を「my-app」に書く。

f:id:albatrosary:20160414142627p:plain:w500

具体的には

(my-app)

@Component({
  selector: 'my-app',
  template: `
    <detail-app (everySecond)="everySecond()" (everyFiveSeconds)="everyFiveSeconds()"></detail-app>
  `,
  directives: [DetailApp]
})
class App {
  everySecond() { console.log('second'); }
  everyFiveSeconds() { console.log('five seconds'); }
}

イベント発火させる「detail-app」は次のように定義する。

(detail-app)

@Directive({
  selector: 'detail-app'
})
class DetailApp {
  @Output() everySecond = new EventEmitter();
  @Output('everyFiveSeconds') five5Secs = new EventEmitter();
  constructor() {
    setInterval(() => this.everySecond.emit("event"), 1000);
    setInterval(() => this.five5Secs.emit("event"), 5000);
  }
}

参考資料

参考資料はこちら

@Directive と @Component の使い分け

@Componentはtemplate必須で@Directiveはtemplate必須で無い。@Componentはtemplateを梱包する場合に利用する(ので通常の利用方法)。今回の@Outputサンプルのようにテンプレートを利用しない場合は@Directiveで良い。なので上記@Directiveのサンプルは@Componentを使って

@Component({
  selector: 'detail-app',
  template: ``
})
class DetailApp {
  @Output() everySecond = new EventEmitter();
  @Output('everyFiveSeconds') five5Secs = new EventEmitter();
  constructor() {
    setInterval(() => this.everySecond.emit("event"), 1000);
    setInterval(() => this.five5Secs.emit("event"), 5000);
  }
}

とも書ける

@Inputの書き方注意

足し算の注意で型をnumberにしたからっていいものではない。

@Component({
  selector: 'detail-app',
  template: `
    <div>Bank Name: {{bankNum+1}}</div>
  `
})

class DetailApp {
  @Input('bank-number') bankNum: number;
}

「detail-app」タグを書くときには次のように[bank-number]とする必要がある

@Component({
  selector: 'my-app',
  template: `
    <detail-app [bank-number]="12"></detail-app>
  `,
  directives: [DetailApp]
})

@Inputと@Outputは@無しでも

@Input@Outputは@無しでも書けます。つまり

(detail-app)

@Component({
  selector: 'detail-app',
  template: `
    <div>Bank Name: {{bankNumber+1}}</div>
  `,
  inputs: ['bankNumber: bank-number']
})

class DetailApp {
  bankNumber: number;
}

ただこの書き方は「Angular2 Style Guilde」ではよろしくないということです、よろしく。

github.com

おわり