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

问题描述 投票:0回答:6

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

为此,我使用

Injector
,并在
ngOnInit
期间加载服务。当使用基于字符串的提供程序时,这工作得很好,但是我的 IDE 指出它已被弃用,我应该使用
InjectionToken
,我似乎无法理解它。

我希望以下代码能够工作,因为删除

InjectionToken
的所有实例并将其替换为直接字符串文字是可行的:

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

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

模块声明

// 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>

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

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

angular typescript dependency-injection
6个回答
26
投票

在询问官方角度存储库后,结果发现这是一个简单的解决方案。您无需将服务名称作为字符串传递,而是通过组件将令牌传递到另一个组件的视图中。

全局定义注入令牌

我将其与我的服务本身一起执行,以便更容易跟踪。

@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(...);
  }
}

10
投票

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


3
投票

您可以做一些事情,而不是将服务实现到 AppModule 的提供者列表中,您只需将providIn 添加到服务的可注入注释的根参数即可。

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 { }

2
投票

你把这一切都搞混了。

令牌被制成普通对象。通过使用具有依赖项注入的服务,您无法将其声明为令牌。

要按需创建服务,您必须使用工厂。工厂是一个函数,它将创建具有给定属性的类的实例。

在我的示例中,我已将端点添加为给定的属性,但您可以在工厂中执行任何您想要的操作。

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
投票

对于 2023 年具有独立组件的 Angular 15,我们应该在路由中定义注入令牌,对于我使用 mfe 和 libs 的情况,如果需要注入令牌,我们需要将它们放入

entryRoutes.ts
数组

export const remoteRoutes: Route[] = [
  {
    path: '',
    component: RemoteEntryComponent,
    children: uiRoutesChildren,
    providers: [
      {
        provide: 'API_URL',
        useValue: API_URL,
      },
    ],
  },
];

来源:https://angular.io/guide/standalone-components


0
投票

今天我得到了这个,这是因为我尝试将

type
添加到导入语句中。我没有注意到,但删除这些解决了问题。

问题示例。 (只需删除

type
字样即可解决。 enter image description here

错误示例:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.