8

I'm working through Angular's upgrade guide to learn how to embed AngularJS components in an Angular app. I've created a bare-bones Angular app using the Angular CLI and added a simple AngularJS module as a dependency.

When I run ng serve, the application compiles with no errors. However, at runtime, I get this message in the console:

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

What is causing this error, and how can I avoid it? I haven't deviated from the steps detailed in the upgrade guide.

Here's how I'm upgrading my AngularJS component inside my Angular app:

// example.directive.ts
import { Directive, ElementRef, Injector } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';

// this is the npm module that contains the AngularJS component
import { MyComponent } from '@my-company/module-test';

@Directive({
    selector: 'my-upgraded-component'
})
export class ExampleDirective extends UpgradeComponent {
    constructor(elementRef: ElementRef, injector: Injector) {

        // the .injectionName property is the component's selector
        // string; "my-component" in this case.
        super(MyComponent.injectionName, elementRef, injector);
    }
}

And here's my app.module.ts:

// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { UpgradeModule } from '@angular/upgrade/static';
import { ExampleDirective } from './example.directive';
import { myModuleName } from '@my-company/module-test';

@NgModule({
    declarations: [AppComponent, ExampleDirective],
    imports: [BrowserModule, AppRoutingModule, UpgradeModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {
    constructor(private upgrade: UpgradeModule) {}
    ngDoBootstrap() {
        this.upgrade.bootstrap(document.body, [myModuleName], {
            strictDi: true
        });
    }
}

I'm using Angular 5.2.0.

6
  • Why is the import statement different for each file when referencing @my-company/module-test? It also doesn't look like you do anything with the imported MyComponent in the first file. Commented Apr 2, 2018 at 16:17
  • @Pop-A-Stash - I was simplifying the example a bit, but I've updated my question to show why I import the component; it's so that I can reference the component's selector string which is stored as a property on the component class. The two files import different things from @my-company/module-test because the module exports multiple named exports. Commented Apr 2, 2018 at 16:26
  • 1
    I've opened an issue on Angular's GitHub here: github.com/angular/angular/issues/23141 Commented Apr 3, 2018 at 13:29
  • 1
    i have same issue stackblitz.com/edit/angular-pyvy53 Commented Apr 6, 2018 at 6:49
  • I got this error when I imported the UpgradeModule in more than one NgModule. Once I removed that, it went away. Commented Aug 23, 2019 at 21:47

3 Answers 3

9

I faced the same issue, and finally solved it. There are some steps to follow before bootstrap an hybrid Angular/angularjs application.

  1. Install the UpgradeModule npm install @angular/upgrade

  2. Wrap your "CompanyModule" (the module where all your company components are registered) into a new angularjs module (for instance: Ng1Shared). If you not have a module for your company components, it must be created. Than downgrade AppComponent as shown below.

    const MyCompanyModule = angular
       .module('MyCompanyModule', [])
       .component('myComponent', MyComponent)
       .name;
    
    
    const Ng1Shared = angular
       .module('Ng1Shared', [MyCompanyModule])
       .directive('appRoot', downgradeComponent({ component: AppComponent }))
       .name; 
    
  3. Configure AppModule with basic imports (BrowserModule, CommonModule, UpgradeModule). Provide the angularjs' Injector to Angular; declare an "entryComponent" and remove the default bootstrap for AppComponent.

    @NgModule({
      imports: [BrowserModule, CommonModule, UpgradeModule], 
      declarations: [AppComponent], 
      providers: [{provide: '$scope', useExisting: '$rootScope'}], // REQUIRED
      entryComponents: [AppComponent], // ADD AN ENTRY COMPONENT 
      // bootstrap: [AppComponent] MUST BE REMOVED
    })
    
  4. Set angularjs globally with a function provided by UpgradeModule itself and manually bootstrap Angular with DoBootstrap method provided by @angular/core.

    export class AppModule implements DoBootstrap { 
      constructor(private upgrade: UpgradeModule) { }
    
      public ngDoBootstrap(app: any): void {
        setAngularJSGlobal(angular);
        this.upgrade.bootstrap(document.body, [Ng1Shared], { strictDi: false });
        app.bootstrap(AppComponent);
      }
    }
    
  5. Create a wrapper directive for every angularjs component and add it to AppModule's declaration array.

    @Directive({
        selector: 'my-component'
    })
    export class MyComponentWrapper extends UpgradeComponent {
        @Input() title: string;
    
        constructor(elementRef: ElementRef, injector: Injector) {
          super('myComponent', elementRef, injector);
        }
    }
    

I wrote a simple example available on stackblitz. For example purposes I added angularjs MyCompanyModule to another angularjs module, called Ng1Module. As you can see also property binding between angularjs and angular component works fine.

I hope it can be useful.

Sign up to request clarification or add additional context in comments.

Comments

0

https://github.com/angular/angular/issues/23141#issuecomment-379493753

you cannot directly bootstrap an Angular component that contains upgraded components before bootstrapping AngularJS. Instead, you can downgrade AppComponent and let it be bootstrapped as part of the AngularJS part of the app:

https://stackblitz.com/edit/angular-djb5bu?file=app%2Fapp.module.ts

Comments

0

try to add an entryComponents to your AppModule like this :

...
@NgModule({
    declarations: [AppComponent, ExampleDirective],
    imports: [BrowserModule, AppRoutingModule, UpgradeModule],
    entryComponents: [
          AppComponent // Don't forget this!!!
    ],
    providers: [],
    // bootstrap: [AppComponent] // Delete or comment this line 
})
...

1 Comment

With Ivy engine, this is no longer needed angular.io/guide/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.