Angularでモジュール(NgModule)を作成する

前回作成したコンポーネントがAppModuleに登録されているので、SharedModuleを作成してそちらに登録するように変更します。

これは後からモジュールを作成するパターンになりますが、一般的には先にモジュールを作成する方が多いため、今後使用するUserModuleも作成しておきます。

モジュールとは

Angularのモジュールは、アプリケーションの一部を構成し、関連するコンポーネント、ディレクティブ、パイプ、サービスなどをグループ化するコンテナです。モジュールはアプリケーションの機能を整理し、再利用しやすくする役割を果たします。

特に、Angularのモジュールは@NgModuleデコレータを使用して定義され、以下の主要なメタデータプロパティを持ちます:

  • declarations: モジュール内で使用されるコンポーネント、ディレクティブ、パイプを宣言します。
  • imports: 他のモジュールがエクスポートしたクラスを使用できるように、このモジュールが他のモジュールに依存していることを指定します。
  • exports: 他のモジュールで使用できるように、宣言されたクラスの一部をエクスポートします。
  • providers: アプリケーション全体で使用できるサービスのプロバイダーを登録します。
  • bootstrap: アプリケーションの起点となるルートコンポーネントを指定します。

モジュールを作成する

モジュールを作成するにはng generate moduleコマンドを使用します。

--dry-runオプション

モジュールを作成する前に--dry-runオプションについて確認しておきましょう。

--dry-runオプションはコマンドを実行せずに、コマンドで作成・更新される内容を確認できるオプションです。前回のようにコンポーネントを指定する際に、appから指定すればいいのかappの下から指定すればいいのか迷うことがあると思います。このような場合には、--dry-runオプションでどんなファイルが作成されてどんなファイルが更新されるかを確認するとよいでしょう。

$ ng generate module shared --dry-run
CREATE src/app/shared/shared.module.ts (192 bytes)

NOTE: The "--dry-run" option means no changes were made.

実際に実行してみると、作成されるモジュールのパスと名前が確認でき、「NOTE: The “–dry-run” option means no changes were made.」と変更がなかった旨のメッセージ表示されています。

SharedModuleはルーティングを必要としない共通コンポーネント・サービス等をまとめたモジュールですので、--routingオプションなしで実行します。

では、--dry-runなしで実行しましょう。

$ ng generate module shared
CREATE src/app/shared/shared.module.ts (192 bytes)

気づいたかもしれませんが、コンポーネントのときとは違ってモジュールクラスのファイルが作成された以外に更新されたファイルなどはありません。すなわち、作成したモジュールが自動的にどこかのモジュールに自動登録されることはありません。

まずはSharedModuleを修正します。まずは修正前の状態を確認しておきましょう。

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';



@NgModule({
  declarations: [],
  imports: [
    CommonModule
  ]
})
export class SharedModule { }

前回作成したTitleComponentを追加していきます。モジュールに属するコンポーネント、ディレクティブ、パイプはdeclarationsに記載します。加えて、他のモジュールに公開するためにexportsに記載します。

修正後のモジュールが以下のようになります。

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TitleComponent } from './components/title/title.component';

@NgModule({
  declarations: [TitleComponent],
  imports: [CommonModule],
  exports: [TitleComponent],
})
export class SharedModule {}

次にAppModuleを修正します。これまではTitleComponentapp.component.htmlで使用するためにAppModuleに登録していました。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { TitleComponent } from './shared/components/title/title.component';

@NgModule({
  declarations: [AppComponent, TitleComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

今後は、SharedModuleをインポートすることでTitleComponentを使用できるようにします。まずはdeclarationsからTitleComponentを削除し、その代わりにimportsTitleComponentが登録されているSharedModuleを追加します。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, SharedModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

実際に動作するか確認してみましょう。http://localhost:4200/にアクセスし、これまでどおり

のように表示されれば正しく修正できています。

UserModuleを作成する

次に今後使用するUserModuleを作成します。

モジュール名は単数形か複数形か

結論からいうと、チームによって異なります。これは個人的な見解となりますが、単数形と複数形で意味が異なる単語もあるため、単数形で書くことを基本にして複数形の意味を使いたい場合は複数形を使用する、というのがよいのではないかと考えています。

モジュールを作成する

ではUserModuleを作成します。今回はルーティングを伴うモジュールとするため、--routingオプションを使用します。また、今回は省略形であるng g mコマンドで実行します。--dry-runオプションをつけて確認しましょう。

$ ng g m user --routing --dry-run
CREATE src/app/user/user-routing.module.ts (247 bytes)
CREATE src/app/user/user.module.ts (272 bytes)

NOTE: The "--dry-run" option means no changes were made.

appディレクトリの下にuserディレクトリが作成され、user.module.tsが作成されます。加えて、ルーティングモジュールであるuser-routing.module.tsも作成されることが確認できます。

期待したとおりに作成されることを確認できたので、--dry-runオプションを外して実行します。

$ ng g m user --routing
CREATE src/app/user/user-routing.module.ts (247 bytes)
CREATE src/app/user/user.module.ts (272 bytes)

UserModuleは現時点ではコンポーネントなど何も属していないのでこのままにしておきます。とりあえず作成したコードだけ確認しておきます。

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { UserRoutingModule } from './user-routing.module';


@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    UserRoutingModule
  ]
})
export class UserModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }

これまでに作成したモジュールとは異なり、user-routing.module.tsにはroutesという定数があります。使い方については今後確認していきます。

変更をコミット&プッシュする

