With angular 5.0 the upgrade module now has the option of using downgradeModule which runs angularjs outside of the angular zone. While experimenting with this I have run into a problem with using downgradeInjectable.
I am receiving the error:
Uncaught Error: Trying to get the Angular injector before bootstrapping an Angular module.
Bootstrapping angular in angular js works fine
import 'zone.js/dist/zone.js';
import * as angular from 'angular';
/**
* Angular bootstrapping
*/
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { decorateModuleRef } from 'src/environment';
import { AppModule } from 'src/app/app.module';
import { downgradeModule } from '@angular/upgrade/static';
export const bootstrapFn = ( extraProviders ) => {
const platformRef = platformBrowserDynamic( extraProviders );
return platformRef
.bootstrapModule( AppModule )
.then( decorateModuleRef );
};
angular.module( 'app.bootstrap', [
downgradeModule( bootstrapFn ),
] );
However...
Since the bootstrapping takes place after angularjs has been initialized I can no longer get the downgrade injectable working.
Service to be downgraded
import { Injectable, Inject, OnInit } from '@angular/core';
@Injectable()
export class MobileService implements OnInit{
constructor(
@Inject( 'angularjsDependency1' ) public angularjsDependency1 : any,
@Inject( 'angularjsDependency2' ) public angularjsDependency2 : any,
) {}
}
Downgrade injectable attempt
import * as angular from 'angular';
import { downgradeInjectable } from '@angular/upgrade/static';
import { MyService } from 'src/services/myService/myService';
export const myServiceDowngraded = angular.module( 'services.mobileService', [
angularjsDependency1,
angularjsDependency2,
] )
.factory(
'mobileService',
downgradeInjectable( MyService ),
).name;
When "downgradeInjectable( MyService ) runs the angular injector is not available yet since angular hasn't been bootstrapped. Hence the error:
Uncaught Error: Trying to get the Angular injector before bootstrapping an Angular module.
Does anyone have an idea how I might fix this?
This was pointed out to me in an angular github thread.
https://github.com/angular/angular/issues/16491#issuecomment-343021511
I had the same issue and it sucked up several hours before finding this. My workaround was to create a ServiceBootstrapComponent that does nothing but injects all the services that we need to downgrade.
I then downgrade that component, mark it as en entry in @NgModule and add it to index.html.
Works for me.
I was getting the same error in our hybrid app. We are using the following versions:
As mentioned in this answer, I also used a dummy component called
<ng2-bootstrap>
to force boostrapping of Angular. And then, I created an AngularJS service which checks if Angular has been bootstrapped:Ng2BootstrapDetectionService
can be used like below:You can find more details about this solution at the end of this blog post.
I had the same issue, and the reasons are explained in the above answer.
I fixed this by dynamically injecting the downgraded angular service using $injector.
Steps
Register your downgraded service to angularjs module
Inject
$injector
to your controller and use this to get the downgraded serviceAnswers in this thread helped me find a solution, but none contains the holy grail:
service-boostrap
component aside the app's code does not work, because Angular is loaded asynchronously, unlike AngularJS. This gives the same errorTrying to get the Angular injector before bootstrapping an Angular module.
service-bootstrap
component wrapping the AngularJS code kind of worked, but then I experienced issues with change detection inside Angular composants, as described in this issue on github.@angular/upgrade
source code to change afalse
totrue
to force components to be created in the Zone. But in this case it seems to cause performance issues (it seemed to launch ngZone's code multiple times on user events)service-bootstrap
To acheive this, I created a slightly modified
service-bootstrap
component:Declared this component as
entryComponent
in the Angular module and calleddowngradeComponent
to register it in AngularJS:Then (and the magic happens here), I created a new AngularJS component:
Then, my index.html only referenced this component
With this approach, I'm not nesting AngularJS components inside Angular components (which breaks change detection in Angular components), and still I ensure that a first Angular component is loaded before accessing the Angular providers.
Note: The answer below follows the convention of calling angular 1.x as angularjs and all angular 2+ versions as simply angular.
Expanding on JGoodgive's answer above, basically, if you're using
downgradeModule
, then angular module is bootstrapped lazily by angularjs when it needs to render the first angular component. Until then, since the angular module isn't initialised, if you are accessing any angular services inside angularjs usingdowngradeInjectable
, those services aren't available too.The workaround is to force bootstrapping of the angular module as early as possible. For this, a simple component is needed:
This component doesn't do anything. Now, we declare this component in the top level angular module.
Next, we also need to add a downgraded version of this component to angularjs module. (I added this to the top level angularjs module I had)
Finally, we throw in this component in our index.html.
When angularjs finds that component in the markup, it needs to initialise angular module to be able to render that component. The intended side effect of this is that the providers etc. also get initialised and are available to be used with
downgradeInjectable
, which can be used normally.