Search code examples
javascriptangularjstypescriptdependency-injectionhybrid

Angular DI in a hybrid boostrapped application


I have a sizable AngularJS (aka Angular 1) application that is written in Typescript. I've decided that new features should be written in Angular (aka Angular 4) and have taken the steps to boostrap the app in "hybrid mode".

This seems to work (all the AngularJS bits work fine) and I've added a single Angular component and that renders fine.

The problem comes when I try to add an Angular service to my Angular (so all Angular 4 so far). Angular fails to work, spitting out the seemlying ubiquitous Can't resolve all parameters for... error.

However, if I mark the type in the components constructor with @Inject then it works ok.

All the documentation points to the fact I should not need to use @Inject routinely, so why is this failing?

My medium term need to is inject AngularJS services into the Angular components and services, and this situation isn't filling me with confidence.

app.module.ts:

// All AngualarJS definitions appear before this section.

@NgModule({
    imports: [
        BrowserModule,
        UpgradeModule,
    ],
    declarations: [
        AppComponent,
        OrgSummaryComponent
    ],
    providers: [
        OrgDetailsService,
    ],
    bootstrap: [
        AppComponent,
    ],
})
export class AppModule {
    ngDoBootstrap() {
        // Does nothing by design.
        // This is to facilitate "hybrid bootstrapping"
    }
}

platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
    const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
    upgrade.bootstrap(document.body, [AppModuleName], {strictDi: true});
});

org-details.service.ts:

import {Injectable} from "@angular/core";

export class OrgDetails {
    public orgName: string;
}

@Injectable()
export class OrgDetailsService {

    getOrgDetails () : OrgDetails {
        const od = new OrgDetails();
        od.orgName = "Some Org Name from a REST service";
        return od;
    }
}

org-summary.component.ts:

import {Component, Inject, OnInit} from "@angular/core";
import {OrgDetailsService} from "./org-details.service";

@Component ({
    selector: "orgsummary",
    template: "<h2>{{OrgName}}</h2>",
})
export class OrgSummaryComponent implements OnInit {
    constructor (
        /*@Inject(OrgDetailsService)*/ private orgs: OrgDetailsService,  // Only works if I uncomment the @Inject
    ) {

    }

    public OrgName: string;

    ngOnInit(): void {
        this.OrgName = `${this.orgs.getOrgDetails().orgName}`;
    }
}

tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "noImplicitAny": true,
    "declaration": true,
    "experimentalDecorators": true
  },
  "exclude": [
    "node_modules",
    "**/*.spec.ts"
  ],
  "include" : [
    "app/**/*.ts"
  ]
}

Many thanks, Jeff


Solution

  • Just add

    "emitDecoratorMetadata": true
    

    to your tsconfig.json