0

I'm trying to get into angular2 using 2.0.0-beta.17. I want to read data from a service but no matter what I do I get an exception:

No provider for LanguageBrowser!

I went through dozens of the same questions here but none of them worked.

This is my service class:

import {Injectable} from "angular2/core";
import {Language} from "../models/language.model";

@Injectable()
export class LanguageService {
    ReadAll(onNext: (json: any) => void) {
        return [
            new Language({ Id: 1, Name: "German" }),
            new Language({ Id: 2, Name: "English" }),
            new Language({ Id: 3, Name: "Dutch" })
        ];
    }
}

Language is a simple typescript class, which is supposed to be retrieved from a webservice call in the end but I'm not that far.

export class Language {
    public Id: number;
    public Name: string;

    constructor(json: any) {
        if (json) {
            this.Id = json.Id || 0;
            this.Name = json.Name || "";
        }
    }
}

The LanguageService is consumed by a LanguageBrowser component

import {Component} from "angular2/core"
import {Language} from "../models/language.model"
import {LanguageDetail} from "./language-detail.component"
import {LanguageCollection} from "./language-collection.component"
import {LanguageService} from "../services/language.service"

@Component({
    selector: "language-browser",
    template: `
        <language-collection [languages]="availableLanguages" (selectionChanged)="onSelectionChanged($event)"></language-collection>
        <language-detail [language]="selectedLanguage"></language-detail>
    `
    providers: [LanguageService],
    directives: [LanguageCollection, LanguageDetail]
})
export class LanguageBrowser {
    private selectedLanguage: Language;
    private availableLanguages: Language[];

    constructor(languageService: LanguageService) {
        languageService.ReadAll(json => this.availableLanguages = json);
    }

    onSelectionChanged(language) {
        this.selectedLanguage = language;
    }
}

LanguageCollection is another component taking a list of languages and emitting an event upon selecting one of the languages in the list. LanguageDetail simply shows the selected language. They're both irrelevant (I assume) so I'll exclude them here.

As another answer suggested I added system-polyfills.js and es6-shim.js as script includes but to no avail. This is the relevant part of my layout:

<script src="~/js/jquery.js"></script>
<script src="~/js/bootstrap.js"></script>
<script src="~/js/system.js"></script>
<script src="~/js/system-polyfills.js"></script>
<script src="~/js/rx.js"></script>
<script src="~/js/typescript.js"></script>
<script src="~/js/es6-shim.js"></script>
<script src="~/js/angular2/angular2-polyfills.js"></script>
<script src="~/js/angular2/angular2.dev.js"></script>
<script src="~/js/angular2/http.dev.js"></script>
<script src="~/js/angular2/router.dev.js"></script>

<script>
        System.config({
            packages: {
                app: {
                    format: 'register',
                    defaultExtension: 'js'
                }
            }
        });
        System.import('app/boot');
</script>

With boot simply looking like this:

import {bootstrap}    from "angular2/platform/browser"
import {AppComponent} from "./app.component"
import "rxjs/add/operator/map";

bootstrap(AppComponent);

And AppComponent like this:

import {Component} from "angular2/core";
import {LanguageBrowser} from "./components/language-browser.component";

@Component({
    selector: "app",
    template: `
        <language-browser></language-browser>
    `,
    directives: [LanguageBrowser]
})

export class AppComponent {
    public LanguageBrowser: LanguageBrowser;

    constructor(languageBrowser: LanguageBrowser) {
        this.LanguageBrowser = languageBrowser;
    }
}

The dependencies in my package.json:

"dependencies": {
    "angular2": "2.0.0-beta.17",
    "bootstrap": "3.3.6",
    "es6-promise": "3.2.1",
    "es6-shim": "0.35.1",
    "jquery": "2.2.4",
    "reflect-metadata": "0.1.2",
    "rxjs": "5.0.0-beta.6",
    "systemjs": "0.19.30",
    "typescript": "1.8.10",
    "zone.js": "0.6.12"
  },
  "devDependencies": {
    "gulp": "3.9.1",
    "del": "2.2.0"
  },
  "peerDependencies": {
    "reflect-metadata": "0.1.2",
    "rxjs": "5.0.0-beta.6"
  }

As npm installation told me angular2 wants peerDependencies of reflect-metadata and rxjs I simply included them as peerDependencies. I also had them as regular dependencies with no change.

Obviously I'm missing something but I'm unable to find out what it might be. Went through several tutorials for angular2 but none of them did the trick.

2 Answers2

3

Use ViewChild:

@Component({
    selector: "app",
    template: `
        <language-browser></language-browser>
    `,
    directives: [LanguageBrowser]
})

export class AppComponent {
    @ViewChild(LanguageBrowser) lb:LanguageBrowser;

    constructor() {}
    ngAfterViewInit() {
        let yey = this.lb.availableLanguages;
    }
}

Also, try to avoid doing anything in the constructor, learn lifecycle hooks and use them instead.

Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • I actually can't see how this should help me with my error. You don't even touch the LanguageBrowser component, which throws the error of lacking the provider. Also I don't need the available languages in my AppComponent, so I'm actually not sure what you're trying to accomplish here. Anyway, I read up on lifecycle hooks as you suggested and also on DI and I now found a working solution, so thanks anyway. – Otto Abnormalverbraucher Jun 20 '16 at 23:33
  • Besides, what is bad about injecting in the constructor? – Otto Abnormalverbraucher Jun 20 '16 at 23:41
  • In constructor child component don't exist yet, see [this plunker](http://plnkr.co/edit/WH6FZfLSejWrYKgJiZaN?p=preview). Also you were trying to inject a component, and that won't work - you inject classes that are decorated with `@Injectable()`, others you import and use typically in lifecycle hooks... – Sasxa Jun 20 '16 at 23:56
  • Well, then I find it funny it actually did work before I was trying to use a service to get my list of languages but instead simply assigned them in the LanguageBrowser's constructor. Besides, any tutorial I found did it this way. As far as I have seen so far, things have changed a lot during the angular2 development process so I assume this may have changed. So ... it doesn't work is not an argument because it does, any other reason? – Otto Abnormalverbraucher Jun 21 '16 at 06:55
1

After Sasxa's suggestion to learn lifecycle hooks and his link provided I was actually able to come up with a solution. I'm pretty sure there are half a dozen simpler ways to do this but other than injecting the LanguageService in the constructor I'm unable to find out how to get hands on the service object.

Anyway, this is what I'm doing now:

import {Component, OnInit, ReflectiveInjector} from "angular2/core"
import {Language} from "../models/language.model"
import {LanguageDetail} from "./language-detail.component"
import {LanguageCollection} from "./language-collection.component"
import {LanguageService} from "../services/language.service"

@Component({
    selector: "language-browser",
    templateUrl: "./app/views/language-browser.html",
    providers: [LanguageService],
    directives: [LanguageCollection, LanguageDetail]
})
export class LanguageBrowser implements OnInit {
    private selectedLanguage: Language;
    private availableLanguages: Language[];

    ngOnInit() {
        var injector: ReflectiveInjector = ReflectiveInjector.resolveAndCreate([LanguageService]);
        var languageService: LanguageService = injector.get(LanguageService);

        this.availableLanguages = languageService.ReadAll();
    }

    onSelectionChanged(language) {
        this.selectedLanguage = language;
    }
}

If anyone has an explanation as to why the error initially occurred I'd be glad to hear it.