お茶漬けびより

学んだことを整理する場所です。主に、C++, Unreal Engine 4 (UE4) を扱っていました。最近は、仕事方面で使っている言語やツールを紹介したいと思います。たまに趣味や雑記も。

Angular のコンポーネントのテスト

f:id:pickles-ochazuke:20190812094957j:plain

マジカルミライ2019を見てきました。初めてのライブでもあったので新鮮な体験でした。

f:id:pickles-ochazuke:20190812094934j:plain

前回と被ってそうですが、Angular のコンポーネントのテストの入門的な話です。

コンポーネントのテスト

Angular のテストは以下のコマンドで行います

ng test

ng new コマンドでプロジェクトを作成すると app ディレクトリ の直下に app.component.html, app.component.spec.ts, app.component.ts などが作成されます。 今回、テストを行うために必要なファイルは先に挙げた3つなので、これだけを編集していきます。

自分の環境では、HTML ファイル(app.component.html)を pug ファイル(app.component.pug)にしていますが、今回は特に難しいことはしていないので、知らなくても問題ないと思っています(一応、 HTML に書き換えた内容も載せますが、表示の確認をしていないので間違いがあるかもしれません)

今回テストする HTML(app.component.pug) の内容は以下になります。

span#title {{title}}

span タグに title という ID がついており、コンポーネントtitle という変数をバインドしています。 HTML だと以下のようになります。

<span id="title"> {{title}} </span>

コンポーネント側(app.component.ts)は以下

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.pug',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'angular-try';
}

これをテストしていきます。

テストをするには、テストコードが必要ですが、Angular では、テストコードはコンポーネントを作ったときに一緒に作成されます(app.component.spec.ts)。 最初は以下のようになっています(流し見する程度でいいです)。

このテストを実行するといくつか失敗します。コンポーネント側と View 側を変更しているためです。

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

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

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

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

  it('should render title in a h1 tag', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-try!');
  });
});

Angular のテストは、 Jasminekarma で動いています。

コード内の describeitbeforeEach は、本題ではないので、ここでは詳しく説明しません(説明するほど知識がありません……)。

一応簡単にいうと、 describe 内にある it 関数が 1 単位のテストで、 beforeEachit が呼ばれる前に行う初期化処理です(自動で呼ばれます)。

TestBed

beforeEach では、以下のような処理が呼ばれています。

TestBed.configureTestingModule({
  declarations: [
    AppComponent
  ],
}).compileComponents();

TestBed は、 @NgModuleエミュレータです。そのコンポーネントを作成するために必要なモジュールやプロパティを用意して、 configureTestingModule で読み込ませてからコンポーネントを作る必要があります。 compileComponents コンパイルをする処理ですが、CLI で実行している場合、コンパイルはしてくれているので通常必要ありません。

今回は特別な機能を使っていないのでデフォルトのままでいいです。

TestBed は Angular のテストの中でも最も重要な機能です。ここでは、全部紹介しきれませんが、一度調べてみることをオススメします。

AppComponent の作成

次は、テストを一つずつ見ていきます。 以下は、 AppComponent が正しく作成できているか確認しているテストです。

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

TestBed.createComponent(AppComponent) では、 beforeEach 内で設定された TestBedAppComponentインスタンスを作成しています。作成したコンポーネントインスタンスをそのまま返すわけではなく、 ComponentFixture という型のインスタンスを返します。コンポーネントには、この fixture を介して操作を行います。

デフォルトでは、 fixture.debugElement.componentInstanceコンポーネントにアクセスしていますが、 fixture からコンポーネントインスタンスが取得できるため、 debugElement はなくても問題ありません。

AppComponent のテスト

2 つ目のテストを見てみると、コンポーネントにアクセスしている様子がわかります。

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

appコンポーネントインスタンスです。コンポーネントが持つ変数が期待通りの値になっているか確認しています。

View 側のテスト

3 つ目のテストでは、コンポーネントと紐付いている HTML 要素のテストをしています。また、このテストは失敗していると思います。

