1. ホーム
  2. angular

[解決済み] Angular InjectionTokenが「No provider for InjectionToken」をスローします。

2022-02-19 23:56:57

質問

現在、新しいAngularフレームワークを学んでいますが、バックエンドサービスに問い合わせるためのサービスを動的に解決するために、サービス名を引数として受け取る動的検索バーを作ろうとしています。

このために、私は Injector の間にサービスをロードします。 ngOnInit . これは文字列ベースのプロバイダを使用しているときはうまくいきますが、私の IDE では、これは非推奨であり InjectionToken というのは、どうも腑に落ちない。

のインスタンスをすべて削除することで、以下のコードが機能することを期待しました。 InjectionToken という文字列リテラルに直接置き換えることで動作します。

以下のドキュメントを見てみましたが、書いてある通りにやったのに、うまくいかないと言われ続けている気がして、よく理解できませんでした。 https://angular.io/guide/dependency-injection-providers

何が間違っているのか、誰か教えてください。
ありがとうございます

モジュール宣言

// app.module.ts
@NgModule({
  declarations: [
    AppComponent,
    SearchBarComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    AppRoutingModule
  ],
  providers: [
    {
      provide: new InjectionToken<ISearchable>('CustomerService'), // <-- doesn't work;  'CustomerService' <-- works
      useValue: CustomerService
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

検索バーコンポーネントです。

// search-bar.component.ts
@Component({
  selector: 'search-bar',
  templateUrl: './search-bar.component.html',
  styleUrls: ['./search-bar.component.sass']
})
export class SearchBarComponent implements OnInit {

  @Input()
  source: string;

  private searcher: ISearchable;

  constructor(private injector: Injector) {}

  ngOnInit() {
    // error: Error: No provider for InjectionToken CustomerService!
    let token = new InjectionToken<ISearchable>(this.source);
    this.searcher = this.injector.get<ISearchable>(token);

    // this works, but it's deprecated and will probably break in the future
    // this.searcher = this.injector.get(this.source);
    console.log(this.searcher);
  }
}

検索バーを使う

<!-- app.component.html -->
<div class="row justify-content-center mb-2">
  <div class="col-8">
    <search-bar title="Customers" source="CustomerService"></search-bar>
  </div>
</div>


Edit: エラーが発生した例です。

https://stackblitz.com/edit/angular-3admbe

解決方法は?

angularの公式リポジトリで聞いてみたところ、簡単な解決策であることが判明しました。サービス名を文字列で渡す代わりに、トークンをコンポーネントからビューに通して、別のコンポーネントに渡す必要があります。

インジェクショントークンをグローバルに定義する

私はこれを自分のサービス自体と並行して行い、追跡を容易にしました。

@Injectable()
export class CustomerService implements ISearchable { ... }

export const CUSTOMER_SERVICE = new InjectionToken<ISearchable>('CustomerService');

インジェクショントークンをアプリプロバイダに登録する

import {CUSTOMER_SERVICE, CustomerService} from "./services/customer/customer.service";


@NgModule({
  declarations: [ ... ],
  imports: [ ... ],
  providers: [
    {
      provide: CUSTOMER_SERVICE,  // That's the token we defined previously
      useClass: CustomerService,  // That's the actual service itself
    }
  ],
  bootstrap: [ ... ],
})
export class AppModule { }

トークンをビューから他のコンポーネントに渡します。

// In your component
import {CUSTOMER_SERVICE} from "./services/customer/customer.service";


@Component({
  selector: 'app-root',
  template: '<app-search-bar [source]="searcher"></app-search-bar>'
})
export class AppComponent
{
  searcher = CUSTOMER_SERVICE;
}

他のコンポーネントから動的にサービスをインポートできるようになりました。

@Component({
  selector: 'app-search-bar',
  templateUrl: './search-bar.component.html',
  styleUrls: ['./search-bar.component.sass'],
})
export class SearchBarComponent implements OnInit
{
  @Input()
  source: InjectionToken<ISearchable>;

  private searcher: ISearchable;

  constructor(private injector: Injector) {}

  ngOnInit()
  {
    this.searcher = this.injector.get<ISearchable>(this.source);
  }

  search(query: string)
  {
    this.searcher.search(query).subscribe(...);
  }
}