Dependency Injection in @Page annotations

I am attempting to inject Http from Angular2/Http into a Page element, and am not seeing a direct way to do this.

I browsed through the source for the Page element and noticed it does not accept a providers array like it would for @Component or @App. It also appears that the bootstrap is being done well away from the @App component where I would expect it, which is limiting my ability to inject as well.

I have attempted to inject in @App via:

@App({ templateUrl: 'app/app.html', providers: [HTTP_PROVIDERS] })

and attepted to call in in my GettingStarted page via:
export class GettingStartedPage { constructor(nav: NavController, @Inject(Http)http: Http) {

This yields the following error:

EXCEPTION: Cannot resolve all parameters for GettingStartedPage(NavController, @Inject(undefined)). Make sure they all have valid type or annotations.

Considering that the @App annotation signifies the root component of the application, the Http service should have been availible in the component. I have confirmed that the injection on the application annotation does provide injection in the application class.

I also attempted to utilize both an @Page and @Component, but this breaks some of the ionic component selectors for some reason.

Is there another way in which I should be able to inject the Http provider into a component with an @Page annotation, or a more appropriate way to handle this?

I found a way to do this via Injector.resolveAndCreate. If there is there a cleaner way than executing this in the constructor?

var injector = Injector.resolveAndCreate([ HTTP_PROVIDERS]); var http = injector.get(Http);

Don’t add anything to App.

providers: [HTTP_PROVIDERS]

is not needed.


Also, you might want to keep the controllers lean and use http calls and such in Services rather than the page controller directly:

import {Injectable} from 'angular2/angular2';
import {Http, Headers} from 'angular2/http';

@Injectable()
export class DBClientService {
    constructor(http: Http) {
        this.http = http;
    }

    read(id){
        this.http.get(...

For a Page, this not any different – things can be injected like this (optimally you’d inject that DBClientService to the Page controller). But here’s how to inject http to the controller:

import {IonicApp, Page, NavController} from 'ionic/ionic';
import {Http, Headers} from 'angular2/http';

@Page({
    templateUrl: 'app/test/test.html',
})
export class TestPage{

    constructor(app: IonicApp, nav: NavController, http: Http) {
        this.nav = nav;
        this.app = app;
        this.http = http;
        // Optionally, set headers like this
        this.headers = new Headers();
        this.headers.append('Content-type', 'text/plain');
    }

The overall intention was for this to be a service, but I was attempting to work backwards as I was having problems with the http injection. I tested the suggestion for the Page, which seems to work (I’m rather surprised I didn’t try that earlier), but the service is still failing as suggested above. For the sake of being thorough, here is the import and constructor block of my service:

import {Injector, Injectable} from 'angular2/angular2'
import {Http, HTTP_PROVIDERS, URLSearchParams, Headers} from 'angular2/http'
import {OauthBase} from "./OauthBase";
import IOauthObject from "./IOauthObject";
import Observable from "../../../../node_modules/@reactivex/rxjs/src/Observable";
import ErrorObservable from "../../../../node_modules/@reactivex/rxjs/src/observables/ErrorObservable";
import * as config from '../../config'
import * as jsSHA from "../../../../bower_components/jsSHA"

export {Twitter}

@Injectable()
class Twitter extends OauthBase {
//region Private Variables
private static _http:Http;
private static _redirect_uri: string;
private _oauthObject: IOauthObject;
private _options: {};
//endregion


//region Singleton Generators
private static _instance = new Twitter();

constructor(http: Http) {
    Twitter._http = http;

   if (Twitter._instance) {
        throw new Error("Error: Instantiation failed. Use GetInstance() instead.")
    }
    Twitter._instance = this;
    Twitter._redirect_uri = "http://localhost/callback";

    super();
}

With this implementation the http property is undefined within the constructor. If I add the following to the constructor, it is defined, but it feels sloppy. Do you have any suggestions about what might be going wrong?

    var injector = Injector.resolveAndCreate([
        HTTP_PROVIDERS]);
    Twitter._http = injector.get(Http);

I tested with Ionic alpha 37, using the following code:

app.js

import {App, IonicApp, Platform} from 'ionic/ionic';

import './app.scss';

import {Twitter} from './twitter/twitter';

// Do add your own services here as providers, but not Http
@App({
    templateUrl: 'app/app.html',
    providers: [Twitter]
})
class MyApp {
    constructor(app: IonicApp, platform: Platform, twitter: Twitter) {
        this.app = app;
        this.platform = platform;
        this.twitter = twitter;
    }
}

twitter/twitter.js

import {Injectable} from 'angular2/angular2'
import {Http} from 'angular2/http'

@Injectable()
export class Twitter {
    constructor(http: Http) {
        this._http = http;
        console.log('all OK', this._http);
    }
}

And this works OK!

Maybe if you test this minimal code first, and start adding things to the service one by one to see what breaks it?


Sidenote:
The singleton generator; is it really needed? If you mark the Twitter class as a provider in the app.js under @App, Angular should take care of instantiating the single instance - then just injecting it in all places as needed. Or maybe I am missing something / some things you need for other code using the class?