Calling downgradeInjectable to use new Angular service in an AngularJS module throws TypeError

727 Views Asked by At

Attempting to use an Angular service in an Angular JS module in a hybrid application is giving one of two errors:

  1. Error: [$injector:unpr] Unknown provider: testServiceProvider <- testService - i.e. the service is registered too late for the AngularJS DI
  2. TypeError: Cannot read property 'has' of undefined - i.e. the service is registered before the AngularJS $injector is available, within the call to downgradeInjectable.

depending on where I attempt to register the service on the AngularJS module:

  • Angular App Module, pre-AngularJS-bootstrap : Error 1
  • Angular App Module, post-AngularJS-bootstrap : Error 2
  • Angular Test Service : Error 1
  • AngularJS Shared Module : Error 2

Does anyone know how to solve this or is it an unsupported approach?

Code

Full Repro : StackBlitz


Angular ngUpgrade : downgradeInjectable

export function downgradeInjectable(token: any, downgradedModule: string = ''): Function {
  const factory = function($injector: IInjectorService) {
    const injectorKey = `${INJECTOR_KEY}${downgradedModule}`;
    const injectableName = isFunction(token) ? getTypeName(token) : String(token);
    const attemptedAction = `instantiating injectable '${injectableName}'`;

/*
 * Error 2 is thrown from the following method call, because $injector is undefined
 */

    validateInjectionKey($injector, downgradedModule, injectorKey, attemptedAction);

    try {
      const injector: Injector = $injector.get(injectorKey);
      return injector.get(token);
    } catch (err) {
      throw new Error(`Error while ${attemptedAction}: ${err.message || err}`);
    }
  };
  (factory as any)['$inject'] = [$INJECTOR];

  return factory;
}

Angular : App Module

@NgModule({
  imports: [BrowserModule, CommonModule, UpgradeModule],
  declarations: [AppComponent, BaseComponent],
  providers: [{ provide: "$scope", useExisting: "$rootScope" }]
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) {}

  public ngDoBootstrap(app: ApplicationRef): void {
    setAngularJSGlobal(angular);

    // Defining the downgraded service here gives:
    // `TypeError: Cannot read property 'has' of undefined`

    angular
      .module(SHARED_MODULE_NAME)
      .factory("testService", [downgradeInjectable(TestService)]);

    this.upgrade.bootstrap(document.body, [SHARED_MODULE_NAME], {
      strictDi: false
    });

    // Defining the downgraded service here gives:
    // `Error: [$injector:unpr] Unknown provider: testServiceProvider <- testService`

    app.bootstrap(AppComponent);
  }
}

AngularJS : Shared Module

import {
  downgradeComponent
  /*downgradeInjectable*/
} from "@angular/upgrade/static";

import { BASE_MODULE_NAME, SHARED_MODULE_NAME } from "./constants.ajs";

import * as angular from "angular";

import { AppComponent } from "../app/app.component";
// import { TestService } from "../app/services/test.service";

angular
  .module(SHARED_MODULE_NAME, [BASE_MODULE_NAME])
  .directive("appRoot", downgradeComponent({ component: AppComponent }));

// Defining the downgraded service here gives:
// `TypeError: Cannot read property 'has' of undefined`
//.factory("testService", [downgradeInjectable(TestService)]);

Angular : Test Service

import { Injectable } from "@angular/core";
/*
import { SHARED_MODULE_NAME } from "../../ajs/constants.ajs";
import { downgradeInjectable } from "@angular/upgrade/static";
import * as angular from "angular";
*/

@Injectable({
  providedIn: "root"
})
export class TestService {
  getText() {
    return "New Text From Service";
  }
}

// Defining the downgraded service here gives:
// `Error: [$injector:unpr] Unknown provider: testServiceProvider <- testService`


/*
angular
  .module(SHARED_MODULE_NAME)
  .factory("testService", [downgradeInjectable(TestService)]);
  */
0

There are 0 best solutions below