テキストを色々なケースに変換する Angular アプリを作った
最近精力的に Angular Utilities を作っていた。今回は、入力したテキストをスネークケースにしたり、キャメルケースにしたりするツールを作った。
ケース変換に使ったのは Lodash。Underscore.js をベースにした汎用ライブラリの、文字列操作部分を利用した。
- 参考 : Lodash
まんま Lodash#camelCase()
などのメソッドが存在しているので、ほとんどコレを叩いてやるだけ。
ただ、表示欄を沢山作って、それぞれに値をバインドするのをベタ書きで作るのは面倒なので、以下のようなオブジェクトを用意し、ループによって描画や処理をさせるようにしてみた。
@Component({ /* 省略 */ })
export class CaseConverterComponent implements DoCheck {
/** 入力値 */
public input: string = '';
/**
* 様々なケースに変換するオブジェクトたち
*
* - title : 画面に表示する「変換方法」のラベル
* - value : テキストエリアに表示する「変換結果」
* - transform : 行データを変換し返却する関数
* - isCollapsed : 行を縮小表示するかどうか
*/
public cases: any = {
pascal : { title: 'パスカルケース', value: '', transform: line => _.upperFirst(_.camelCase(line)), isCollapsed: false },
camel : { title: 'キャメルケース', value: '', transform: line => _.camelCase(line) , isCollapsed: false },
kebab : { title: 'ハイフンケース', value: '', transform: line => _.kebabCase(line) , isCollapsed: false },
snake : { title: 'スネークケース', value: '', transform: line => _.snakeCase(line) , isCollapsed: false },
upperSnake: { title: 'アッパースネークケース', value: '', transform: line => _.toUpper(_.snakeCase(line)) , isCollapsed: false },
lower : { title: 'ロウワーケース', value: '', transform: line => _.lowerCase(line) , isCollapsed: false },
upper : { title: 'アッパーケース', value: '', transform: line => _.upperCase(line) , isCollapsed: false }
};
/**
* 変更を監視してケースを変換する
*/
public ngDoCheck(): void {
// 入力値を行ごとに配列に分割する
const lines = this.input.split('\n');
// ケースごとに処理する
Object.keys(this.cases).forEach((key) => {
this.cases[key].value = lines.map((line) => {
// 行ごとに所定の変換メソッドを利用する
return this.cases[key].transform(line);
}).join('\n'); // 配列を結合して返す
});
}
}
このようにすると、cases.pascal.value
や cases.camel.value
が変換後の値を持つことになる。
ただ、今回、メンバ変数 cases
を Array ではなく Object で作ったので、画面側で *ngFor
によるループ表示ができない。連想配列を順に操作する時は、
Object.keys(this.cases).forEach((key) => {
this.cases[key]; // コレで「pascal」や「camel」にアクセスできる
});
このように Object.keys()
を使う必要がある。
コレを画面側で実現するために、連想配列を Angular パイプで渡し、キーの配列を返す、独自の「KeyPipe」なるものを作った。
import { Pipe, PipeTransform } from '@angular/core';
/** 連想配列のキーの配列を返すパイプ : オブジェクトを *ngFor でループさせたい時に */
@Pipe({ name: 'keys' })
export class KeysPipe implements PipeTransform {
transform(value: any, args?: any): any {
return Object.keys(value);
}
}
パイプはコレだけ。
HTML 側では、このパイプを利用して以下のようにケース変換結果欄を作っている。
<!-- さきほどのパイプを使い、cases のキーを順にループする -->
<tr *ngFor="let key of cases | keys">
<!-- ラベルのセルをクリックすると、その行の高さを縮めたりできる -->
<td (click)="cases[key].isCollapsed = !cases[key].isCollapsed">
<!-- ラベルを表示する -->
{{ cases[key].title }}
</td>
<td>
<!-- 変換結果を表示。ngStyle を利用して height 値を動的に書き換える -->
<textarea [value]="cases[key].value" readonly
[ngStyle]="{ 'height': cases[key].isCollapsed ? '2.5em' : '4em' }">
</textarea>
</td>
</tr>
こんな感じ。KeyPipe なんて Angular 組み込みで作っておいてくれても良さそうなのにね〜。というか cases
を連想配列にする必要がなかった…!?w