毎度のことですが、ここまでの変更をコミット&プッシュします。

$ git add . 
$ git commit -m 'create SharedModule and UserModule'
✔ Preparing lint-staged...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
[main ed5311a] create SharedModule and UserModule
 4 files changed, 33 insertions(+), 3 deletions(-)
 create mode 100644 src/app/shared/shared.module.ts
 create mode 100644 src/app/user/user-routing.module.ts
 create mode 100644 src/app/user/user.module.ts
$ git push
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 10 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (10/10), 1.12 KiB | 1.12 MiB/s, done.
Total 10 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), completed with 4 local objects.
To github.com:t0k0sh1/angular-tutorial.git
   90cd6c9..ed5311a  main -> main

まとめ

今回はSharedModuleを作成し、TitleComponentAppModuleからSharedModuleに移しました。今後は共通コンポーネントはSharedModuleに登録していくことにします。

また、今後に備えてルーティング設定をもったUserModuleも作成しました。こちらは今後使用していきます。

次回は画面にメッセージを表示するコンポーネントの作成を通じてサービスを使ったコンポーネント間のデータの受け渡しについて見ていきます。

AngularプロジェクトにTailwind CSSを導入する

今回はTailwind CSSを導入し、簡単なコンポーネントを作成します。

Tailwind CSSとは


Tailwind CSSは、高度にカスタマイズ可能なユーティリティファーストのCSSフレームワークです。ユーティリティファーストとは、小さい単位(ユーティリティ)のクラスを組み合わせてデザインを構築するアプローチのことで、そのための多くのプリセットクラスが提供されています。

特徴

  1. ユーティリティファースト: HTMLの中で直接、スタイリングができるため、CSSファイルを跨いで行ったり来たりする必要がありません。
  2. カスタマイズ可能: プロジェクトの特定のデザイン要件に合わせて、設定ファイルを使って色、サイズ、余白などをカスタマイズすることが可能です。
  3. レスポンシブデザイン: レスポンシブデザインのプレフィックスが提供されているため、さまざまなデバイスサイズでの表示を容易にコントロールできます。
  4. プラグインシステム: カスタムユーティリティやコンポーネントを簡単に追加するためのプラグインシステムも提供しています。

Angularでのサポート状況

Angular v11.2から公式にサポートされています。

Tailwind CSSを導入する

では、AngularプロジェクトにTailwind CSSを導入していきます。基本的には公式の手順に従って進めます。

$ npm install -D tailwindcss postcss autoprefixer

added 26 packages, changed 1 package, and audited 1161 packages in 3s

186 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
$ npx tailwindcss init

Created Tailwind CSS config file: tailwind.config.js

必要なパッケージをインスト−ルし、tailwind.config.jsが作成されます。

続いてtailwind.config.jsを以下のように変更します。

/** @type {import('tailwindcss').Config} */
module.exports = {
- content: [],
+ content: [
+   "./src/**/*.{html,ts}",
+ ],
  theme: {
    extend: {},
  },
  plugins: [],
}

修正後のtailwind.config.jsは以下のようになります。

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,ts}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

続いてsrc/styles.cssを以下のように編集します。

/* You can add global styles to this file, and also import other style files */
@tailwind base;
@tailwind components;
@tailwind utilities;

元々あるコメントは追加しても削除してもどちらでも構いません。

ここまででインストールは完了です。実際に使用可能か動作確認をしてみます。

まずは開発サーバーを起動します。

$ ng serve
✔ Browser application bundle generation complete.

Initial Chunk Files   | Names         |  Raw Size
vendor.js             | vendor        |   1.98 MB | 
polyfills.js          | polyfills     | 333.16 kB | 
styles.css, styles.js | styles        | 242.41 kB | 
main.js               | main          |  46.29 kB | 
runtime.js            | runtime       |   6.53 kB | 

                      | Initial Total |   2.59 MB

Build at: 2023-08-06T07:20:58.691Z - Hash: 34fc5b5c11ac8fa2 - Time: 4089ms

** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **


✔ Compiled successfully.

ng serveコマンドは開発サーバーを起動するコマンドです。デフォルトでは4200ポートを使用します。

動作確認をするために、src/app/app.component.htmlを以下のように書き換えます。

<h1 class="text-3xl font-bold underline">Hello world!</h1>

元々ある大量のコードはすべて削除しています。

変更保存をするとすぐに反映されます。では、http://localhost:4200/にアクセスし、以下のように表示されればTailwind CSSが有効になっています。

Tailwind CSSを便利にする拡張機能をインストールする

Tailwind CSSでのプログラミングを便利にする拡張機能をいくつかご紹介します。

Tailwind CSS IntelliSense

Tailwind CSSのオートコンプリート、リント、class属性で設定されるスタイルのプレビューなどの機能を提供する拡張機能です。

Tailwind Fold

Tailwind CSSを使用するとclass属性が長くなりがちです。これはコード全体の可読性低下にもつながるため、この拡張機能で普段は省略表示しておくとよいでしょう。

Tailwind Documentation

Tailwind CSSのドキュメントにすばやくアクセスするための拡張機能です。最近ではチートシートサイトがいくつもりますので、そちらの方が使いやすければこの拡張機能は特に使う必要はありません。

コンポーネントを作成する

動作確認は行っていますが、実際にコンポーネントを作成してみましょう。

ここで作成するコンポーネントは先ほど動作確認のために作成したタグをコンポーネント化してみましょう。

コンポーネントのひな形を自動生成する

