angularのDirectiveにおけるHostデコレーターについて


formを作るときに何気なく使っているformControlNameですが、ふと本家のソースを読んでみたら、普段使わないような記法が出てきたので調べてみました。

今回はHostデコレーターに注目します。

環境

  • angular 2.3.1
  • angular/material 2.0.0-beta.2

シンプルなフォーム

とりあえずinputが一つあるだけの簡単なフォームを作成します

コード例

simple-form.component.ts
@Component({...})
export class SimpleFormComponent {
  form: FormGroup;

  constructor(
    private fb: FormBuilder
  ) {
    this.form = this.fb.group({
      user: ['default user name'],
    });
  }

  onSubmit() {
    console.log(this.form.value);
  }
}
simple-form.component.html
<md-card>
  <md-card-title>SimpleForm</md-card-title>
  <md-card-content>
    <form [formGroup]="form" novalidate (ngSubmit)="onSubmit()">
      <md-input-container>
        <input mdInput formControlName="user">
      </md-input-container>
      <p>{{ form.get('user').value }}</p>
      <div>
        <button md-button>Submit</button>
      </div>
    </form>
  </md-card-content>
</md-card>

material使っちゃったので見づらいですが、formGroupを作り、userプロパティにformControlがセットされただけのシンプルなフォームです。

formControlNameのソースを見る

執筆時点 https://github.com/angular/angular/blob/bebedfed24d6fbfa492e97f071e1d1b41e411280/packages/forms/src/directives/reactive_directives/form_control_name.ts

constructor(
      @Optional() @Host() @SkipSelf() parent: ControlContainer,

ソースを眺めると、なんとなくformGroupを参照してそうな雰囲気です。

Hostデコレーター

any directive that matches the type between the current element and the Shadow DOM root.

DOMを辿って宣言した型に該当する要素を参照できると。
すごい。なんでもやれそうな気がしてきますね。

formGroupから、特定のformControlを取得するDirectiveを書いてみる

Hostアノテーションを用いることによって、formGroupが取得出来そうなので、formGroupの取得および、指定されたプロパティの値をログに出すようなDirectiveを書いてみます。

コード例

fake-form-control-name.directive.ts
@Directive({
  selector: '[appFakeFormControlName]'
})
export class FakeFormControlNameDirective implements AfterContentInit {
  @Input('appFakeFormControlName') appFakeFormControlName: string;
  private parent: FormGroupDirective;

  constructor(
    @Host() parent: FormGroupDirective
  ) {
    this.parent = parent;
  }

  ngAfterContentInit() {
    console.log(
      this.parent.form.get(this.appFakeFormControlName).value
    );
  }
}

この状態で動かすと、コンソールに 'default user name' が流れるのが確認できました