it('should render title in a h1 tag', () => {
  const fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  const compiled = fixture.debugElement.nativeElement;
  expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-try!');
});

View 側をテストしたい場合は、querySelector を使ってエレメントを取り出し、期待の結果になっているか確認ができます。

実際の Angular では、変更を検知して結果を View に反映してくれますが、テストの場合は手動で変更を伝える必要があります。それが fixture.detectChanges() です。

これを実行しない場合、バインドされている部分が変化せずテストが失敗します。

テストを通す

では、最後のテストが失敗していると思いますので、このテストを通していきます。

まず、テストを確認します。

it('should render title in a h1 tag', () => {
  const fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  const compiled = fixture.debugElement.nativeElement;
  expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-try!');
});

コンポーネントを作成するところは、別のテストで問題なく通っているので、最後のテストしている部分を見ます。

現在の HTML は以下のようになっています。

span#title {{title}}

h1 のタグは存在せず、 span タグになっています。また、表示する文字列は、title 変数の内容だけなので、 angular-try になるはずです。テストを以下のように書き換えます。

expect(compiled.querySelector('span').textContent).toContain('angular-try');

これはテストが通るはずです。最後に、テストの名称を変更します。変更したテストは以下のようになります。

it('should render title in a span tag', () => {
  const fixture = TestBed.createComponent(AppComponent);
  fixture.detectChanges();
  const compiled = fixture.debugElement.nativeElement;
  expect(compiled.querySelector('span').textContent).toContain('angular-try');
});

まとめ

以上、コンポーネントのテストを簡単に説明しました。Angular では、コンポーネントのテストを行うときは、以下のようになると思います。

  1. テスト対象のコンポーネントに必要なデータ(メタデータ)を TestBed に設定する
  2. 設定した TestBedコンポーネントを作成し、fixture を取得する。
  3. fixture を介して、コンポーネント、 View にアクセスし、期待する結果になっているか確認する。

上記を行うために主に必要な機能は以下です。

  • TestBed.configureTestingModule(): TestBed を設定する
  • TestBed.createComponent(AppComponent): 指定されたコンポーネントクラスのインスタンスを作成する
  • ComponentFixture\.detectChanges(): バインドしている部分を View 側に反映させる
  • fixture.debugElement.nativeElement: View の要素にアクセスし、期待する値になっているか確認する

今回 HTML 側を変更するとテスト側も変更する必要が出ました。これでは、気軽に HTML 側を変更するのが難しくなってしまいます。これを解決するには、PageObject というパターンを使うことである程度解決することが出来ます。

次は、今回のテストをもう少し変更に強いテストに変えていきます。

Angular でテストを書く(サービス編)

Mac に慣れません。身体が Windows でできているので Mac を受け付けないのかもしれません。 最近、五等分の花嫁にハマっておりスマホの壁紙やキーボードの見た目を五等分の花嫁仕様にして楽しんでいます。Kindle で買った後に物理的に欲しくなったので全巻買いに行ったら8巻がありませんでした(そのとき9巻まだ出てなかった)。

f:id:pickles-ochazuke:20190421223158j:plain

全員可愛くて好きですがあえて一番を上げるなら三玖です。そろそろ本題に入ります(9巻の表紙ヤバいですね!)。

はじめ

Angular は標準でテストを行う機能があるので、簡単に実行することができます。

テストは、 ng test で行うことができます。

プロジェクトの作成

ng new angular-test で作成しました。

router はなし、CSSは標準のCSSを選択しました。今回はどっちも関係ないのでたぶんどっちでもいいです。

ng test でテストが可能なことを確認します。

とりあえず、放り投げた値を計算して画面に表示するようにします。

ng serve --open

html を以下のように変更します。

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>
<div>
  5 + 4 = 9
</div>

これから、このコンポーネントで足し算をする画面を作成して、そのテストを書くことを目指します。

テストの作成(コンポーネント

実は初めからテストコードが用意されています。 app.component.spec.ts がそれです。テストファイルは、.spec.ts という拡張子(?)です。

中身を見ると以下のようなコードが書かれています。

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

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

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

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

  it('should render title in a h1 tag', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to angular-test!');
  });
});