ng generateコマンドを使ってコンポーネントのひな形を作成します。コンポーネントを作成する場合はcomponentを指定して、その後に生成するコンポーネントのパスを書きます。

$ ng generate component shared/components/title
CREATE src/app/shared/components/title/title.component.css (0 bytes)
CREATE src/app/shared/components/title/title.component.html (20 bytes)
CREATE src/app/shared/components/title/title.component.spec.ts (552 bytes)
CREATE src/app/shared/components/title/title.component.ts (198 bytes)
UPDATE src/app/app.module.ts (390 bytes)

コマンドは省略形もありますが、今回は省略せずに書きました。また、コンポーネントのパスにはsrc/app/を含まない点に注意してください。

コンポーネントを作成する

このコンポーネントは呼び出し元からテキスト(先ほど作成したタグのHello world!の部分)を受け取って表示するコンポーネントにします。

まずはtitle.component.tsから作成します。自動生成したコードは次のようになっています。

import { Component } from '@angular/core';

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.css']
})
export class TitleComponent {

}

詳しくは別途解説しますが、コンポーネントを使用する側から何か値を受け取るには@Inputディレクティブを使用します。

修正後のtitle.component.tsは以下のとおりです。

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-title',
  templateUrl: './title.component.html',
  styleUrls: ['./title.component.css'],
})
export class TitleComponent {
  @Input() title: string;

  constructor() {
    this.title = '';
  }
}

titleというメンバ変数に@Input()を付与し、呼び出し元から受け取れるようにしています。

次にtitle.component.htmlを作成します。

<h1 class="text-3xl font-bold underline">{{ title }}</h1>

先ほどTitleComponentに追加したtitleを表示しています。

では実際に使ってみましょう。app.component.htmlを以下のように修正します。

<app-title title="{{ title }}" />

app-titleコンポーネントのtitle属性にAppComponenttitleを設定します。

AppComponenttitleメンバに設定されている文言が表示されれば期待したとおりの動作になっています。

テストを行う

最後にテストを行いましょう。

今回作成したTestComponentとHTMLを変更したAppComponentの両方を修正します。

まずはTestComponentのテストです。

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { TitleComponent } from './title.component';

describe('TitleComponent', () => {
  let component: TitleComponent;
  let fixture: ComponentFixture<TitleComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [TitleComponent],
    });
    fixture = TestBed.createComponent(TitleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create the title component', () => {
    expect(component).toBeTruthy();
  });

  it('should have as title an empty string', () => {
    expect(component.title).toEqual('');
  });

  it('should display the title', () => {
    const expected = 'Test title';
    component.title = expected;
    fixture.detectChanges();

    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain(expected);
  });
});

次にAppComponentのテストです。

import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { TitleComponent } from './shared/components/title/title.component';

describe('AppComponent', () => {
  beforeEach(() =>
    TestBed.configureTestingModule({
      declarations: [AppComponent, TitleComponent],
    }),
  );

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'angular-tutorial'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app.title).toEqual('angular-tutorial');
  });

  it('should render title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('app-title h1')?.textContent).toContain('angular-tutorial');
  });
});

テストの実行はng testコマンドを使用します。

$ ng test
✔ Browser application bundle generation complete.
06 08 2023 20:16:25.133:WARN [karma]: No captured browser, open http://localhost:9876/
06 08 2023 20:16:25.140:INFO [karma-server]: Karma v6.4.2 server started at http://localhost:9876/
06 08 2023 20:16:25.140:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
06 08 2023 20:16:25.141:INFO [launcher]: Starting browser Chrome
06 08 2023 20:16:25.984:INFO [Chrome 115.0.0.0 (Mac OS 10.15.7)]: Connected on socket A-bmpclSUPRNDYqsAAAB with id 34000686
Chrome 115.0.0.0 (Mac OS 10.15.7): Executed 6 of 6 SUCCESS (0.035 secs / 0.024 secs)
TOTAL: 6 SUCCESS

テストではブラウザが開き結果が表示されます。

テストは起動しっぱなしにしておいて、コードを変更すればリロードされます。

コミットとプッシュを行う

ここまでの変更をコミット、プッシュします。

$ git add .
$ git commit -m 'add tailwind css'
✔ Preparing lint-staged...
✔ Running tasks for staged files...
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...
[main 90cd6c9] add tailwind css
 11 files changed, 428 insertions(+), 633 deletions(-)
 create mode 100644 src/app/shared/components/title/title.component.css
 create mode 100644 src/app/shared/components/title/title.component.html
 create mode 100644 src/app/shared/components/title/title.component.spec.ts
 create mode 100644 src/app/shared/components/title/title.component.ts
 create mode 100644 tailwind.config.js
$ git push
Enumerating objects: 26, done.
Counting objects: 100% (26/26), done.
Delta compression using up to 10 threads
Compressing objects: 100% (14/14), done.
Writing objects: 100% (17/17), 5.50 KiB | 1.10 MiB/s, done.
Total 17 (delta 6), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (6/6), completed with 6 local objects.
To github.com:t0k0sh1/angular-tutorial.git
   58ad1f8..90cd6c9  main -> main

まとめ

今回はTailwind CSSを導入し、コンポーネントを作成しました。

今回sharedにTitleComponentを作成したのですが、このコンポーネントはAppModuleに含まれています。AppModuleにすべてのコンポーネントを含めるのではなく、いくつかのモジュールに分けた方が管理しやすいです。

次回はSharedModuleを作成してTitleComponentを含めるように変更する手順を解説します。

AngularプロジェクトにPrettierとESLintを導入する

