私は現在、新しい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>
編集:これはエラーの例です:
公式の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(...);
}
}
あなたはそれをすべて混ぜ合わせています。
トークンはプレーンオブジェクトになるように作成されます。依存性注入を伴うサービスを使用することにより、それをトークンとして宣言することはできません。
オンデマンドでサービスを作成するには、ファクトリを使用する必要があります。ファクトリは、指定されたプロパティを使用してクラスのインスタンスを作成する関数です。
私の例では、エンドポイントを特定のプロパティとして追加しましたが、ファクトリで何でも実行できます。
Stackblitz: https://stackblitz.com/edit/angular-dg1hut?file=src/app/search-bar/search-bar.component.ts
const factory = (http: HttpClient) => new CustomerService(http, '/api/v1/customer')
// ...
providers: [{
provide: CustomerService,
useFactory: factory,
deps: [HttpClient]
}],
export class SearchBarComponent implements OnInit {
@Input() source: string;
constructor(private searcher: CustomerService) { }
ngOnInit() { console.log(this.searcher); }
search() { return this.searcher.search(''); }
}
AppModuleのプロバイダーのリストにサービスを実装する代わりに、私たちにできることの1つとして、サービスの注入可能なアノテーションにrootInパラメーターにprovidedInを追加することができます。
https://angular.io/guide/providers#providing-a-service
例:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
しかし、もう1つの方法があります。あなたの方法です。サービスは、IncpetionTokenを必要とせずにモジュールのプロバイダーのリストで使用できます。すでに機能しているため、プロバイダーのリストに追加するだけです。
https://angular.io/guide/providers#provider-scope
例:
@NgModule({
declarations: [
AppComponent,
SearchBarComponent
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule
],
providers: [
CustomerService // <-- You don't need to create any token.
],
bootstrap: [AppComponent]
})
export class AppModule { }