らいふうっどの閑話休題

興味のあることをゆる~く書いていく

実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part4

実践 Angular: Standalone Components をざっくりキャッチアップしてみた Part4

lacolaco さん監修の実践 Angular: Standalone Components をざっくりキャッチアップしてみます。 zenn.dev

Who is lacolaco? / lacolaco さんて、どんな人?
  • Google Developers Expert for Angular
  • Angularコントリビューター
  • Angular日本ユーザー会代表
  • jsprimer.net 著者

Zenn プロフィールから引用

前回

lifewood.hatenablog.com

  • directive の作成

$ cd ~/stand-alone-components-sample/src/app
# component 同様に --standalone オプションを付けないとスタンドアロンコンポーネントでは使えない
$ npx ng g d h-tag-style --standalone 
CREATE src/app/h-tag-style.directive.spec.ts (238 bytes)
CREATE src/app/h-tag-style.directive.ts (167 bytes)
$ ../..
$ tree
.
├── LICENSE
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.config.ts
│   │   ├── app.routes.ts
│   │   ├── child
│   │   ├── child.routes.ts
│   │   ├── grand-child
│   │   ├── h-tag-style.directive.spec.ts # directive の spec ファイル
│   │   ├── h-tag-style.directive.ts # directive  ファイル
│   │   ├── list
│   │   ├── parent
│   │   └── parent.routes.ts
│   ├── assets
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tree.txt
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json


■ directive の実装

  • h-tag-style.directive.ts

import { Directive, HostBinding } from '@angular/core';

@Directive({
  selector: '[appHTagStyle]',
  standalone: true,
})
export class HTagStyleDirective {
  constructor() {}

  @HostBinding('style')
  style = {
    color: 'red',
  };
}

  • app.component.ts

import 'zone.js/dist/zone.js';
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { ListComponent } from './list/list.component';
+ // HTagStyleDirective を import する
+ import { HTagStyleDirective } from './h-tag-style.directive';

@Component({
  selector: 'app-root',
  standalone: true,
-  imports: [CommonModule, RouterOutlet, ListComponent],
+  // HTagStyleDirective を import する
+  imports: [CommonModule, RouterOutlet, HTagStyleDirective, ListComponent],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'stand alone components sample';
}

  • app.component.html

- <h1>{{ title }}</h1>
+ <!-- appHTagStyle を実装して、文字色を赤くする -->
+ <h1 appHTagStyle>{{ title }}</h1>

<router-outlet></router-outlet>


■ 実行結果イメージ

  • directive の作成

$ cd ~/stand-alone-components-sample/src/app
$ npx ng g d composition-ngif --standalone
CREATE src/app/composition-ngif.directive.spec.ts (261 bytes)
CREATE src/app/composition-ngif.directive.ts (179 bytes)
$ ../..
$ tree
.
├── LICENSE
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── app.component.css
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.config.ts
│   │   ├── app.routes.ts
│   │   ├── child
│   │   ├── child.routes.ts
│   │   ├── composition-ngif.directive.spec.ts # directive の spec ファイル
│   │   ├── composition-ngif.directive.ts # directive  ファイル
│   │   ├── grand-child
│   │   ├── h-tag-style.directive.spec.ts
│   │   ├── h-tag-style.directive.ts
│   │   ├── list
│   │   ├── parent
│   │   └── parent.routes.ts
│   ├── assets
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tree.txt
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json


■ directive の実装

  • composition-ngif.directive.ts

import { NgIf } from '@angular/common';
import {
  Directive,
  Input,
  OnChanges,
  SimpleChanges,
  inject,
} from '@angular/core';

@Directive({
  selector: '[appCompositionNgif]',
  standalone: true,
  // `NgIf` ディレクティブを合成する
  hostDirectives: [NgIf],
})
export class CompositionNgifDirective implements OnChanges {
  // 自身と同じ要素に付与された `NgIf` ディレクティブのインスタンスを取得する
  readonly #ngIf = inject(NgIf, { self: true });

  @Input('appCompositionNgif') isShowH1: boolean = true;

  ngOnChanges(changes: SimpleChanges): void {
    this.#ngIf.ngIf = this.isShowH1;
  }
}

  • app.component.ts

import 'zone.js/dist/zone.js';
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { ListComponent } from './list/list.component';
import { HTagStyleDirective } from './h-tag-style.directive';
import { CompositionNgifDirective } from './composition-ngif.directive';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    CommonModule,
    RouterOutlet,
    HTagStyleDirective,
+    CompositionNgifDirective,
    ListComponent,
  ],
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'stand alone components sample';

+  // 制御用フラグ
+  isShowH1: boolean = true;
}

  • app.component.html

- <h1 appHTagStyle>{{ title }}</h1>
+ <h1 *appCompositionNgif="isShowH1">{{ title }}</h1>

+<button (click)="isShowH1 = !isShowH1">
+  now header 1 is {{ isShowH1 ? "show" : "hide" }}
+ </button>
<router-outlet></router-outlet>


■ 実行結果イメージ

Show
Hide

まとめ
  • hostDirectives を上手に活用すると実装の簡素化が見込まそうです。
  • ユニットテスト のキャッチアップは次回に続きます。

関連記事
参考文献