AngularプロジェクトではPrettierとESLintは最初から導入されてはいません。プロジェクト作成後にセットアップすることで利用可能となります。

実際のプロジェクトではPrettierとESLintは導入した方がよいので、実際にセットアップしていきます。

ESLintを導入する

Angular CLIにはng lintというコマンドが用意されています。プロジェクト作成直後はESLintが導入されていないため、lintの実行の代わりにELintのインストールを行うことができます。実際にやってみましょう。

$ ng lint
? Would you like to share pseudonymous usage data about this project with the Angular Team
at Google under Google's Privacy Policy at https://policies.google.com/privacy. For more
details and how to change this setting, see https://angular.io/analytics. No
Global setting: enabled
Local setting: disabled
Effective status: disabled
Cannot find "lint" target for the specified project.
You can add a package that implements these capabilities.

For example:
  ESLint: ng add @angular-eslint/schematics

Would you like to add ESLint now? Yes
ℹ Using package manager: npm
✔ Found compatible package version: @angular-eslint/schematics@16.1.0.
✔ Package information loaded.

The package @angular-eslint/schematics@16.1.0 will be installed and executed.
Would you like to proceed? Yes
✔ Packages successfully installed.
    
    All @angular-eslint dependencies have been successfully installed 🎉
    
    Please see https://github.com/angular-eslint/angular-eslint for how to add ESLint configuration to your project.
    
    We detected that you have a single project in your workspace and no existing linter wired up, so we are configuring ESLint for you automatically.
    
    Please see https://github.com/angular-eslint/angular-eslint for more information.
    
CREATE .eslintrc.json (991 bytes)
UPDATE package.json (1424 bytes)
UPDATE angular.json (3085 bytes)
✔ Packages installed successfully.

一番最初の質問はAngular CLIの使用状況データを提供するかどうかを聞いています。その後に@angular-eslint/schematicsのインストールを行うかの確認があります。基本的にはデフォルトのまま進めていただいて構いません。

インストールが完了すると、以下が行われます。

  • AngularプロジェクトでESLintを使用する基本的なパッケージがインストールされる
  • lintを実行するためのスクリプトがpackage.jsonに追加される
  • ESLintの設定ファイル(.eslintrc.json)が追加される
  • angular.jsonにESLintに関する設定が追加される

これでlintが使用可能になります。実際に使ってみましょう。

$ ng lint

Linting "angular-tutorial"...

All files pass linting.

ESLint拡張機能を導入する

Visual Studio Codeを使用している場合は、ESLint拡張機能を導入するとリアルタイムに違反をチェックできるようになりますので、導入をお勧めします。

拡張機能にはいくつか設定項目がありますが、特に設定変更しなくても動作します。

Prettierを導入する

次にPrettierを導入します。こちらはAngular CLIに専用のコマンドが用意されているわけではないため、インストールおよび設定作業は手動で行います。

Prettierをインストールしますが、先にインストールしたESLintと競合した設定が一部あるため、eslint-config-prettierを併せてインストールします。

$ npm install --save-dev prettier eslint-config-prettier

added 2 packages, and audited 1097 packages in 4s

164 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

これでインストール作業が完了しました。続いて設定作業を行います。

ESLintとPrettierを併用できるようにする設定を追加する

.eslintrc.jsonにPrettierを併用できるようにするための設定を追加します。

{
  "root": true,
  "ignorePatterns": ["projects/**/*"],
  "overrides": [
    {
      "files": ["*.ts"],
      "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
+       "prettier"
      ],
      "rules": {
        "@angular-eslint/directive-selector": [
          "error",
          {
            "type": "attribute",
            "prefix": "app",
            "style": "camelCase"
          }
        ],
        "@angular-eslint/component-selector": [
          "error",
          {
            "type": "element",
            "prefix": "app",
            "style": "kebab-case"
          }
        ]
      }
    },
    {
      "files": ["*.html"],
      "extends": [
        "plugin:@angular-eslint/template/recommended",
        "plugin:@angular-eslint/template/accessibility"
      ],
      "rules": {}
    }
  ]
}

"prettier"を追記します。前の行にカンマをつけるのを忘れないように気をつけてください。

.prettierrc.jsonを作成する

Prettierの設定ファイルである.prettier.jsonを作成します。

$ touch .prettierrc.json

ファイルを作成したら以下のように記述します。

{
  "printWidth": 120,
  "singleQuote": true
}

ここでは1行の文字数を120文字、文字列でシングルクォーテーションを使用するように設定しています。シングルクォーテーションを使用する設定はEditorConfigの設定ファイル.editorconfigに記載されている

[*.ts]
quote_type = single

と一致するように設定してください。ダブルクォーテーションを使用したい場合は、.prettierrc.jsonの設定を"singleQuote": falseにし、.editorconfigの設定をquote_type = doubleに変更します。ただし、Angularプロジェクトを作成したときに自動生成されているコードはシングルクォーテーションを使用しているため、特段理由がなければシングルクォーテーションを使用してください。

コマンドでフォーマットできるようにする

package.jsonを編集してコマンドでフォーマットできるようにしましょう。

