providedInオプションを使うべき?使わないべき?

2024-04-02

Angular 6におけるInjectableデコレータのprovidedInオプションの目的

providedInオプションは、サービスのインスタンスをどこで生成するかを指定します。以下の3つの値を設定できます。

  • root: サービスはルートインジェクタで生成され、アプリケーション全体でシングルトンとして提供されます。
  • any: サービスはコンポーネント、ディレクティブ、サービスなど、どこからでも注入できます。
  • custom: サービスはカスタムのインジェクタで生成されます。

providedInオプションの使用例

サービスをルートインジェクタで生成する

@Injectable({
  providedIn: 'root'
})
export class MyService {
  // ...
}

上記のコードでは、MyServiceはルートインジェクタで生成されます。つまり、アプリケーション全体でMyServiceの唯一のインスタンスが作成され、すべてのコンポーネントやサービスから注入できます。

サービスをコンポーネントで生成する

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  constructor(private readonly myService: MyService) {
    // ...
  }
}

@Injectable({
  providedIn: MyComponent
})
export class MyService {
  // ...
}

上記のコードでは、MyServiceMyComponentコンポーネントで生成されます。つまり、MyComponentコンポーネントのインスタンスごとにMyServiceの新しいインスタンスが作成されます。

サービスをカスタムインジェクタで生成する

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

@Injectable()
export class MyCustomInjector {
  // ...
}

@Injectable({
  providedIn: MyCustomInjector
})
export class MyService {
  // ...
}

上記のコードでは、MyServiceMyCustomInjectorカスタムインジェクタで生成されます。

  • サービスの依存関係注入をより細かく制御できる
  • サービスのライフサイクルをより明確に定義できる
  • コードのテストがしやすくなる

providedInオプションを使用するデメリット

  • コードの複雑さが増す
  • 誤った設定により、予期せぬ動作が発生する可能性がある

providedInオプションは、Angular 6におけるサービスの生成と依存関係注入を制御するための強力なツールです。オプションの役割と使用例を理解し、適切な設定を行うことで、コードの品質とパフォーマンスを向上させることができます。




サービスをルートインジェクタで生成する

// my-service.ts

@Injectable({
  providedIn: 'root'
})
export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// my-component.ts

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  constructor(private readonly myService: MyService) {
    console.log('MyComponent created!');
    this.myService.doSomething();
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
MyService created!
MyComponent created!
MyService doing something!

サービスをコンポーネントで生成する

// my-service.ts

@Injectable({
  providedIn: MyComponent
})
export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// my-component.ts

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  constructor(private readonly myService: MyService) {
    console.log('MyComponent created!');
    this.myService.doSomething();
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

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

上記のコードを実行すると、コンソールに以下の出力が出力されます。

MyService created!
MyComponent created!
MyService created!
MyService doing something!

サービスをカスタムインジェクタで生成する

// my-custom-injector.ts

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

@Injectable()
export class MyCustomInjector {
  constructor() {
    console.log('MyCustomInjector created!');
  }

  get(token: any) {
    if (token === MyService) {
      return new MyService();
    }

    return null;
  }
}

// my-service.ts

@Injectable({
  providedIn: MyCustomInjector
})
export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// my-component.ts

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  constructor(private readonly myService: MyService) {
    console.log('MyComponent created!');
    this.myService.doSomething();
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    MyCustomInjector
  ],
  bootstrap



サービスの生成方法

コンストラクタインジェクション

コンポーネントまたは他のサービスのコンストラクタでサービスを直接注入できます。

// my-service.ts

export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// my-component.ts

@Component({
  selector: 'my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  constructor(private readonly myService: MyService) {
    console.log('MyComponent created!');
    this.myService.doSomething();
  }
}

サービスファクトリーを使用してサービスを生成できます。

// my-service-factory.ts

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

@Injectable()
export class MyServiceFactory {
  constructor() {
    console.log('MyServiceFactory created!');
  }

  create() {
    return new MyService();
  }
}

// my-service.ts

export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    MyServiceFactory,
    {
      provide: MyService,
      useFactory: (myServiceFactory: MyServiceFactory) => myServiceFactory.create()
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

NgModuleprovidersプロパティを使用してサービスを生成できます。

// my-service.ts

export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    MyService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
// my-service-locator.ts

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

@Injectable()
export class MyServiceLocator {
  private readonly services = new Map<any, any>();

  constructor() {
    console.log('MyServiceLocator created!');
  }

  get(token: any) {
    if (!this.services.has(token)) {
      this.services.set(token, new token());
    }

    return this.services.get(token);
  }
}

// my-service.ts

export class MyService {
  constructor() {
    console.log('MyService created!');
  }

  doSomething() {
    console.log('MyService doing something!');
  }
}

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MyComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [
    MyServiceLocator,
    {
      provide: MyService,
      useFactory: (myServiceLocator: MyServiceLocator) => my

angular typescript angular6


TypeScriptでオブジェクトを機能豊富にする:関数プロパティとメソッドの活用術

共通点:オブジェクトのプロパティとして定義される関数オブジェクトに対して機能を提供する型注釈を使用して型安全性を担保できる相違点:例:使い分け:単純な関数を提供したい場合は、関数プロパティが簡潔で読みやすいです。オブジェクトの状態に依存した処理や、this キーワードへのアクセスが必要な場合は、メソッドが適切です。...


Angular テンプレートで ngForOf を使用した際に発生するエラー "Can't bind to 'ngForOf' since it isn't a known property of 'tr' (final release)" の原因と解決方法

Angular テンプレートで ngForOf ディレクティブを tr 要素で使用すると、Can't bind to 'ngForOf' since it isn't a known property of 'tr' (final release) というエラーが発生することがあります。これは、tr 要素が ngForOf ディレクティブをサポートしていないためです。...


JavaScript、Angular、TypeScriptで「Property 'entries' does not exist on type 'ObjectConstructor'」エラーが発生したときの解決策

このエラーは、JavaScript、Angular、TypeScriptでオブジェクトのentries()メソッドを使用しようとした際に発生します。entries()メソッドは、オブジェクトのキーと値のペアをイテレータとして返すために使用されます。...


Angularテンプレートの再利用:コンポーネント、ディレクティブ、サービス、サードパーティライブラリ

コンポーネントは、AngularでテンプレートHTMLブロックを再利用するための最も強力な方法です。 コンポーネントは、独自のテンプレート、スタイル、ロジックを持つ独立したモジュールです。 コンポーネントを使用すると、テンプレートHTMLブロックを他のコンポーネントで再利用できます。...


AngularとTypeScriptにおける「TS1086: An accessor cannot be declared in ambient context」エラー:原因と解決策

「TS1086: An accessor cannot be declared in ambient context」エラーは、AngularとTypeScriptを使用する開発者にとって一般的な問題です。このエラーは、アクセサー(getter/setter)を環境コンテキストで宣言しようとした場合に発生します。環境コンテキストとは、実際のコードを実行する前に宣言された変数や型などの定義を格納する場所です。...