初めて目にすると「う゛っ……」ってなるかもしれません。自分はなりました。

今回の話では見る必要はないので、読み飛ばしても大丈夫ですが、気になる人は、以下を一読すると参考になるかも。

少し目を慣らすために、まずは大きく分類していきます。 自分は、このテストコードは、大きな項目とその中にある小さな項目の集まりだと思っています。 大きな項目は、describe で始まる関数で、小さな項目は、it で始まる関数です。 どちらも一つ目の引数に文字列が渡され、二つ目の引数に無名関数が渡されています。

文字列は、テストを実行した時に、実行者がなんのテストを行って、どのテストが失敗したか、成功したのかを簡単に判別するための文字です。要は何をテストしているのかを説明するような文章を書くといいでしょう。当然日本語が書けるし、書くべきです。周りの母国語が英語なら英語にするべきだろうけど。

describe は小さなテストを集めた親みたいなもので、テスト自体はその中にある it がテスト内容になります。 it の中では、大抵以下の順番でテストを書きます。

  1. テストの前準備
  2. テスト対象の処理を実行
  3. 処理結果が期待する結果になっているか判定

といった感じです。 ここで、テストを書いていると各 it で共通の初期化処理があることに気づいたりします。そういった共通処理は、beforeEach に書きます。beforeEach は、it を実行する時に最初に勝手に呼ばれます。

期待値の比較は、いろいろメソッドが存在しますが、基本は、expect(比較対象).比較方法(期待値) のように書きます。 上のコードを見れば何となくわかるかもしれないですが。

初めから用意されているテストコードは、Angular 固有のコンポーネントクラス(呼び方あってる?)に対してテストを行うテストになっています。実は、コンポーネントのテストは面倒くさいし難しいです(設計に問題があったのだろうか……)。まずはサービスで慣れていくのがいいと思います。

サービスとそのテストの作成

サービスでテストコードを書く前に、画面に変更を加えて渡した値を足し算してくれるようにしたいと思います。

<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">

<div>
  {{right_operand}} + {{left_operand}} = {{result}}
</div>
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'angular-test';
  right_operand = 10;
  left_operand = 20;
  result = this.right_operand + this.left_operand;
}

コンポーネントに存在する right_operand, left_operand, result をビューにバインドして表示しているだけです。 これぐらいなら既存のテストコードに追加するだけでいいんですが、厄介なのが画面のために追加した機能がある場合。 例えば、ダイアログを追加したり、他のコンポーネントを追加したりすると、テストの準備だけで大変になったりします。

ロジック部分だけテストしたいのに、一切使わないものを作らないといけないのは煩わしいので、足し算のようなロジックや、保持しておくデータは積極的にサービスに追いやります。

ng generate service app でサービスを作成します。

以下のようなコードが作成されます。

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

@Injectable({
  providedIn: 'root'
})
export class AppService {

  constructor() { }
}

テストも作られているので、そちらも見てみます。

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

import { AppService } from './app.service';

describe('AppService', () => {
  beforeEach(() => TestBed.configureTestingModule({}));

  it('should be created', () => {
    const service: AppService = TestBed.get(AppService);
    expect(service).toBeTruthy();
  });
});

短くてまだ理解しやすそうです。コンポーネントのテストが面倒くさいというのが少し理解できましたでしょうか。 以下のようなテストを追加します。

  it('1 + 1 の結果は 2 であるべき', () => {
    const service: AppService = TestBed.get(AppService);
    expect(1+1).toEqual(5000);
  });

追加されて、成功しましたか? いまいち成功したかわからなければ、失敗を書いてみましょう。 例えば、最後をexpect(1+1).toEqual(5000); のようにします。 失敗すると、赤いバーが表示され、5 specs, 1 failure と表示されます。