{
  "name": "angular-tutorial",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint",
+   "format": "prettier \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\" --write"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.1.0",
    "@angular/common": "^16.1.0",
    "@angular/compiler": "^16.1.0",
    "@angular/core": "^16.1.0",
    "@angular/forms": "^16.1.0",
    "@angular/platform-browser": "^16.1.0",
    "@angular/platform-browser-dynamic": "^16.1.0",
    "@angular/router": "^16.1.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.1.6",
    "@angular-eslint/builder": "16.1.0",
    "@angular-eslint/eslint-plugin": "16.1.0",
    "@angular-eslint/eslint-plugin-template": "16.1.0",
    "@angular-eslint/schematics": "16.1.0",
    "@angular-eslint/template-parser": "16.1.0",
    "@angular/cli": "~16.1.6",
    "@angular/compiler-cli": "^16.1.0",
    "@types/jasmine": "~4.3.0",
    "@typescript-eslint/eslint-plugin": "5.62.0",
    "@typescript-eslint/parser": "5.62.0",
    "eslint": "^8.44.0",
    "eslint-config-prettier": "^8.9.0",
    "jasmine-core": "~4.6.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "prettier": "^3.0.0",
    "typescript": "~5.1.3"
  }
}

コマンド名は何でも構いませんが、ここではnpm run formatで実行できるようにしています。

これから動作確認のためにコマンドを実行しますが、実行するといくつかのファイルが変更されます。変更したくない場合は記事を読むだけにしてください。

$ npm run format

> angular-tutorial@0.0.0 format
> prettier "src/**/*.{js,jsx,ts,tsx,html,css,scss}" --write

src/app/app.component.css 9ms
src/app/app.component.html 84ms
src/app/app.component.spec.ts 72ms
src/app/app.component.ts 3ms
src/app/app.module.ts 3ms
src/index.html 1ms
src/main.ts 2ms
src/styles.css 1ms

実行するといくつかのファイルについて変更されたことが表示されます。

Prettier – Code formatter拡張機能を導入する

Visual Studio Codeを使用している場合はPrettier – Code formatter拡張機能を導入することで、コードフォーマットを自動化することができます。

こちらは拡張機能導入後にいくつかの設定を行う必要があります。主な設定項目は以下の3つです。

  • Format On Paste
  • Format On Save
  • Default Formatter

他からコピーしたコードをペーストしたときにフォーマットする場合はFormat On Pasteの設定にチェックを入れてください。これは好みかもしれませんが、ペースト後に少しいじってから保存することがほとんどなので私はチェックを入れていません。

次に保存時にフォーマットを行うようにすることでコードフォーマットを自動化できます。基本的にはチェックを入れていただくとよいのですが、自動フォーマットを有効化していると操作性が気になる場合があるかもしれません。実際に試していただいて好みに合うように調整してください。

デフォルトのフォーマッタをPrettierに変更します。これによりPrettierが有効化します。一つだけ注意点があり、PrettierはPythonには対応していません。そのため、Pythonでの開発もVisual Studio Codeで行う場合は、デフォルトのフォーマッタをPrettierに変更した上で、settings.jsonに以下の記述を追加してください。

  "[python]": {
    "editor.defaultFormatter": null,
  },

これにより.pyファイルではPrettierをフォーマッタとして使用しなくなります。少し脱線しますが、blackをフォーマッタとして使用している場合は、

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "python.formatting.provider": "black",
  "[python]": {
    "editor.defaultFormatter": null,
  },
}

のような記載になります。

変更をコミットする

前回からずっとコミットしていなかったので、ここまでの変更をコミットしておきます。

$ git add .
$ git commit -m 'install eslint and prettier'
[main f20d6df] install eslint and prettier
 11 files changed, 3137 insertions(+), 327 deletions(-)
 create mode 100644 .eslintrc.json
 create mode 100644 .prettierrc.json

本記事で使用しているプロジェクトは以下にありますので、必要に応じて参考にしてみてください。

まとめ

ちょっと長くなったので今回はここまでにします。次回はHuskyを使ってpre-commitフックでESLintとPrettierを自動実行するようにしたいと思います。

ただし、若干のトレードオフがあるため、その点についても解説します。

Angular向けのVSCode拡張機能をインストールする

Visual Studio CodeでAngularプロジェクトの開発をするために必要な拡張機能をインストールしていきます。

ここで紹介する拡張機能は以下の3つです。

  • Angular Language Service
  • Angular Snippets (Version 16)
  • EditorConfig for VS Code

ここに掲載している拡張機能は必須といえるものを選んでいます。すべての拡張機能がAngularプロジェクトの作成直後から使用可能です。

では、一つずつ見ていきましょう。

Angular Language Service

Angularチーム公式の拡張機能で、テンプレートファイル内でのIntelliSense機能や定義へのジャンプなど、様々な便利な機能を提供しています。

Angularアプリケーションの開発を行う際は必ず入れておきましょう。

Angular Snippets (Version 16)

いくつかのバージョン対応版が見つかると思いますが、今回使用するバージョンは16.xですので、対応するバージョンの拡張機能を使用しています。

強力な拡張機能であることは間違いありませんが、人によっては使用する/しないがはっきり分かれると思います。最近ではGitHub CopilotなどのAIを活用したコード生成も手段としてあるので、必要性を感じない場合はインストールしなくても問題ありません。

EditorConfig for VS Code

Angularプロジェクトを作成すると、.editorconfigファイルが作成されていますので、これを有効化させるために導入します。

実務のことを考えるとPrettierやESLintを導入する必要がありますが、プロジェクト作成直後では使える状態になっていないため、今回の拡張機能から除外しました。

EditorConfigを使うべきか否か

Prettierを導入する場合、EditorConfigは必要かどうか、という議論があるようです。機能的には重複している部分があるため、EditorConfigを使わずにPrettierのみを使用する、という選択肢もアリという主張はそれなりに筋が通っています。

