angularangular2-servicesangular2-httpangular2-di

Angular 2 "No provider for String!"


I'm trying to create a generalized data service in Angular 2, and I'm encountering a strange error. Essentially, I'm creating an HTTP service whose methods take in part of the api url so that I can use it for multiple cases. For example, I would like to pass in 'projects/' and join it with 'api/' to get 'api/projects/' which I could then use in the http calls.

My current dataService.ts:

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import 'rxjs/add/operator/map'
import { Observable } from 'rxjs/Observable';
import { Configuration } from '../app.constants';

@Injectable()
export class DataService {

    private actionUrl: string;
    private headers: Headers;

    constructor(private _http: Http, private _configuration: Configuration, public action: string) {

    this.actionUrl = _configuration.ApiUrl

    this.headers = new Headers();
    this.headers.append('Content-Type', 'application/json');
    this.headers.append('Accept', 'application/json');
    }

    public GetAll (action: string): Observable<any> {
        return this._http.get(this.actionUrl + action).map((response: Response) => <any>response.json());
    }

    public GetSingle (action: string, id: number): Observable<Response> {
        return this._http.get(this.actionUrl + action + id).map(res => res.json());
    }

    public Add (action: string, itemToAdd: any): Observable<Response> {
        var toAdd = JSON.stringify(itemToAdd);

        return this._http.post(this.actionUrl + action, toAdd, { headers: this.headers }).map(res => res.json());
    }

    public Update (action: string, id: number, itemToUpdate: any): Observable<Response> {
        return this._http
        .put(this.actionUrl + id, JSON.stringify(itemToUpdate), { headers: this.headers })
        .map(res => res.json());
    }

    public Delete (action: string, id: number): Observable<Response> {
        return this._http.delete(this.actionUrl + id);
    }
}

The component I'm trying to call GetAll with 'projects' as a parameter from:

import { Component, OnInit } from '@angular/core';
import { DataService } from '../services/dataService';

@Component({
    selector: 'project-detail',
    templateUrl: 'app/project/project-detail.component.html'
})
export class ProjectDetailComponent implements OnInit{
    projects: Object[];

    constructor(private _dataService: DataService) {}

    getProjects(): void {
        this._dataService.GetAll('projects').subscribe(projects => this.projects = projects);
    }
    ngOnInit(): void {
        this.getProjects();
    }
}

I do have the service added as a provider in my app.module.ts. One more thing of note is that the component I show here is a child component of a component called HomeComponent, which sits in AppComponent.

This is what HomeComponent looks like:

import { Component, OnInit } from '@angular/core';
import { ProjectDetailComponent } from '../project/project-detail.component';


@Component({
    selector: 'home',
    templateUrl: '<project-detail></project-detail>'
})

export class HomeComponent {}

The error:

EXCEPTION: Uncaught (in promise): Error: Error in      app/home/home.component.html:2:0 caused by: No provider for String!

I know that it's complaining about the string I'm passing into GetCall, but I just don't know why.

Any help would be appreciated!


Solution

  • You can't just inject arbitrary things. Anything you try to inject, needs to be configured to be injectable. In most cases that means just adding the class to the providers list. In the case of a string, it's not that simple. We need to do a few things.

    1. First we need to create a token1
    2. Then configure the string to be injected, using that token.
    3. Then with the same token, @Inject it into the target.

    import { OpaqueToken } from '@angular/core';
    
    // 1. Create token
    export const ACTION = new OpaqueToken('app.action');
    
    // 2. Configure the `providers` (Maybe in the AppModule)
    providers: [
      { provide: ACTION, useValue: 'the value you want'
    ]
    
    import { Inject } from '@angular/core';
    
    export class DataService {
      // 3. Injection target 
      constructor(@Inject(ACTION) action: string) {}
    }
    

    1 - See Dependency Injection Tokens


    UPDATE

    Now, with Angular 4, we should use InjectionToken rather than OpaqueToken