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:
https://www.inversionofcontrol.co.uk/injection-tokens-in-angular/
https://dev.to/leonelngande/passing-additional-parameters-to-an-angular-service-pma
Could someone please explain what I'm doing wrong?