ただ、PrettierとEditorConfigは共存可能で、VSCode以外のIDEを自由に使用するという選択肢がある場合、多くのIDEでサポートされているEditorConfigの設定ファイルを残しておくことで、Prettierの設定が行われていない場合やGitのpre-commit時にしかPrettierが動作しない状況などにおいても必要最低限のフォーマットが行われるというメリットがあります。

そのため、本記事では必要な拡張機能にEditorConfig for VS Codeを挙げています。

まとめ

今回はAngularプロジェクトの開発に最低限必要なVisual Studio Codeの拡張機能3つをインストールしていきました。

実用面を考えると、PrettierとESLintの導入は必要ですので、次回はPrettierとESLintの導入とHuskyによるpre-commitフックの設定を行っていきましょう。

Angular CLIのインストールとプロジェクトの作成

AngularはGoogleによって開発され、維持されているオープンソースのJavaScriptフレームワークであり、主にSingle Page Application(SPA)の開発に使用されます。AngularはTypeScriptで書かれており、高度なツールやエディタのサポートを享受できます。

Angularの特徴

以下がAngularの主な特徴です。

  1. コンポーネントベースのアーキテクチャ: Angularはコンポーネントベースのアーキテクチャを採用しており、アプリケーションは再利用可能なコンポーネントに分割されます。これにより、コードの管理が容易になり、再利用性と可読性が向上します。
  2. 依存性注入: Angularは依存性注入パターンを採用しており、依存オブジェクトをコンポーネントに提供します。これにより、コードの再利用性とテストのしやすさが向上します。
  3. データバインディング: Angularは双方向データバインディングをサポートしており、モデルとビュー間の同期を自動的に保つことができます。
  4. ルーティング: Angularのルーティング機能により、アプリケーション内でのページ間の移動が容易になります。
  5. Angular CLI: Angular Command Line Interface(CLI)を使用すると、プロジェクトの作成、開発、テスト、ビルドが容易になります。
  6. テスト容易性: Angularは単体テストからエンドツーエンド(E2E)テストまで、テストを容易にするツールとライブラリを提供します。

他の主要なJavaScriptフレームワーク、特にReactやVue.jsと比較した場合、Angularはフルスタックなフレームワークで、多くの機能を提供します。一方で、ReactやVue.jsはライブラリとしてスタートし、他のライブラリやツールと組み合わせて使用することでフレームワークのような機能を達成します。そのため、ReactやVue.jsはより柔軟性がありますが、セットアップや学習曲線が少し複雑になる可能性があります。

また、AngularはTypeScriptを使用しますが、ReactやVue.jsはJavaScriptを使用します(ただし、TypeScriptのサポートもあります)。TypeScriptは静的型付けを提供し、大規模なプロジェクトでは開発効率やコードの品質を向上させますが、一方で学習コストがかかることも事実です。

Angularは日本国内においてはReactやVue.jsと比べるとメジャーではありませんが、まったく使われていないというわけではありません。主に企業向けシステムを中心に利用されています。

Angular CLIのセットアップとプロジェクトの作成

Angularのプロジェクト作成およびコードのひな形の生成にはAngular CLIを使用します。

本記事では、執筆時点で最新版の16.1.6を使用します。基本的にAngularで使用するNode.jsは同時期の安定版をサポートしているため、ここではNode.jsは18.17.0、npmは9.8.1を使用します。また、全体を通してパッケージマネージャーはnpmを使用します。

$ node -v
v18.17.0
$ npm -v
9.8.1

以下のコマンドを入力してAngular CLIをインストールします。

$ npm install -g @angular/cli

added 240 packages in 13s

36 packages are looking for funding
  run `npm fund` for details

Angular CLIのインストール成功すると、ngコマンドが使用可能になります。versionサブコマンドでインストールしたAngular CLIのバージョンを確認してみましょう。

$ ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 16.1.6
Node: 18.17.0
Package Manager: npm 9.8.1
OS: darwin arm64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1601.6 (cli-only)
@angular-devkit/core         16.1.6 (cli-only)
@angular-devkit/schematics   16.1.6 (cli-only)
@schematics/angular          16.1.6 (cli-only)

Angular CLI、Node.js、パッケージマネージャー(npm)およびインストールしたパッケージのバージョンを確認することができます。

開発者全員で同じバージョンのAngular CLIを使用する場合は、latestの部分を指定のバージョンに変更します。

$ npm install -g @angular/cli@16.1.6

added 240 packages in 15s

36 packages are looking for funding
  run `npm fund` for details

システム開発においてはバージョンを明示的に指定するこちらの方法でインストールしてください。

Angularプロジェクトの作成

Angular CLIのインストールが完了したら、Angularプロジェクトを作成します。

プロジェクト名は任意の名前で構いませんが、本記事ではangular-tutorialというプロジェクト名にします。

Angular CLIでAngularプロジェクトを作成するには、newサブコマンドを使用します。

$ ng new angular-tutorial

使用するAngular CLIのバージョンによって変わる場合がありますが、コマンド実行後にいくつか質問が表示されます。ここではすべてデフォルト値のまま進めることにします。

