Angular UpgradeComponent cannot find $scope

1.9k views Asked by At

I have a hybrid angular-cli that roughly follows Victor Savkin's Lazy Loaded AngularJS guide. AngularJS is bootstraped in the constructor of a LazyLoaded Angular module. The main difference between my app and the guide is that I am trying to wrap the <ui-view> directive inside of some Angular components. Because of how my layout is structured the <ui-view> element will not be available when AngularJS is bootstrapped and may be added or removed at any time.

import { Component, Directive, ElementRef, Injector } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';
import * as angular from 'angular';

@Component({
  template: `
  <layout-wrapper>
    <my-toolbar></my-toolbar>
    <layout-contents>
      <ng2-ui-view>
        <h3 class="text-center">AngularJS page not loaded</h3>
      </ng2-ui-view>
    </layout-contents>
  </layout-wrapper>
  `,
})
export class LegacyOutputComponent { }

@Directive({selector: 'ng2-ui-view'})
export class UpgradedUiViewComponent extends UpgradeComponent {
  constructor(ref: ElementRef, inj: Injector) {
    super('uiViewWrapper', ref, inj);
  }
}

export const routerPatchModule = 'arcs.router.patch';

// We need to define a wrapper for ui-view because we can only upgrade
// components with only one definition. uiView cannot be automatically
// upgraded because its definition is too complex
angular.module(routerPatchModule, ['ui.router'])
.component('uiViewWrapper', { template: '<ui-view></ui-view>'})

When I run the code a Error: No provider for $scope! error is thrown. Checking the stack trace I can see that it is thrown in the UpgradeComponent super class. The injector tries to get $scope and

2

There are 2 answers

1
Chic On BEST ANSWER

This setup will not work. AngularJS needs to be able to load in the root of your application in order for the scope to be defined correctly.

A better way to approach this problem is to use the <div ui-view> directive in the root of your application (as in the upgrade guide) and then to downgrade a layout component from Angular into AngularJS to wrap your content.

1
Vizjerai On

Alternative is to let Angular know that it needs to provide the $scope.

import { Injector } from '@angular/core';

// allow $scope to be provided to ng1
export const ScopeProvider = {
  deps: ['$injector'],
  provide: '$scope',
  useFactory: (injector: Injector) => injector.get('$rootScope').$new(),
};

@Directive({
  providers: [ ScopeProvider ],
  selector: 'ng2-ui-view',
})
export class UpgradedUiViewComponent extends UpgradeComponent {
  constructor(ref: ElementRef, inj: Injector) {
    super('uiViewWrapper', ref, inj);
  }
}