48

While working on a program that I am facing an issue with, constructor and its dependency injection from its child class.

  • DataService: A service class which has all the CRUD operation at one place and also has a parameterized constructor for injecting the end-point URL and HTTP from its child classes.
constructor(private url: string, private http: Http) { }
  • PostService : A service class which extends the above DataService class to have the CRUD operation functionality within it and a one-parameterized constructor and internally calls the super(endPointURL, httpObject) as below :
constructor(http: Http) {
    super('https://jsonplaceholder.typicode.com/posts', http);
}

Before this re-factoring (moving all the common CRUD operation and extending it by child classes) my code was working as expected but AFTER the above changes I am getting the below error:

Date: 2020-03-22T15:26:23.248Z - Hash: 7130497a38c152c58258
5 unchanged chunks

Time: 1859ms

ERROR in src/app/services/data.service.ts:14:23 - error NG2003: No suitable injection token for parameter 'url' of class 'DataService'.
Found string

14   constructor(private url: string, private http: Http) { }

Also, when I remove the url param from Datsource constructor (modify PostService.ts accordingly) the api is working as expected. Not sure why !!!

I am using:

Angular CLI: 9.0.4 Node: 12.16.1 OS: win32 x64

Angular: ... Ivy Workspace:

Package Version

@angular-devkit/architect 0.900.4

@angular-devkit/core 9.0.4

@angular-devkit/schematics 9.0.4

@schematics/angular 9.0.4

@schematics/update 0.900.4

rxjs 6.5.3

DataService.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AppError } from '../common/app-error';
import { NotFoundError } from '../common/not-found-error';
import { BadInput } from '../common/bad-input';

@Injectable()
export class DataService {

//   private url = 'https://jsonplaceholder.typicode.com/posts';

  constructor(private url: string, private http: Http) { }

  getAll() {
    return this.http.get(this.url).pipe(catchError(this.errorHandle));
  } 

  create(resource) {
    return this.http.post(this.url, JSON.stringify(resource))
      .pipe(catchError(this.errorHandle));
  }

  update(resource) {
    return this.http.patch(this.url + '/' + resource.id, JSON.stringify({ isRead: true }))
    .pipe(catchError(this.errorHandle));
  }

  delete(resource) {
    return this.http.delete(this.url + '/' + resource.id)
      .pipe(catchError(this.errorHandle));
  }
  
  private errorHandle(error: Response){
    if (error.status === 404) {
      return throwError(new NotFoundError());
    }
    if (error.status === 400) {
      return throwError(new BadInput(error.json()));
    }
    return throwError(new AppError(error));
  }

}

PostService.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { DataService } from './data.service';

@Injectable()
export class PostService extends DataService {

  constructor(http: Http) {
    super('https://jsonplaceholder.typicode.com/posts', http);
  }

}
Useme Alehosaini
  • 2,998
  • 6
  • 18
  • 26
Seemant Shukla
  • 619
  • 1
  • 7
  • 16

5 Answers5

58

In DataService.ts :

Update the Constructor.

//import { Inject } from '@angular/core';

constructor(@Inject(String) private url: string, private http: Http)

UPDATED (Explanation):

According to https://angular-2-training-book.rangle.io/di/angular2/inject_and_injectable ;

@Inject() is a manual mechanism for letting Angular know that a parameter must be injected.

@Inject decorator is only needed for injecting primitives.

The primitive types are number, string, boolean, bigint, symbol, null, undefined.

The other (alternative) way can be used is:

//import { Inject } from '@angular/core';

@Inject('url') private url: string;
Useme Alehosaini
  • 2,998
  • 6
  • 18
  • 26
  • 4
    Hey @Useme, Thankyou for the suggestion but wants to know any specific reason for injecting String explicitly ? And that too not 'string' but injecting 'String' explicitly ? Also before going through your approach while googling I got to see a solution in which it was asked to remove @Injectable() from DataService.ts, not sure why ! But this also worked for me. :) – Seemant Shukla Mar 30 '20 at 06:54
  • With some kind of explanation: https://stackoverflow.com/a/36418185/2816279 Explanations are useful, for coders. – Pedro Ferreira Jun 06 '20 at 15:26
  • I have a similar problem. I have a class that I need to extend. I also want to inject a service into the child. When I go to add my service it forces me to use super. The parent class has an empty variable. I use both solutions you mentioned above and neither worked. I receive an error: core.js:5980 ERROR NullInjectorError: R3InjectorError(AuthModule)[String -> String -> String -> String]: NullInjectorError: No provider for String! – Kyle Waid Jan 27 '21 at 02:09
25

remove @Injectable() from DataService.ts

samivic
  • 1,216
  • 14
  • 13
  • 1
    Yeah It will will, Have mentioned in my previous ansers. BDW, Thanks :) but can you let me know the reason behind that ! – Seemant Shukla Apr 02 '20 at 05:51
  • 1
    https://stackoverflow.com/questions/39452578/how-should-i-extend-injectable-from-another-injectable-with-many-injections-in-a – samivic Apr 02 '20 at 15:03
11

As I encounter with same error myself, with same notation I believe you've watched 'Mosh' angular course which was belong to version 4.

There were several changes since then, one of them is replacing Http with HttpClient, another one was huge changes in rxjs library, needless to say same happened for services .Now instead of putting your services in providers you use @Injectable method which I think is more cleaner and more related to the service itself.

However if you had close attention to implementation you'll notice that DataService is just a base class and it would never be use as a service itself.It's like a manifest.So, it does not need to be injected or provided in app module. If you can recall 'Mosh' never did provide it in App module.The only service that was provided was PostService. That's why you should remove @Injectable decoration from DataService.

2

Look at the parameter that throws the error, and all uses of the class. This error is commonly thrown when a constructor defines parameters with primitive types such as string, number, boolean, and Object.

  • Use the @Injectable method or @Inject decorator from @angular/core to ensure that the type you are injecting is reified (has a runtime representation).

  • Instead of using

      constructor( private url: string, private http: HttpClient) { }
    
  • Use the below codes.

      import { Component, Inject, } from '@angular/core';
      import { HttpClientModule } from '@angular/common/http';
    
       constructor(@Inject(String) private url: string, private http: HttpClient) 
       { }
    
0

For NX MFE and angular 15 currently it also can be caused if you have a standalone components structure approach.

In this way, you need to insert your service as a global service to global providers in bootstrap.ts of your mfe app file like:

if (environment.production) {
  enableProdMode();
}

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
    provideHttpClient(),
    importProvidersFrom(BrowserAnimationsModule),
    importProvidersFrom(CoreModule),
    YourGloballyInsertedService, // <-- here
  ],
}).catch((err) => console.error(err));
Igor Kurkov
  • 4,318
  • 2
  • 30
  • 31