$ ng new angular-tutorial
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
CREATE angular-tutorial/README.md (1069 bytes)
CREATE angular-tutorial/.editorconfig (274 bytes)
CREATE angular-tutorial/.gitignore (548 bytes)
CREATE angular-tutorial/angular.json (2750 bytes)
CREATE angular-tutorial/package.json (1047 bytes)
CREATE angular-tutorial/tsconfig.json (901 bytes)
CREATE angular-tutorial/tsconfig.app.json (263 bytes)
CREATE angular-tutorial/tsconfig.spec.json (273 bytes)
CREATE angular-tutorial/.vscode/extensions.json (130 bytes)
CREATE angular-tutorial/.vscode/launch.json (470 bytes)
CREATE angular-tutorial/.vscode/tasks.json (938 bytes)
CREATE angular-tutorial/src/main.ts (214 bytes)
CREATE angular-tutorial/src/favicon.ico (948 bytes)
CREATE angular-tutorial/src/index.html (301 bytes)
CREATE angular-tutorial/src/styles.css (80 bytes)
CREATE angular-tutorial/src/app/app.module.ts (314 bytes)
CREATE angular-tutorial/src/app/app.component.css (0 bytes)
CREATE angular-tutorial/src/app/app.component.html (23083 bytes)
CREATE angular-tutorial/src/app/app.component.spec.ts (922 bytes)
CREATE angular-tutorial/src/app/app.component.ts (220 bytes)
CREATE angular-tutorial/src/assets/.gitkeep (0 bytes)
✔ Packages installed successfully.
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: 	git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: 	git branch -m <name>
    Successfully initialized git.

質問は2つありますので、1つずつ見ていきましょう。

Would you like to add Angular routing?

ルーティングモジュールを追加するかについて訪ねられています。ルーティングモジュールを使用しないことはほとんどないので、yを回答しても構いませんが、後で追加するのでここではデフォルト値のままにしておきます。

Which stylesheet format would you like to use?

スタイルシートのフォーマットを選択することができます。デフォルトはCSSですが、SCSSやSASS、LESSなどを選択できます。UIをどのように開発するか決まっていればそれに合わせて選択していただくのがよいと思います。本記事執筆時点ではいくつかのCSSフレームワークを試そうと思っていますので、デフォルトのCSSのままにしておきます。

hint: Using ‘master’ as the name for the initial branch. This default branch name

プロジェクトの作成が成功したあと、いくつかhint:で始まるメッセージが表示されています。

私の環境では最初に作成されるGitのブランチがmasterになっているため、このメッセージが表示されています。変更しなくても動作には支障はありませんが、不適切なブランチ名であるというのが業界における共通認識になっていると思いますので、GitHubの方針に合わせてmainブランチに変更しておきましょう。

また、git config --global init.defaultBranch <name>でデフォルトのブランチ名を変更しておくことも可能です。毎回変更するのが面倒な場合はこちらでデフォルトブランチを変更してください。ただし、次回以降の作成で有効になる点にはご注意ください。(すでに作成済みのGitリポジトリは変更されません)

これ以降ではVisual Studio Codeを使って開発を進めていきますので、作成したプロジェクトをcodeコマンドで表示し、ターミナルを開きます。

$ code angular-tutorial

Visual Studio CodeでAngularプロジェクトを開くと、拡張機能をインストールを勧めるメッセージが表示されますが、あとでインストールするので、ここでは閉じておいてください。

$ git branch
* master

ブランチを確認すると、masterブランチになっています。これをgit branch -mコマンドでmainに変更します。

$ git branch -m main
$ git branch
* main

ブランチ名がmainに変わったことを確認できました。

まとめ

Angular CLIのインストールとAngularプロジェクトの作成までを行いました。

次回はVisual Studio CodeでのAngularの開発を円滑に進めるための拡張機能をインストールしていきます。

Nx-WorkspaceでAngular+NestJSのプロジェクトを構築する

Nx Workspaceを使ってAngular+NestJSのプロジェクトを作成します。

Nx Workspaceとは?

Nx Workspaceとは、Angular CLIを使用して開発する際に、複数のアプリケーションやライブラリを一元管理するツールです。Nx WorkspaceはAngular CLIだけでなく、ReactやNestJSなども管理することができます。

今回はフロントエンドとしてAngularを使用し、バックエンドとしてNestJSを使用してみます。

Angular CLIをインストールする

まずはAngular CLIをインストールします。

Node.jsは執筆時点のLTS版を使用しています。

$ node -v
v18.14.0
$ npm -v
9.4.2

通常の手順どおりにAngular CLIをインストールします。すでにインストール済みの場合はこの手順はスキップしてください。

$ npm install -g @angular/cli
$ ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 15.1.5
Node: 18.14.0
Package Manager: npm 9.4.2
OS: darwin arm64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1501.5 (cli-only)
@angular-devkit/core         15.1.5 (cli-only)
@angular-devkit/schematics   15.1.5 (cli-only)
@schematics/angular          15.1.5 (cli-only)

次に@nwrl/schematicsをインストールします。@nwrl/schematicsは新しいアプリケーションやライブラリを生成するためのツールです。

$ npm install -g @nrwl/schematics

警告がかなり表示されるかもしれませんが、気にせずインストールしてください。

プロジェクトを作成する

create-nx-workspaceを使ってプロジェクトを作成します。後からアプリケーションを追加するので、--preset=emptyを指定して空のプロジェクトを作成します。

執筆時点では1回だけ質問されますが、ここではNoで回答します。

$ npx create-nx-workspace@latest demo-app --preset=empty
$ cd demo-app

以降の操作は作成したプロジェクト内で行います。

フロントエンドの作成

Angularをインストールし、フロントエンドアプリケーションを作成します。

まずは、@nrwl/angularパッケージをインストールします。

$ npm install -D @nrwl/angular