動作確認ができたので、実際にサービスに組み込んでテストをします。 まずはテストを変更しましょう。サービスには、足し算する対象を二つ保持し、それらを足し合わせた結果を持ちたいサービスだとします。以下のようなテストを考えました。

  it('right_operand に値を渡すと渡した値を持つ', () => {
    const service: AppService = TestBed.get(AppService);

    service.right_operand = 10;
    
    expect(service.right_operand).toEqual(10);
  });

残念ながら、テストは失敗しません(コンパイルエラーは起こりますが)。エラーを起こさないようにサービス側に変数を追加します。変数は private にしましょう。

export class AppService {

  private rightOperand: number = 0;

  constructor() { }
}

まだエラーが出ます。private なので アクセサ(setter) を作ってやる必要があります。以下のようになりました。

export class AppService {
  private rightOperand: number = 0;

  constructor() { }

  public set rightOperand(v : number) {
    this._rightOperand = v;
  }
}

まだエラーが出ます。Expected undefined to equal 10. どうやら戻り値が未定義になっていて、undefined10 を比較しているようです。 今度は、getter を作ってやりましょう。

export class AppService {
  private _rightOperand: number = 0;

  constructor() { }

  public set rightOperand(v : number) {
    this._rightOperand = v;
  }
  
  public get rightOperand() : number {
    return this._rightOperand;
  }
}

テストが成功しました! leftOperand にも同じ実装を施して、最後に足し算をテストします。 以下のようなテストを作りました。

  it('3 + 4 = 7 であるべき', () => {
    const service: AppService = TestBed.get(AppService);

    service.leftOperand = 3;
    service.rightOperand = 4;

    expect(service.addOperand()).toEqual(7);
  });

とても簡単なのでサービス側の実装は、載せません。

最後に、コンポーネント側のオペランドたちをサービスに置き換えます。

export class AppComponent {
  title = 'angular-test';

  constructor(private appService: AppService) {
    appService.rightOperand = 30;
    appService.leftOperand = 20;
  }
}
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
  <h1>
    Welcome to {{ title }}!
  </h1>
</div>

<div>
  {{appService.rightOperand}} + {{appService.leftOperand}} = {{appService.addOperand()}}
</div>

これで Angular のテストを行える知恵を身につけることができました!

参考

angular.jp

Magic Keyboard の Eject ボタンを Karabiner を使って、フルスクリーンショートカットに割り当てる

f:id:pickles-ochazuke:20190112163941j:plain

Mac mini を買いました。初めての MacOS です。スペックは i5 でメモリ16GBです。 デフォルトで CUI の文字がキレイなのはかなりいいですね。まだ慣れていませんが徐々に使いこなせるようになりたいです。

Mac 用にキーボードは、MagicKeyboard を買いました。カーソルキーが右下にあって使いづらいですが、Karabiner というアプリを使ってちょっといじっています。 Magic Keyboard には右上に Eject ボタンがあるのですが、こいつはドライブを開くボタンです。いらない……。ショートカットでスリープしたりできるみたいですが、そんな頻繁にスリープしないのでやっぱりいらない。 なので、自分としてはよく使うフルスクーンのショートカットに割り当てることにしました。

~/.config/karabiner/karabiner.json を開いて、以下を追加。 追加場所は、rules の中。

"rules": [
{
  "description": "eject をフルスクリーンショートカットに変更",
  "manipulators": [
    {
      "type": "basic",
      "from": {
        "consumer_key_code": "eject"
      },
      "to": [
        {
          "key_code": "f",
          "modifiers": [
            "command",
            "control"
          ]
        }
      ]
    }
  ]
}
]

味噌は from のとこの key_code ではなく、consumer_key_code にしているところです。

超簡易な xUnit を Kotlin 入門しながら書く

f:id:pickles-ochazuke:20181202154024j:plain

Pixel 3 で写真撮るの楽しいです。久々の更新です(毎回言ってる気がする)。
Kotlin Advent Calendar 2018 5日目の記事です。

qiita.com

テスト駆動開発の本に書かれているプログラムを Kotlin で書いてみました。自分は Kotlin を触って日が浅いのですが、入門本を一冊読んでみたので何か書いてみようかと思ったのと、最近 「テスト駆動開発」という本を読んだので試しに xUnit 書いてみるかと。

