Search code examples
javascriptangularecmascript-6angular5ecmascript-5

Angular 5 : How to inject Router service with ES6/ES5 (without Typescript)


TL;DR

I want to inject the Angular Router service into a component with ES6/ES5 (yes without Typescript :p).


Hello every body,

I recently created an Angular 5 proxy for javascript, because I don't want to use Typescript.

My library is available on github : https://github.com/kevinbalicot/angular-js-proxy.

It's works when you want to create your Component, your Injectable service and your NgModule.

Check out this example on jsFiddle https://jsfiddle.net/kevinbalicot/nynzceb1/

// ng variable contains { compiler, common, core, router, platformBrowserDynamic, platformBrowser } from Angular 5 library

class Service {
    constructor() {}

    toUpperCase(string) {
        return String(string).toUpperCase();
    }
}

// Like @Injectable
Injectable({})(Service);

class HomeComponent {
    constructor(service) {
        this.message = service.toUpperCase('hello');
    }
}

// Like @Component
Component({
      selector: 'home-component',
      providers: [Service],
      template: `
            <h1>{{ message }}</h1>
        `
})(HomeComponent);

class Module {
    constructor() {}
}

// Like @NgModule
NgModule({
      imports: [ng.platformBrowser.BrowserModule],
      declarations: [HomeComponent],
      providers: [],
      bootstrap: [HomeComponent]
})(Module);

ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(Module);

You'll be able to create your Injectable Service and Inject it to your Component.

THE PROBLEM

When I want to inject an Angular service like Router for example, I get an Angular Injector error

Error: Can't resolve all parameters for Router: (?, ?, ?, ?, ?, ?, ?, ?).

Demo on jsFiddle https://jsfiddle.net/kevinbalicot/nynzceb1/4/

// ng variable contains { compiler, common, core, router, platformBrowserDynamic, platformBrowser } from Angular 5 library

class Service {
    constructor() {}

    toUpperCase(string) {
        return String(string).toUpperCase();
    }
}

// Like @Injectable
Injectable({})(Service);

class HomeComponent {
    constructor(service, router) {
        this.message = service.toUpperCase('hello');
    }
}

// Like @Component
Component({
      selector: 'home-component',
      providers: [Service, ng.router.Router],
      template: `
            <h1>{{ message }}</h1>
        `
})(HomeComponent);

class Module {
    constructor() {}
}

// Like @NgModule
NgModule({
      imports: [
            ng.platformBrowser.BrowserModule,
            ng.router.RouterModule
      ],
      declarations: [HomeComponent],
      providers: [],
      bootstrap: [HomeComponent]
})(Module);

ng.platformBrowserDynamic.platformBrowserDynamic().bootstrapModule(Module);

Do I have to inject an Angular service differently than a custom Injectable Service ? (My code here : https://github.com/kevinbalicot/angular-js-proxy/blob/master/src/index.js#L51).

function NgModule(metadata = {}) {
    return function decorator(target) {
        target.annotations = [new core.NgModule(metadata)];
        target.parameters = [];

        if (!!metadata.providers && Array.isArray(metadata.providers)) {
            metadata.providers.forEach(provider => {
                if (!provider.useValue && !provider.useClass) {
                    // Like Service class on example
                    target.parameters.push([new core.Inject(provider)]);
                } else {
                    // Like { provide: LOCALE_ID, useValue: 'en-EN' } provider
                    target.parameters.push([new core.Inject(provider.provide), provider.useValue || provider.useClass]);
                }
            });
        }

        return target;
    }
}

Thanks for reading, any idea is welcome.


EDIT My question it's not the same of Angular 2 dependency injection in ES5 and ES6

Because, dependency injections are not the same between versions 4 and 5 (https://github.com/angular/angular/blob/master/CHANGELOG.md#breaking-changes)


Solution

  • Injectable is for TypeScript only, it uses emitted parameter types to annotate injectable class for DI.

    Classes should be annotated with static parameter property in ES5 and ES6, as shown in this answer:

    class HomeComponent {
        constructor(service, router) {
            this.message = service.toUpperCase('hello');
        }
    }
    HomeComponent.parameters = [
      [new ng.core.Inject(Service)],
      [new ng.core.Inject(ng.router.Route)]
    ];
    

    A problem with this example is that Router should be global provider, it shouldn't be defined in component providers. Here's an example.