简体   繁体   中英

How can I inject a parameter from a component to a service in Angular?

Greetings,

While I was following the Tour of Heroes guide, I wanted to experiment a little with having multiple data array options and with which heroesUrl I use to ask for data from the InMemoryDbService. I'd like to be able to pass the heroesUrl parameter from the heroes component to the heroes service to determine which batch of heroes I'd like to get from the db, but I'm getting the following error message in the console:

vendor.js:14812 ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AppModule)[HeroService -> heroesUrl -> heroesUrl -> heroesUrl]: 
  NullInjectorError: No provider for heroesUrl!
NullInjectorError: R3InjectorError(AppModule)[HeroService -> heroesUrl -> heroesUrl -> heroesUrl]: 
  NullInjectorError: No provider for heroesUrl!
    at NullInjector.get (vendor.js:10919)
    at R3Injector.get (vendor.js:24642)
    at R3Injector.get (vendor.js:24642)
    at R3Injector.get (vendor.js:24642)
    at injectInjectorOnly (vendor.js:10774)
    at Module.ɵɵinject (vendor.js:10784)
    at Object.HeroService_Factory [as factory] (main.js:503)
    at R3Injector.hydrate (vendor.js:24869)
    at R3Injector.get (vendor.js:24630)
    at NgModuleRef$1.get (vendor.js:41631)
    at resolvePromise (polyfills.js:806)
    at resolvePromise (polyfills.js:765)
    at polyfills.js:867
    at ZoneDelegate.invokeTask (polyfills.js:413)
    at Object.onInvokeTask (vendor.js:46107)
    at ZoneDelegate.invokeTask (polyfills.js:412)
    at Zone.runTask (polyfills.js:181)
    at drainMicroTaskQueue (polyfills.js:583)

It says that there is no provider for heroesUrl, so I suspect that I am not defining the provider in the component the right way:

import { Component, OnInit } from "@angular/core";

import { Hero } from "../hero";
import { HeroService } from "../hero.service";
import { MessageService } from "../message.service";

@Component({
  selector: "app-heroes",
  templateUrl: "./heroes.component.html",
  styleUrls: ["./heroes.component.scss"],
  providers: [{ provide: "heroesUrl", useValue: "heroesTwo" }]  //<-------------
})
export class HeroesComponent implements OnInit {
  heroes: Hero[];

  constructor(
    private heroService: HeroService,
    public messageService: MessageService
  ) {}

  ngOnInit(): void {
    this.heroService.getHeroes().subscribe(heroes => (this.heroes = heroes));
  }
}

This is what the service looks like, where I attempted to inject the parameter:

import { Observable, of } from "rxjs";

import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Injectable, Input, Inject, Optional } from "@angular/core";

import { Hero } from "./hero";
import { MessageService } from "./message.service";

import { catchError, map, tap } from "rxjs/operators";

@Injectable({
  providedIn: "root"
})
export class HeroService {
  constructor(
    private http: HttpClient,
    private messageService: MessageService,
    @Inject("heroesUrl") private heroesUrl: string //<-------------
  ) {}

  getHeroes(): Observable<Hero[]> {
    return this.http.get<Hero[]>(this.heroesUrl).pipe(
      tap(tappedData =>
        this.log("Fetched heroes: " + tappedData.map(data => data.name))
      ),
      catchError(this.handleError<Hero[]>("getHeroes", []))
    );
  }

  getHero(id: number): Observable<Hero> {
    const url = this.heroesUrl + "/" + id;
    return this.http.get<Hero>(url).pipe(
      tap(_ => this.log("fetched hero id=" + id)),
      catchError(this.handleError<Hero>("getHero id=" + id))
    );
  }

  private log(message: string) {
    this.messageService.add(`HeroService: ${message}`);
  }

  private handleError<T>(operation = "operation", result?: T) {
    return (error: any): Observable<T> => {
      console.error(error);
      this.log(`${operation} failed: ${error.body.error}`);
      return of(result as T);
    };
  }
}

In the service that intercepts the http calls, implementing InMemoryDbService:

return { heroesOne: heroes1, heroesTwo: heroes2 };

I've been looking at these guides in particular:

Could someone please explain what I'm doing wrong?

添加这一行 -> providers: [{ provide: "heroesUrl", useValue: "heroesTwo" }] 到 Module.ts 文件提供者数组而不是组件 ts 文件并尝试。

You need to declare your heroesUrl in the providers of your app.module.ts but then every heroes service would have the same url provided.

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [
    HeroService,
    { provide: "heroesUrl", useValue: "heroesOne" }
  ],
  bootstrap: [AppComponent]
})

Or you declare your provider in the component using the service and the url too and then every component will have its own service with its own url.

@Component({
  selector: "app-heroes",
  templateUrl: "./heroes.component.html",
  styleUrls: ["./heroes.component.scss"],
  providers: [
    HeroService,
    { provide: "heroesUrl", useValue: "heroesTwo" }
  ]
})

And you can combine the two to have a generic HeroesService with the "heroesOne" url and others HeroesService with others url for specific components.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM