angularangular-hybrid

Angular Upgrade - can't use upgraded component as entry component


I'm using Angular Upgrade module with components upgraded from AngularJS. When I use components inside templates they are working fine but when I try to use them as entry components I receive an error:

MyNgComponent cannot be used as an entry component.

Probably it's because upgraded components are defines as directives. I've also tried to wrap into another component but in this case it gives me an error when tries to create a component instance:

NullInjectorError: No provider for $scope!

$scope is requested by internal UpgradeComponent implementation and it's resolved fine if component is used in a template.

Is it a hybrid mode limitation or is there any way to use it as entry component?

Entry component is needed because I need to instantiate this component from JS and manually attach to non-angular DOM element


Solution

  • Seems to be a limitation of a hybrid mode. In my case it was necessary to dynamically create upgraded components and attach them to DOM. In this case it's possible to use the following workaround:

    1. Create a regular Angular Component as a wrapper for upgraded AngularJS directive so that it's template only contains an upgraded directive
    2. Add this wrapper component to entryComponents module section
    3. Dynamically instantiate this wrapper component using an injector of root hybrid component

    For example, I have a have a dummy AngularJS directive

    angular.module('app').component('myComponent', {
      template: 'dummy content',
      controller: class {},
      controllerAs: 'vm'
    });
    
    @Directive({
      selector: 'my-component'
    })
    export class MyUpgradedDirective extends UpgradeComponent {
      constructor(elementRef: ElementRef, injector: Injector) {
        super('myComponent', elementRef, injector);
      }
    }
    

    Create a wrapper component:

    @Component({
      selector: 'app-nested',
      template: '<my-component></my-component>'
    })
    export class WrapperComponent { }
    

    Add it to entryComponents

    @NgModule({
      imports:      [ BrowserModule, UpgradeModule ],
      declarations: [ AppComponent, NestedComponent, MyUpgradedDirective ],
      entryComponents: [ AppComponent, NestedComponent ] // here
    })
    

    Create a wrapper instance using an injector from root hybrid component:

    function createMyComponent<T>(componentFactoryResolver: ComponentFactoryResolver, injector: Injector /* must be passed from root hybrid component */, component: Type<T>) {
      return componentFactoryResolver
        .resolveComponentFactory(component)
        .create(injector);
    }
    

    Here are some details: https://github.com/angular/angular/issues/27432