Angular InjectionToken 抛出“没有 InjectionToken 的提供者”

Par*_*xis 12 dependency-injection typescript angular

我目前正在学习新的 Angular 框架,我正在尝试制作一个动态搜索栏,它接受服务名称作为参数,以便动态解析服务以查询后端服务。

为此,我使用Injector, 并在ngOnInit. 这在使用基于字符串的提供程序时工作正常,但是我的 IDE 指出它已被弃用,我应该使用一个InjectionToken我似乎无法理解的。

我希望以下代码可以工作,因为删除所有实例InjectionToken并将其替换为直接字符串文字有效:

我尝试查看以下文档,但我不太明白,因为我觉得我完全按照它说的做了,但它一直告诉我它不起作用:https : //angular.io/guide/dependency-注射提供者

有人能告诉我我做错了什么吗?
谢谢

模块声明

// 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 { }
Run Code Online (Sandbox Code Playgroud)

搜索栏组件:

// 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

使用搜索栏:

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个错误示例:

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

Par*_*xis 9

在官方 angular 存储库上询问后,结果证明这是一个简单的解决方案。您不必将服务名称作为字符串传递,而是将令牌通过组件传递到视图中,然后传递到另一个组件中。

全局定义注入令牌

我这样做是在我的服务本身旁边进行的,以便更容易跟踪。

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

export const CUSTOMER_SERVICE = new InjectionToken<ISearchable>('CustomerService');
Run Code Online (Sandbox Code Playgroud)

在您的应用程序提供商中注册注入令牌

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 { }
Run Code Online (Sandbox Code Playgroud)

通过视图将令牌传递给您的其他组件

// 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;
}
Run Code Online (Sandbox Code Playgroud)

您现在可以从其他组件动态导入服务

@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(...);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这会创建一个依赖关系,如果你在 2 个独立的模块中,你真的需要它们完全独立。因此,传递原始令牌并不能真正解决问题。.get('TOKEN_NAME'),不应该真的被弃用,这将是现在的解决方案。我希望 Angular 团队意识到这一点。 (2认同)

小智 8

对于任何仍在为此苦苦挣扎的人来说,这可能就像重新启动 VS Code 一样简单,就像我的情况一样。我不知道为什么,但这为我解决了。当然,我首先尝试了这个,因为我很确定这个错误一开始就是错误的。

  • 这实际上是一个不错的答案——我已经说过很多次了。有时,Angular 编译器在服务模式下无法识别模块中的更改,您需要重新启动编译器。 (4认同)