こんにちは、あかいです🐗
本記事は Angular Advent Calendar 2022 の7日目の記事の代打です🏏⚾
6日目は@beltway7さんで8日目は@KOHETsさんです!
今作のポケモンには、「さまざまな材料をかけあわせてサンドウィッチを作る」というコンテンツがあります。
これを行うとき、手探りで作るのがすごく大変だったので、いい感じにレシピを検索できるWebアプリを作りました🥪
なんと、作り始めてその当日(※1)にリリースすることができ、
今回ひしひしとAngularのパワーを実感したので、その所感をお伝えしたいと思います!
(※1 現在、リリース後4人日くらいかけて改善/機能追加を行っちゃってはいますが、
コアのデザインや機能に大きく変更はないはずです)
実際にアプリを動かしながらこの記事を読んで「あ〜ここがこうね〜」みたいなのを感じ取ってもらえればと思います。
Anguar CDK
今回は「サラッとイイカンジでオリジナル」にUIを構築したかったので、Angular CDKを利用することにしました!
今回利用しているCDKのモジュールは、次の3コです!
セレクトボックス - CdkListboxModule
超お手軽にアクセシビリティに配慮したセレクトボックスを実現しています。
勝手にセレクトボックス内のキーボード操作機能とかついてくれるのサイコーですね……。
共通コンポーネントとして/src/app/shared/components/selectbox/selectbox.component.*
みたいなのを切り出しました。
実装はtsとhtmlで50行程度です、スバラシ!
アコーディオン - CdkAccordionModule
リリース後のスマホ表示対応で、表示領域の確保を行うために
セレクトボックスをアコーディオンで開閉するようにしました。
正直変数用意してclassを付け替えるだけの実装とあまり変わりないのですが、
こちらもCDKを利用することでrole属性がイイカンジに付与されてアクセシビリティを担保できます。
ただ、今回ちょっとめんどくさがって、CSSのメディアクエリでアコーディオンの有効/無効化を操作していますので、
アコーディオンが使われていないデスクトップUIでも、DOMにrole="button"
がついてしまっています。
本来はLayoutModuleとかを利用して、
cdk-accordion要素の切り替え操作とかをしなければいけないかもしれませんネ……。
仮想スクロール - ScrollingModule
CDKのv15で無事experimentalが外れた仮想スクロール機能も活用しています。
当初はCdkTableを利用してUIを組みました。
しかし、本アプリのように巨大なリストを一覧表示するためには、
やはり仮想スクロールを用いたほうがパフォーマンスが出るため、
リリース後にul/li要素を用いたcdk-virtual-scroll-viewportに置き換えました。
パフォーマンスの計測などは今回行っていないのですが、
実際に動かしてみて、想像以上に応答速度がはやくなったので驚きましたね!
CDKに期待してたけどなかったのであきらめたこと
Angular MaterialにはタブUIが用意されているのですが、残念ながらCDKには同じものはないようでした😢
ここは我慢して、他のコンポーネントでも利用した前述のCdkListboxModuleを使って
CSSでタブっぽく仕上げました。
role属性には値としてtab
とかもあるのですが、ListBoxを利用したここにはもちろんそういったものは入っていないので、
アクセシビリティ的には☓ですネ😢😢
v15の目玉機能たちについて
このアプリを作り始めたときは、ちょうどv15がリリースされたばかりなのですが、
v15の目玉機能の一部も早速利用しています!
Standalone components
v15からはNgModuleを不要としたStandalone componentsというものがstableになっておりまして、
今回は試しに使ってみました!
必要なファイル数が減ったことや、
コンポーネントをひと目見るだけで依存がすべてマルワカリになるのが嬉しかったですね。
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
// Common Modules
CommonModule,
NgOptimizedImage,
RouterModule,
// CDK Modules
CdkListboxModule,
ScrollingModule,
// Components and Pipes
AdsComponent,
SearchBoxSectionComponent,
SelectboxComponent,
ShareButtonsComponent,
I18nPipe,
],
styleUrls: ['./app.component.scss'],
})
importsに全部ドバっと記述されるので、こんな感じでコメントアウトしておくと見やすいんでないかな〜と思います!
NgOptimizedImage
v15からstableになったAPIの中には他にもNgOptimizedImageディレクティブというものが存在します。
これは、img要素のsrc属性を拡張したngSrc属性を提供し、遅延読み込みなどによるさまざまな画像の最適化を行います!
やってくれることがいろいろあって割とよくわかってないところもあるのですが、
enforces a number of image best practices
と公式ドキュメントで言ってるので、使い得でしょう!!
多国語化 i18n(の反省とか)
Angularには公式ドキュメントに載ってるi18nの手法があります。
いわく、@angular/localize
パッケージをng add
することで有効化できるようです。
i18n対応はなんだかんだ一度も経験がなく、
「色々と準備が必要で面倒だなぁ」と思ったので、
今回は次のようなパイプを作ってオレオレi18nをしてしまいました。
@Pipe({
name: 'i18n',
standalone: true,
})
export class I18nPipe implements PipeTransform {
constructor(private settingsService: SettingsService) {}
transform(value: string, placeholders: (string | number)[] = []): string {
const lang = this.settingsService.getLanguage();
const dict = lang === 'ja' ? ja : en;
const text = dict.get(value) || value;
const placed = placeholders.reduce<string>(
(a, b) => a.replace('%%%', `${b}`),
text
);
return placed;
}
}
// ...ここに百行くらいの翻訳Mapがある
そんなに悪くない感じもするのですが、SEOの観点から見ると、
ロボットに認識される言語が翻訳前のものになってしまうなどの問題があります。
(実装しながら気づきました。)
工数が取れるのであれば、やはり公式のi18nに則るのがよいですかねぇ😢
Angularでは人気のi18nライブラリにngx-translateというものがあるので、こちらもいつか触ってみたいですね。
その他
爆速開発に一役買ってくれるライブラリとして、eslint/prettierなんかも入れました!
勢いだけの一人開発でも、キレイなコードにしたいですよね!!!
この辺をもろもろ加味して、今回は次のような感じでライブラリを追加してます!
$ ng add @angular/cdk
$ ng add @angular-eslint/schematics
$ npm i -D prettier eslint eslint-config-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser @angular/language-service
The Great and Powerful Angular!!