0

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?

2
  • Do you provide an heroesUrl in your app.module.ts ? And did you try with {provide: heroesUrl, useValue:"heroesTwo"} without the quotes on heroesurl. Commented Mar 4, 2020 at 12:59
  • If I try without the quotes on heroesUrl, it doesn't know what the heroesUrl variable is and throws an error. Commented Mar 4, 2020 at 15:16

2 Answers 2

1

Add this line -> providers: [{ provide: "heroesUrl", useValue: "heroesTwo" }] to Module.ts file provider array instead of component ts file and try.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi, Thank you for answering. If I do what you suggested, I get an error that says "Collection 'undefined' not found" in the console.
Could you please give more description about an error that you are facing.
Please read through my conversation with David Gonzales in the other comment thread, maybe that will satisfy your thirst for elaboration.
0

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.

7 Comments

Hi. Thanks for the reply. If I add " providers: [ HeroService, { provide: "heroesUrl", useValue: "heroesTwo" } ]" to the component, I still get the error like before. If I add it to the app.module.ts, I get a different error saying: {body: {…}, url: "heroesOne", headers: HttpHeaders, status: 404, statusText: "Not Found"} body: {error: "Collection 'undefined' not found"}
If you add the HeroService to you Component and don't mind of having a new Singleton of hero service for everycomponent you should remove the HeroService you have in the app.module.ts. You get the error because you do not provide a "heroesUrl" for the HeroService that is created at the root of your application (The one you delare in the app.module.ts)
For the second error it's an http error and as nothing to do with you're first question. The error is quite explicit it can not find anything at the url "heroesOne".
this is in the inMemoryDb return { heroesOne: heroes1, heroesTwo: heroes2 }; that cathces those http calls, and I called it earlier without the injected property and it works fine. If I remove the HeroService from app.module.ts and there is no reference to it whatsoever in the file, and the provider line is: providers: [{ provide: "heroesUrl", useValue: "heroesTwo" }], I still get the same error saying collection undefined not found (also the 404 one with heroesTwo or heroesOne, depending on which one I used.)
How is your inMemoryDb linked to your url ? I can guaranty you that the errors 404 as nothing to do with the property injection. It's an http error. That error is sent back on your http request meaning that what you wanted to achieve with the property injections works.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.