𝓗𝓪𝓹𝓹𝔂 𝓜𝓮𝓻𝓻𝔂 𝓒𝓱𝓻𝓲𝓼𝓽𝓶𝓪𝓼...


本記事は Angular Advent Calendar 2023 の24日目の記事です❗

23日目は[@ic_lifewood](https://qiita.com/ic_lifewood/items/817f7ed51440e8d4a368)さんで25日目は[@lacolaco](https://zenn.dev/lacolaco/articles/angular-advent-calendar-2023)さんです❗

---

最新のAngularの機能をふんだんに使って、
Angular Advent Calendarの記事をツリーの飾り(オーナメント)にしたサイトをつくりました❗❗

![20231224234656.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104311/c229a760-99b6-f9f4-ac22-c604f783251a.png)

- https://ver-1000000.github.io/angular-advent-calendar-2023/
- GitHub: https://github.com/ver-1000000/angular-advent-calendar-2023/

あまりおもしろみのあるサイトではないかもしれませんが、
「見た目から要件がすぐにわかる」ことを目指して作成しました。

他の方のAdvent Calendar記事に言及しつつ、実装の詳細を解説していきますので、
実際に触りながら読み進めていただければと思います❗

# 他の記事で言及されていた実装
## Prettier & ESLint
Angularを入れたら最初に設定しておきたい、リンターとフォーマッタの設定です。

https://github.com/ver-1000000/angular-advent-calendar-2023/compare/f94ebdd3667772c3fec2a43026e929f15b032698...974a305e4f86134c548fcc7a057dead8eb4215d0

次のコマンドで導入したあと、生成された`.eslintrc.json`を編集しました。

```sh
$ npm i -D prettier eslint-plugin-prettier eslint-config-prettier
$ # commit
$ ng add @angular-eslint/schematics 
```

基本的に、最新のrecommendedな設定を使いたいニンゲンなので、
prettierとの共存のための設定以外ルールの変更などは特に行っていません。
(「アップデートのたびにルールの名前や設定ファイルの構造が変わったことを検知するのが難しくて嫌じゃない？」という気持ちがある)

しかし、有用なオプトインルールはたくさんあるので、導入の際は都度一考することをお勧めします。

- [Angular ESLintの導入と推しルール6選(2023)](https://zenn.dev/komura_c/articles/9ac623f9519bf9) | [@komura-c](https://zenn.dev/komura_c)
  - 改めて眺めてみるとどれも本当に有用ですね……。

## Angular CDK
普通にWebサイトを作成すると、CDKの使いどころは結構多いのですが、今回はダイアログの作成のみに利用しています。

![20231225011120.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104311/22266394-1cd2-71ec-bdbd-f0474dd41f01.png)

UI面は非常に薄いですが、反してアクセシビリティなどのサポートなどは手厚く、
実装の際は積極的に利用したいライブラリですね。
(この画面を見ただけでも「モーダルを開いたときに、UI上の最初の要素にフォーカスを当てる」粋なアクセシビリティポイントが嬉しい)

ちなみに、Dialogは内部的に利用しているOverlayなどが、
専用のCSSを読み込まないとなんのスタイルも当てられていない状態になるので、
次の一行をstyles.cssに書いておきましょう。

```css
@import '@angular/cdk/overlay-prebuilt.css';
```

- https://material.angular.io/cdk/overlay/overview

なぜか[DialogのDocument](https://material.angular.io/cdk/dialog/overview)にはこのことが記載されていません。

CDK Dialogについて、より詳しい説明は次の記事をどうぞ。

- [Angular CDKのCdkDialogでダイアログを爆速実装
](https://qiita.com/KOHETs/items/cb1d372f63aa6a5e8f7b) | @KOHETs

## NgOptimizedImage
「開発者があまり意識しなくても画像表示を最適化してくれるくん」です。

適当に使ってみると(`NgOptimizedImage`をimportしてimgタグのsrcをngSrcにするだけ)、
実行時に警告を出してくれるので、対話的に実装できます。

https://github.com/ver-1000000/angular-advent-calendar-2023/commit/8bc1dc9c538d00d7be6ba6f0cbf05d0c0f3b7b3d#diff-773eb24612d3f52bc608a5bd030d275274f7d829083801547aa58afa57de0607R8

より詳しい説明は次の記事をどうぞ。

- [NgOptimizedImageに入門する](https://zenn.dev/hoshima19/articles/31fc0bbb3d916e) | [hoshima](https://zenn.dev/hoshima19)

:::note warn
ngSrcを記述する際はルートから始まるパスを書かないように注意！

assetsディレクトリに入れている画像を参照するために
`/assets/xxx.png`などと書くと、build時にbase-hrefを設定している場合
パスの解決ができなくなってしまうため、
`assets/xxx.png`や`./assets/xxx.png`のように書く必要がある。

筆者はデプロイ時に画像が読み込まれない自体に陥って少しだけハマりました😢
:::

## SSG(SSR)
v17からはServer Side Generation(Rendering)がヒッッジョ〜に簡単になりました❗

プロジェクト作成時、対話的にssrサポートをONにするか聞かれるので、
ここで有効化したあとは`npm run build`でSSGの生成物が作成されます。

[本サイト](https://ver-1000000.github.io/angular-advent-calendar-2023/)ではSSRオプションをオンにしてGitHub Pagesにデプロイしているので、
すでに一度レンダリングされた結果が返ってくるようになっています。

背景のパーティクルの位置やオーナメントの色がランダムで変わるようになっているので、
JSをオフにした状態でページのリロードを走らせると違いが分かりやすいです。

他にもSSRについて、次の記事でそれぞれ詳しく書かれています。

- [AngularではじめるSSR入門](https://qiita.com/nishitaku/items/b67e14a08d47447b0c37) | @nishitaku 
- [2024年から始めるAngular Universal (SSR) の本番運用](https://zenn.dev/seapolis/articles/6669b3c1cda5b0) | [海都](https://zenn.dev/seapolis)

### SSGを利用した外部リソースのキャッシング
今回、オーナメントを表示するために、アプリ内のHttpClientでQiitaのRSSフィードから記事情報を25個取得しています。

ところが、実際にブラウザを更新すると、該当の通信は走りません。

[What’s next for Server Side Rendering in Angular](https://blog.angular.io/whats-next-for-server-side-rendering-in-angular-2a6f27662b67)の記事によれば、
> We’ve also made some updates to HttpClient. It will now cache requests made on the server to avoid re-fetching that same data again on the client.

とのことなので、どうやらSSRモードを有効にするとデフォルトでいい感じにキャッシュしてくれるようです！ス、スゲーー！！！

本サイトではmainブランチへのpush時に、GitHub ActionsのCI上でビルドが走るので、
このタイミングで通信情報が永続化されるわけですね。

## Control Flow
ココ最近のアップデートでは大目玉商品ということで話題のControl Flowですが、
Advent Calendarでもやはりみなさん言及されていて、注目度が伺えます。

- [2023 年に入った Angular のさまざまなアップデート](https://kasaharu.hatenablog.com/entry/20231201/1701381600) | [kasaharu](https://kasaharu.hatenablog.com/about)
- [Angularにおける組み込み制御フローの導入とその背景](https://zenn.dev/carimatics/articles/angular_2023_builtin_control_flow) | [かっこ](https://zenn.dev/carimatics)
- [Angular v17 で新しい制御フロー構文がやってきた！マイグレーションコマンドを試してみたよ](https://qiita.com/kozy4324/items/356fd8e2429ae5142641) | @kozy4324
- [Control FlowとViewレンダリング](https://zenn.dev/noxi515/articles/angular-control-flow-rendering) | [noxi515](https://zenn.dev/noxi515)
- [Prettier の Angular サポートの仕組みと built-in control flow 対応](https://zenn.dev/sosukesuzuki/articles/1553af074e5884) | [Sosuke Suzuki](https://zenn.dev/sosukesuzuki)

本サイトでも早速使ってみました。

https://github.com/ver-1000000/angular-advent-calendar-2023/commit/8bc1dc9c538d00d7be6ba6f0cbf05d0c0f3b7b3d#diff-d44b60b354b044f81728e60164a98ff4fb38daca4b53bc3387e82027c44f0498

switch文なんかはかなりシンプルで適切な感じの書き方ができている気持ちになっていいですね。
trackByも非常に書きやすいです。

……ただ、ControlFlowの導入によってhtml内に`@`記号が使えなくなった点について、
これを解決するための参照文字列を調査するのに少しハマりました。

https://github.com/ver-1000000/angular-advent-calendar-2023/commit/8bc1dc9c538d00d7be6ba6f0cbf05d0c0f3b7b3d#diff-7d6d1704c4b8379f025a5774b6ca84afe8dce50b2c4c569868f3e3b5fe692294R6

もともとこの事象自体は知識として知っていたのですが、
「参照文字列」という言葉が頭に出てこなくて
アットマークの参照体(`&#64;`)が調べられず……。

アットマークはテンプレートに書くとコンパイルエラーが出るのですが、
ブレースの直前に書いてしまい、構文のパースが影響して
次のようなエラー内容になってしまい、適切な対応方法がしばらくわかりませんでした😢。

```m
|6 col 51 error| ngtsc:Error:-995002:Unrecognized block @.
|14 col 6 error| ngtsc:Error:-995002:Unexpected character "EOF" (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)
|14 col 6 error| ngtsc:Error:-995002:Invalid ICU message. Missing '}'.
|6 col 51 error| ngtsc:Error:-995002:Unclosed block ""
```

比較的何もない箇所に`@`を記述した場合は、次のようなエラーが出て対応方法がわかりやすいみたいです。

```m
|1 col 1 error| ngtsc:Error:-995002:Unrecognized block @.
|1 col 1 error| ngtsc:Error:-995002:Incomplete block "". If you meant to write the @ character, you should use the "&#64;" HTML entity instead.
```

## 新しい`@Input`デコレータのオプション
https://github.com/ver-1000000/angular-advent-calendar-2023/commit/8bc1dc9c538d00d7be6ba6f0cbf05d0c0f3b7b3d#diff-b67c0c3bfcf43ab3949e4026587893790ad17c15cdba9a9b66df7b1d7510bdb2R28-R29

地味に嬉しいInputのオプション機能、早速使ってみました❗
古いバージョンのAngularを使っていると「新しいInputなら……」と思うことが多々あるんですよね。

単純ですが非常に強力なAPIだと思います。

- [2023 年に入った Angular のさまざまなアップデート](https://kasaharu.hatenablog.com/entry/20231201/1701381600) | [kasaharu](https://kasaharu.hatenablog.com/about)

# 他の記事であまり言及されていない実装・小技
## SVG
今回、イラストみたいなUIを実装するにあたって、SVGを多用することにしました。
こういったサイトの構築だと、
SVGによる実装がDOMのセマンティクスを汚さない気がしててスキなんですよね……。
(「イラストの中のクリスマスツリーの星」を表すタグとかにdivを使いたくない)

ただし、SVGによる実装は基本的に座標の絶対指定になってしまうので、
そこは賢くやらないといけませんね。

現在はコードの中に多くのマジックナンバーがあるので、これを除去していくのは課題です。
Angular Pipeとか使うといいかもしれません

新しいAngularとの相性についてですが、使い心地は昔と変わらずというところでした。
Control Flowなどで問題が起きるかなと不安でしたが、
案外すんなりと進めることができました💮。

## GitHub Pagesへのホスティング
筆者は結構GitHub ActionsをつかってGitHub Pagesにホスティングしているのですが、
今回はここで結構詰まってしまいました……。

結論的には、`Settings > Actions > General > Workflow permissions`の権限が弱かったことが問題でした。

今までここを触ったことなかったんで、最近デフォルト設定が弱くなったんですかね。

ちなみに、あまり凝っていないのですがGitHub Pagesにデプロイするワークフローの変更はこんな感じです。

https://github.com/ver-1000000/angular-advent-calendar-2023/commit/c7eaf673eb299fcfc7c3b848c6e44d944daeba2a

:::note info
ポイント
- actions用のymlを書く
  - 今回はpeaceiris/actions-gh-pages@v3を利用
  - `publish_dir`にはSSGで生成されるビルド結果の場所を指定する
    - Angular CLIで普通に作成した場合`.dist/APP_ID/browser`のパスを指定してやるといい
- `build`コマンドで`--base-href`を設定してやる
  - ちゃんとやるならenvrionment.tsとかでいい感じにやるのかな？
  - ngSrcの絶対パスには気をつける
- `Settings > Pages > Build and deployment`の設定を適切に変更する
  - Source: `Deployment from a branch`
  - Branch: `gh-pages` `/ (root)`
:::

## Signal API
一部ですが、Signal APIによる実装を頑張ってみました……❗

案外、今回はSignal APIを主軸としたAdvent Calendarの記事はありませんでしたので、
@lacolacoさんの次の記事にある`Simple PDS`パターンを参考にして組んでみてます。

- [SignalベースのAngularコンポーネント設計パターン](https://zenn.dev/lacolaco/articles/angular-signals-component-design-patterns)

実際に実装してみると、なるほどしっくりくるカモ…… いい感じです🚥。
この辺はコメントも厚めに書いてるので、ぜひコードの方をご覧ください❗

https://github.com/ver-1000000/angular-advent-calendar-2023/blob/8deec46de2179e5d6a47744690f11e64fa699723/src/app/tree/tree.component.ts

https://github.com/ver-1000000/angular-advent-calendar-2023/blob/8deec46de2179e5d6a47744690f11e64fa699723/src/app/tree/tree-usecase.service.ts

# 時間がなくて実装が間に合わなかったけど盛り込みたかったやつ
## ルーティング(View Transitions API)
当初は記事の詳細表示をダイアログではなく、ルーティングを利用して表示しようとしていました。

この時の遷移にView Transitions APIを利用したかったのですが……。
UI的に遷移を踏まえたアイディアがパパっと出てこず、工数的に見送りました😢。

- [Angular 17 の View Transitions API 対応で遊んでみた](https://zenn.dev/rch850/articles/3c214564567e5b) | [rch850](https://zenn.dev/rch850)


## Deferrable views
クライアントでの動的な取得が行えれば`@defer`とかの出番がありそうなので使用するつもりだったのですが、
いざ実装してみると、本アプリでは基本的に通信にリアルタイム性が求められません。

SSGが勝手に通信をキャッシュするため遅延読み込みの必要がありませんでした😽。

## テスト/CI周り
`npm run test`は通るようにしていますが、カバレッジはほぼ0みたいなもんなので、もう少し何かしら書いてあげたい。
あと、とりあえずテストの自動化とprettier/eslintのチェックをCIで動かしたいですね。

## CSP_NONCEによるCSSインジェクション対策
- [Angular CSP_NONCEによるCSSインジェクション対策
](https://tech.quartetcom.co.jp/2023/12/11/angular-csp-nonce/) | [ringtail003](https://qiita.com/ringtail003)

ウオオこれ完全に知りませんでした……。
非常に簡単なので、基本的には意識して有効にするようにしたいですね。

# あとがき
Advent Calendarに登録してすぐに「SSGを利用したブツを作って、他の人の記事を全部読んで関連してそうなやつをリファレンスしていく」という指針をたてたのですが、
実装の着手を先送りにしまくった結果、24日当日に泣きながら全部実装することになりました……。


しかしながら……
逆に言えば1日でそれっぽいアプリがほぼフルスクラッチでできたのはなかなかの成果ではないでしょうか❓

しかも工数のうち半分以上はWebデザインとSVGの作成に費やしているわけですから、Angularの生産力は驚かされますね💪

キリの良いところまでは進めましたが、[時間がなくて実装が間に合わなかったけど盛り込みたかったやつ](https://qiita.com/drafts/36154cb31a3bdba1a51d/#%E6%99%82%E9%96%93%E3%81%8C%E3%81%AA%E3%81%8F%E3%81%A6%E5%AE%9F%E8%A3%85%E3%81%8C%E9%96%93%E3%81%AB%E5%90%88%E3%82%8F%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%91%E3%81%A9%E7%9B%9B%E3%82%8A%E8%BE%BC%E3%81%BF%E3%81%9F%E3%81%8B%E3%81%A3%E3%81%9F%E3%82%84%E3%81%A4)がたくさんあるように、まだ色々課題を残しています。
Issueなどは整備していませんが、どこかで改修していけたらいいなと思います。(PRも歓迎)

それではみなさま、𝓗𝓪𝓹𝓹𝔂 𝓜𝓮𝓻𝓻𝔂 𝓒𝓱𝓻𝓲𝓼𝓽𝓶𝓪𝓼...💫