Nathan Friend
Nathan Friend

Reputation: 12814

Error when using AngularJS component inside Angular app: "Error: Trying to get the AngularJS injector before it being set."

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.

Upvotes: 8

Views: 8760

Answers (3)

lucaleone
lucaleone

Reputation: 159

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.

Upvotes: 9

Ridae HAMDANI
Ridae HAMDANI

Reputation: 746

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 
})
...

Upvotes: 0

Amin Rahimi
Amin Rahimi

Reputation: 235

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

Upvotes: 0

Related Questions