クラスは、TestCaseTest, TestCase, WasRun, TestResult, TestSuite を作り、関数として getAttr 関数を作りました(機能的には getMethod という感じですが)。

Python では、オブジェクトの変数やメソッドを取り出す getAttr 関数があるのですが、Kotlin にはなさそうだったので、メソッドを取り出す関数を作りました。といってもただ Java のリフレクションを使っただけですが。以下のような感じです。

import java.lang.reflect.Method

fun getAttr(obj: Any, method_name: String): Method {
  return obj.javaClass.getMethod(method_name)
}

メソッドや関数の宣言の仕方はあとで書いてあるので飛ばしますが、上記のようにインポートすれば、Java のライブラリを使うことが出来ます。もっと上手いやり方がある気がするのですが本題じゃないので今回は上記のようにしました。

まず、基本クラスになる TestCase ですが、以下のようになります。

open class TestCase(var name: String) {
  open fun setUp() {
  }
  open fun tearDown() {
  }
  
  fun run(result: TestResult) {
    result.testStarted()
    setUp()
    try {
      val method = getAttr(this, this.name)
      method.invoke(this)
    }
    catch(e: Exception) {
      result.testFailed()
    }
    tearDown()
  } 
}

open class ですが、Kotlin はデフォルトで継承が禁止されています。なので、継承させるために open を指定あげます。class は察しの通りクラスの定義を意味します。

次の TestCase(var name: String) は、TestCase がクラスの名前で、括弧内はクラスの持つ変数の宣言とプライマリコンストラクタの仮引数を表します。このように書くことで引数の値が宣言されている変数内に格納されます。ちなみに name は、テストしたいメソッド名を持ちます。

理解しやすいように書くと以下と同じ意味になります。

// open class TestCase constructor(name: String) でも同じ意味
open class TestCase(name: String) {
  var name: String

  init {
    this.name = name
  }
}

これでコンストラクタと変数の初期化の仕方が分かりました。次は、メソッドです。 open fun setUp(), open fun tearDown() ですが、open でオーバーライドを許可しているメソッドになります。

fun run() メソッドですが、仮引数の TestResultクラスは、テスト結果をカウントするクラスです。 run メソッドは、テストを実行するメソッドです。初めにテストの回数を増やし、テスト前に必要な初期化処理を行います(実際は継承先でオーバーライドされている処理が走ります)。その後、例外処理の try ブロック内でメソッドを実行します。失敗したときにプログラムが終了しないように catch 内で テストが失敗したことをカウントします。

Kotlin のアクセス修飾子ですが、デフォルトでは public です。Java とは異なるので要注意です。

こちらが参考になると思います。 qiita.com

次に WasRun クラスを見ていきます。

class WasRun(name: String): TestCase(name) {
  var log: String

  init {
    log = ""
  }

  override fun setUp(): Unit {
    log = "setUp"
  }
  fun testMethod(): Unit {
    log = "${log} testMethod"
  }
  override fun tearDown() {
    log = "${log} tearDown"
  }
  fun testBrokenMethod() {
    throw Exception()
  }
}

WasRun クラスは、テスト対象となるクラスです。ここでは、TestCase のテストを行いたいので、TestCase を継承しています。class WasRun(name: String): TestCase(name) で使われているコロン(:)は、それのあとに書かれているクラスを継承することを意味しています。そして、コンストラクタに渡す引数として、WasRunクラスの変数を渡しています。 残りのメソッドは、オーバーライドをしていたり、テストに必要なメソッドを追加しています。testBrokenMethod() 内の throw Exception() ですが、見ての通りエラーを発生させています。

また、"${log} testMethod" のように文字列テンプレートを書くことが出来ます。

最後に、TestSuite クラスを見ていきます。

class TestSuite(val tests: MutableList<TestCase> = arrayListOf<TestCase>()) {

  var result: TestResult
  
  init {
    result = TestResult()
  }

