简体   繁体   中英

Upgraded AngularJS component which requires another upgraded component can't find the parent controller when using Angular content projection

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM