1

I am trying to do a PoC for ngUpgrade on one module of my app, and I'm running into an issue with transclusion/content projection along with AngularJS requires.

Let's say there's a downgraded Angular component defined like:

@Component({
  selector: 'common-filter',
  template: `
    <ajs-filter>
      <div>Common Filter</div>
      <ajs-button text="Outside of content projection"></ajs-button>
      <ng-content></ng-content>
    </ajs-filter>
})

export class CommonFilter {}

ajsButton is an upgraded AngularJS component that requires a parent controller:

require: {
  ajsFilterCtrl: '^ajsFilter'
}

A normal use of common-filter works perfectly fine, however when projecting an ajs-button like:

<common-filter>
  <ajs-button text="Inside of content projection"></ajs-button>
</common-filter>

This error is thrown:

Unhandled Promise rejection: [$compile:ctreq] Controller 'ajsFilter', required by directive 'ajsButton', can't be found!

Is there any way around this? I know that I can rewrite the surrounding classes, but many other apps use them and I need to be able to upgrade the apps gradually.

Working Example:

https://stackblitz.com/edit/ngupgradestatic-playground-uvr14t

Snippets above are from index.html and common-filter.component.ts. This is a (more-or-less) minimal example based on much more complicated components. I'm going with the downgradeModule approach for performance reasons.

ach
  • 6,164
  • 1
  • 25
  • 28
  • I'm not sure how else to communicate this, but for other prospective editors of my question -- please research what ngUpgrade is before suggesting incorrect edits. – ach Jul 09 '18 at 17:07

1 Answers1

0

A maintainer, gkalpak, provided a workaround for this issue in the Github issue that I posted for this: https://github.com/angular/angular/issues/24846#issuecomment-404448494

In short, this is an issue with the order of initialization of the different controllers. To get around it, you can make the parent require optional and then use setTimeout to wait until the parent controller is initialized:

this.$onInit = () => {
  this.requireAjsFilterCtrl().then(() => console.log('!', !!this.ajsFilterCtrl));
}

this.requireAjsFilterCtrl = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      this.ajsFilterCtrl = this.ajsFilterCtrl || $element.controller('ajsFilter');
      resolve(this.ajsFilterCtrl);
    });
  });
};

The workaround can be seen in action here: https://stackblitz.com/edit/ngupgradestatic-playground-a6zkwb

ach
  • 6,164
  • 1
  • 25
  • 28