次にAngularのアプリケーションを作成します。nx generate @nrwl/angular:appコマンドを使うことでAngularのアプリケーションを作成できます。

$ npx nx generate @nrwl/angular:app frontend --routing

インストールが完了したら、サーバーを起動して動作確認してみましょう。

$ npx nx serve frontend

Angularの標準的な画面とは異なりますが、http://localhost:4200/にアクセスし、下記のような画面が表示されればサーバーが問題なく起動しています。

バックエンドの作成

次にバックエンドを作成します。基本的な流れは先ほどと同じです。

まずは@nrwl/nestパッケージをインストールします。

$ npm install -D @nrwl/nest

nx generate @nrwl/nest:appコマンドでバックエンドアプリケーションを作成します。

$ npx nx generate @nrwl/nest:app backend --frontendProject frontend

>  NX  Generating @nrwl/nest:application

UPDATE package.json
CREATE apps/backend/src/app/.gitkeep
CREATE apps/backend/src/assets/.gitkeep
CREATE apps/backend/src/main.ts
CREATE apps/backend/tsconfig.app.json
CREATE apps/backend/tsconfig.json
CREATE apps/backend/webpack.config.js
CREATE apps/backend/project.json
CREATE apps/backend/.eslintrc.json
CREATE apps/backend/jest.config.ts
CREATE apps/backend/tsconfig.spec.json
CREATE apps/backend-e2e/project.json
CREATE apps/backend-e2e/jest.config.ts
CREATE apps/backend-e2e/src/backend/backend.spec.ts
CREATE apps/backend-e2e/src/support/global-setup.ts
CREATE apps/backend-e2e/src/support/global-teardown.ts
CREATE apps/backend-e2e/src/support/test-setup.ts
CREATE apps/backend-e2e/tsconfig.json
CREATE apps/backend-e2e/tsconfig.spec.json
CREATE apps/backend-e2e/.eslintrc.json
CREATE apps/frontend/proxy.conf.json
UPDATE apps/frontend/project.json
CREATE apps/backend/src/app/app.controller.spec.ts
CREATE apps/backend/src/app/app.controller.ts
CREATE apps/backend/src/app/app.module.ts
CREATE apps/backend/src/app/app.service.spec.ts
CREATE apps/backend/src/app/app.service.ts

added 27 packages, changed 1 package, and audited 1462 packages in 13s

180 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

こちらもサーバーを起動し、動作確認をしてみます。

$ npx nx serve backend

http://localhost:3333/apiにアクセスし、以下のようにメッセージが返ってくれば正常にサーバーが起動しています。

グローバルインストールしたAngular CLIをアップデートする

グローバルインストールしたAngular CLIをアップデートする手順について解説します。

注意事項

本手順はグローバルインストールされたAngular CLIのバージョンアップする手順です。
プロジェクトのAngularをバージョンアップする手順ではないのでご注意ください。

アップデート前の状態

アップデート前の状態を確認しておきます。ng versionでインストールされているAngular CLIのバージョンを確認することができます。

$ ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 13.3.1
Node: 16.14.2
Package Manager: npm 8.7.0
OS: darwin arm64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1303.1 (cli-only)
@angular-devkit/core         13.3.1 (cli-only)
@angular-devkit/schematics   13.3.1 (cli-only)
@schematics/angular          13.3.1 (cli-only)

本手順では、13.3.1がアップデート前の状態になります。

Angular CLIをアップデートする

Angular CLIを13.3.1から13.3.4にアップデートしましょう。

まずは、現在インストールしているAngular CLIをアンインストールします。

$ npm uninstall -g @angular/cli

removed 196 packages, and audited 1 package in 567ms

found 0 vulnerabilities

次にAngular CLIを再度インストールします。ここでは最新版をインストールしていますが、特定のバージョンにアップデートしたい場合は@angular/cli@x.x.xのようにバージョンを指定してください。

$ npm install -g @angular/cli

added 196 packages, and audited 197 packages in 10s

24 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

インストール後に再度バージョンを確認します。

$ ng version

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 13.3.4
Node: 16.14.2
Package Manager: npm 8.8.0
OS: darwin arm64

Angular:
...

Package                      Version
------------------------------------------------------
@angular-devkit/architect    0.1303.4 (cli-only)
@angular-devkit/core         13.3.4 (cli-only)
@angular-devkit/schematics   13.3.4 (cli-only)
@schematics/angular          13.3.4 (cli-only)

Angular CLIのバージョンが13.3.4にアップデートされたことを確認できました。

この手順はバージョンアップにもバージョンダウンにも使用できます。

Angularでキャッシュ対策を行う(–output-hashing=all)

Angularでキャッシュが使用されてアプリケーションが更新されないことがある問題を解消する方法について解説します。

この方法はプログラムを変更することなく実施できますが、必ずしも安全であるとは限りません。
実際にこの方法を試して、うまくいかない(キャッシュ対策ができない、というのではなく動作しない)ことがありました。このことから、アプリケーションの使用しているAngularのバージョンやアプリケーションの特性等の要因によって副作用が発生する場合があります。

必ずテスト環境で十分確認してから本番環境へ適用してください

ビルドされたファイルにハッシュを付与する

ng buildコマンドのオプションに--output-hashing=allを設定します。

$ ng build --configuration production --output-hashing=all

もし、ビルドオプションを変更できない場合は、angular.jsonoutputHashingオプションを設定します。

   "configurations": {
     "production": {
       ・・・
       "outputHashing": "all"
       ・・・
     },
   },
モバイルバージョンを終了