  fun add(test: TestCase) {
    tests.add(test)
  }

  fun run(result: TestResult) {
    tests.forEach{
      it.run(result)
    }
  }
}

TestSuite クラスは、テストをまとめて行うためのクラスです。class TestSuite(val tests: MutableList<TestCase> = arrayListOf<TestCase>()) は、変数 tests に引数がなければデフォルト引数として arrayListOf<TestCase>() を入れるようにしています。 Kotlin の List についてはまだ詳しくないのですが、 readOnly と mutable なリストの二種類があります。簡単に紹介すると readOnly なリストは、中身を変えることができないリストで、 mutable なリストは、中身を変えることができるリストです。ただし、readOnly は以下のように書くことで中身を変えることが出来ます。

var list = mutableListOf(10, 20, 30)
var list2: List<Int> = list
list[0] = 33

リストについては以下が参考になると思います。

qiita.com

qiita.com

TestSuite では、様々なテストを tests にまとめておきます。そして run メソッドによってリストの中身をすべて実行していきます。

fun run(result: TestResult) {
  tests.forEach{
    it.run(result)
  }
}

簡単ですが、以上で Kotlin で作った xUnit の紹介を終わります。

Kotlin を使ってみた感想

書いてて楽しい言語だなと思いましたね。セミコロン必要ないですし、デフォルトで public になっていたり、継承がコロンだけで済んだり、また、継承もデフォルトで禁止されているのは良いなと思いました。null を許容しないのもいいですね。

Kotlin は、短く書けるけど何でもかんでも短くはしないというか、書きやすいと読みやすいのバランスが良いという感じがしました。

ぜひ仕事で使ってみたいので、修得してサーバやスマフォ開発で使いたいですね。

参考

入門するために読んだ本です。これは何か言語を知っていれば短時間で Kotlin にどんな機能があるのか把握できるので、とてもよかったです(何か言語を使ったことある前提)。

速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズ

速習 Kotlin: Javaより簡単!新Android開発言語を今すぐマスター 速習シリーズ

テスト駆動開発の本です。改訂版ですが、とても翻訳のクオリティが高く、言語も今に合わせて翻訳者が書き直してくれています。しかも付録として歴史的な話も載っています。さらに値段も安いです。すごいです…。

テスト駆動開発

テスト駆動開発

以下もおすすめらしいです。

Kotlinイン・アクション

Kotlinイン・アクション

記事内のコード

github.com

おれ、今の仕事納めたら Kotlin 勉強するんだ……

NgModule について

今回は、ngModule について説明していきます。 といってもまだ理解しきれていないので、簡単な説明ですが……。

NgModule

ng コマンドでプロジェクトを作成すると、app フォルダの中に、app.module.ts というファイルが作られます。 例えば以下のような感じです。

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

import { AppComponent } from './app.component';

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

この app.module.ts は、AppModule と呼ばれ、ルートモジュールとも呼ばれます。アプリの起動時は、初めにこのモジュールが読まれることになります。また、NgModule は一つだけではなく、FeatureModule を利用して、このルートモジュールの子として含めることができるようです。

アプリを起動するときに必要な情報を @NgModule に記述していきます。@NgModule を使えるようにするには、import { NgModule } from '@angular/core' を記述する必要があります。

@NgModule 内には、メタデータを定義していきます。

  • declaratons は、アプリに必要なコンポーネント、ディレクティブ、パイプを登録します。ng generate [component | directive | pipe] で作成した場合、自動で追加されます。
  • imports は、使いたい他のモジュールをインポートします(よく利用するものはこちらです)。
  • providers は、シングルトンにしたいサービスを登録します。
  • bootstrap は、アプリのルートとなるコンポーネントを登録します。

Angular は、JavaScriptimport と Angular の imports を使って、一部のモジュールを扱えるようにしています。最初は紛らわしいのですが、使っているうちに慣れていきます(それでいいのか……)。

モジュールのイントロダクション

簡単になりましたが今回はこのへんで。公式のドキュメントは充実してるので、一度目を通すことをオススメします。

NgModule