我目前正在学习新的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>
编辑:这是一个带有错误的示例:
答案 0 :(得分:1)
您可以执行一些操作,而不是将服务实现到AppModule的提供程序列表中,只需将providerIn添加到根参数中即可注入服务的可注解。
https://angular.io/guide/providers#providing-a-service
示例:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
但是,还有另一种方法,您的方法。由于已被检测,因此可以将服务用于模块的提供者列表,而无需任何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 { }
答案 1 :(得分:1)
您正在将所有内容混合在一起。
令牌被制成普通对象。通过将服务与依赖项注入配合使用,您不能将其声明为令牌。
要按需创建服务,您将不得不使用工厂。工厂是将使用给定属性创建类实例的函数。
在我的示例中,我已将端点添加为给定属性,但是您可以在工厂中执行任何操作。
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(''); }
}
答案 2 :(得分:1)
在询问了官方的角度存储库之后,事实证明这是一个简单的解决方案。无需将服务名称作为字符串传递,而是将令牌通过组件传递到视图中的另一个组件中。
我与服务本身一起进行了此操作,以使其更易于跟踪。
@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(...);
}
}