Search code examples
angularjsangularserviceangular-upgrade

Unable to get AngularJS service working in Angular


I searched a lot of posts and also the official Angular documentation, but I'm not able to get an AngularJS service running in Angular. I finally came to this page https://angular.io/api/upgrade/static/UpgradeModule#examples which seems to explain exactly what I need, but when doing all those steps I'm getting:

ERROR Error: Trying to get the AngularJS injector before it being set.

My impression is that this example is not quite complete. E.g. there is no hint were the (old) AngularJS framework must be loaded. My service looks like angular.module('my-module').service('my-service', ... thus angular needs to be defined, otherwise I'm getting an error. Furthermore many examples assume that the AngularJS code is written in TypeScript. In my case this is not true (just plain Javascript).

Unfortunately with Angular 9 there is an additional issue with the @angular/upgrade module which is not mentioned anywhere and can only be solved by disabling the new Ivy compiler in tsconfig.app.json, otherwise the compiler will throw Error: Error on worker #1: Error: getInternalNameOfClass() called on a non-ES5 class: expected UpgradeComponent to have an inner class declaration:

"angularCompilerOptions": {
    "enableIvy": false
}

I'd really appreciate if somebody could post a complete example on what exactly must be done in order to run an AngularJS service in an Angular component.

UPDATE [6th July 2020]

Here you can find a GitHub repo which you can clone, to reproduce the behavior: https://github.com/berkon/angularjs-service-upgrade-test. I should also mention that I'm using the Electron framework and started based on this repo https://github.com/maximegris/angular-electron but I guess that shouldn't matter in this case.


Solution

  • Finally I got it working! It was really really cumbersome to figure this all out. A lot of things aren't mentioned in most tutorials and even in the official Angular guide there are only code snippets which make it hard for Angular newbies to guess where to put all that stuff. Also the bootstraping is not explained correctly. Furthermore all tutorials assume that the "old" AngularJS code is already written in TypeScript, which makes it even harder to find the right way/order to load/bootstrap/import all that stuff. Finally there seems to be an issue with the @angular/upgrade module in combination with the new Ivy compiler in Angular 9. It throws the error mentioned below. Thus it must be disabled to get things working. A real pain!!!

    So roughly these are the steps:

    • install the angular and @angular/upgrade node modules
    • load all .js modules including AngularJS in the script section of angular.json
    • interrupt the regular Angular bootstrap process by removing the bootstrap section from @NgModule and bootstrap AngularJS via ngDoBootstrap manually. First bootstrap AngularJS, afterwards bootstrap the AppComponent class. This way the service is available at AppComponent initialization. Otherwise you'll get an injection error!
    • Add a new provider in providers [] section to get access to the new service
    • Now the new (upgraded) service can be injected in the constructor of AppComponent

    Its quite a lot of work to perform all steps below manually, but I listed them for reference. Here you can find a GitHub repo where you can clone a working app. Don't be surprised! This repo uses the Electron framework (electronjs.org). But don't worry this doesn't have any influence on my findings: https://github.com/berkon/angularjs-service-upgrade-test

    And here is the step-by-step guide:

    Prerequistes

    • execute npm install angular --save

    • execute npm install @angular/upgrade --save

    • in tsconfig.app.json add "enableIvy": false to angularCompilerOptions to avoid getting:

      Error: getInternalNameOfClass() called on a non-ES5 class: expected UpgradeComponent to have an inner class declaration
      
    • add "node_modules/angular/angular.js" and the Javascript file which contains your AngularJS service (in this case "src/app/angular-js-service.js") to the scripts [] array in angular.json

    app.module.ts

    • add ApplicationRef to the import brackets of @angular/core

    • add import { UpgradeModule } from '@angular/upgrade/static'

    • add UpgradeModule to imports [] array of @NgModule

    • remove bootstrap section completely from @NgModule and replace it with this: entryComponents: [AppComponent]

    • add this to the providers [] array in @NgModule and make sure to replace myService with the correct name of your service:

      { provide: 'myService', useFactory: (i: any) => { return i.get('myService') }, deps: ['$injector'] }
      
    • replace the constructor of AppModule with this:

      constructor ( public upgradeModule: UpgradeModule ) {}
      
    • add this to the AppModule class and make sure to replace ajsAppModule with the name of your AngularJS main app module:

      ngDoBootstrap ( appRef: ApplicationRef ) {
          this.upgradeModule.bootstrap(document.body, ['ajsAppModule'], { strictDi: true } )
          appRef.bootstrap ( AppComponent )
      }
      

    app.component.ts

    • add Inject to the import brackets at @angular/core

    • in the AppComponent class change the constructor to this and make sure to replace myService with the name of your AngularJS service

      constructor ( @Inject('myService') myService: any ) {
          myService.doSomething()
      }