Angular InjectionToken выбрасывает «Нет поставщика для InjectionToken» - PullRequest
0 голосов
/ 15 ноября 2018

В настоящее время я изучаю новую платформу 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>

Редактировать: Вот пример с ошибкой:

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

Ответы [ 3 ]

0 голосов
/ 15 ноября 2018

Вы все путаете.

Жетоны сделаны как простые объекты. Используя сервис с внедрением зависимости, вы не можете объявить его как токен.

Для создания услуг по требованию вам придется использовать фабрику. Фабрика - это функция, которая будет создавать экземпляры вашего класса с заданными свойствами.

В моем примере я добавил конечную точку в качестве данного свойства, но вы можете делать все, что захотите на своей фабрике.

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(''); }
}
0 голосов
/ 20 ноября 2018

Спросив в официальном угловом репозитории, получается простое решение. Вместо передачи имени службы в виде строки, вы должны передать токены через компонент в представление в другой компонент.

Глобально определить токен впрыска

Я сделал это вместе с самой службой, чтобы было легче отслеживать.

@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(...);
  }
}
0 голосов
/ 15 ноября 2018

Что-то, что вы можете сделать нам, вместо того, чтобы внедрять службу в список поставщиков AppModule, вы можете просто добавить в поле «root» параметр для инъекционной аннотации службы.

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