athome-developer’s blog

不動産情報サービスのアットホームの開発者が発信するブログ

AngularでAtomicDesign的にコンポーネントを作ってみた3

こんにちは、情報システム部の高野です。
AngularでAtomicDesignの第三弾です。 以前の記事はこちら

dblog.athome.co.jp dblog.athome.co.jp

今回は、前回までに作ったボタンとテキストボックスを使って
検索フォームコンポーネントを作ってみます。
前回まではAtomとしてコンポーネントを作っていましたが
今回の検索フォームはAtomAtomをくっつけたMolecule(分子)になります。

バージョンなど

前回同様です。
Angularは7.2.0 ブラウザはChromeのバージョン73を使用しています。
IEでは利用できないCSSの機能があると思うので試す時はその他のブラウザをご利用ください。

検索フォームを作る

上記で書いたとおりボタンコンポーネントとテキストボックスコンポーネントを使って
検索フォームコンポーネントを作ります。

単純に要素を並べてコンポーネントを作ってみる

ne generateコマンドで検索フォームコンポーネントを作成し編集していきます。

search-form.component.html
<p>
  <app-text-box color="primary"></app-text-box>
  <app-button size="small" color="primary">検索</app-button>
</p>

ボタンとテキストボックスのcolorprimaryに固定します。
外部から変更できる必要は無いという判断です。
ボタンの大きさもsmallに固定しましたが、あとで外部から設定できるように変更します。

現状の見た目は下図のようになります。
f:id:taktak1974:20190416133426p:plain

テキストボックスとボタンの間がつまりすぎているのでmarginを設定します。

search-form.component.scss
app-text-box {
  margin-right: 10px;
}

テキストボックスコンポーネントに対してmarginを設定しました。
この結果、下図のような見た目になります。
f:id:taktak1974:20190416133445p:plain

Angularは、コンポーネントのタグもそのままHTMLに残るのでこのように指定すると
下図のようにCSSが設定されます。
f:id:taktak1974:20190416133504p:plain 注意点としては、このコンポーネント要素はinlineなのでmargin-topなどは効かないです。
必要に応じてinline-blockなどにする必要があります。

外部から大きさを変更してみる

単純にボタンとテキストボックスにパラメータを引き渡すだけなので
ここに載せるほどのことでもないのですが、一応載せておきます。

search-form.component.ts
// 本来は1箇所に定義すべき
export type Size = 'large' | 'medium' | 'small';

// 省略
export class SearchFormComponent implements OnInit {

  @Input() size: Size = 'medium';

  // 省略
}

ボタンの時と同様に@Inputを使って外部からサイズを受け取ります。

search-form.component.html
<app-text-box color="primary" placeholder="検索ワードを入力" [size]="size"></app-text-box>
<app-button color="primary" [size]="size">検索</app-button>

外部から受け取ったsizeをそのままテキストボックスとボタンに渡すだけです。
CSSの追加もありません。
※前回のテキストボックスでサイズを変更できるように作っていません。
ボタンと同様に作成します。 *1
これでフォーム全体でのサイズ変更が可能になりました。
下図はそれぞれのサイズを指定して表示したものです。
f:id:taktak1974:20190416133553p:plain

呼び出し元に入力値を渡す

moleculeは、再利用できるように作成する必要があるので
ビジネスロジックを実装することはできません。
Serviceなども利用しないように作ります。
なので検索ボタンを押された時に呼び出し元に値を渡すように実装します。

search-form.component.ts
export class SearchFormComponent implements OnInit {

  // 省略

  @Output() clickSearchButton: EventEmitter<string> = new EventEmitter();

  value = '';

  // 省略

  onClick(event: any) {
    this.clickSearchButton.emit(this.value);
  }
}

@Outputで関数を受け取れるようにしてonClickメソッドでそれを実行します。
emitの引数に入力値を持つvalueフィールドを渡します。

search-form.component.html
<app-text-box color="primary" placeholder="検索ワードを入力" [size]="size" [(ngModel)]="value"></app-text-box>
<app-button color="primary" [size]="size" (click)="onClick()">検索</app-button>

テキストボックスにngModelを追加します。
ボタンのclickイベントでonClickメソッドを呼び出します。

app.component.html
<app-search-form size="medium" (clickSearchButton)="onSearchButtonClick($event)"></app-search-form>

呼び出し側でclickSearchButtonイベントにメソッドを関連付けます。

app.component.ts
onSearchButtonClick(value: string) {
  alert(value);
}

onSearchButtonClickメソッドの引数で入力値を取得します。
ここでは結果が分かりやすいようにアラートに表示しています。
これで検索フォームコンポーネントの作成は終了です。
実際に検索するのはOrganism層以上の仕事になります。

まとめ

Atomを組み合わせてMoleculeを作ってみました。
実際に作ってみると割と簡単にできます。
ただしなにをどの層に置くのかは結構悩みます。
例えばチェックボックスは、チェック部分だけをAtomにして
それとラベルを含めてMoleculeとして作るのか
それともラベル+チェックでAtomなのかとか
MoleculeにMoleculeを含むのはありにするのかとか
OrganismでサーバーとのやりとりをするのかサーバーとのやりとりはPageだけにするのかとか
その辺りのルールは各プロジェクトなりで決めていく必要があります。

AtomicDesignに関して弊社では、新入社員研修で作成するシステムで試してみようと考えています。
それ以外にもコンシュマーメディア側のシステムでも実装予定になっています。
最終的には、社内共通で使えるものができれば良いなと考えています。

*1:プレースホルダーも追加しています。