升级的 AngularJS 组件需要另一个升级的组件,在使用 Angular 内容投影时找不到父控制器

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

我正在尝试在我的应用程序的一个模块上为 ngUpgrade 做一个 PoC,我 运行 遇到 transclusion/content 投影和 AngularJS requires.

假设有一个降级的 Angular 组件定义如下:

@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是升级后的AngularJS组件,需要父控制器:

require: {
  ajsFilterCtrl: '^ajsFilter'
}

common-filter 的正常使用效果非常好,但是当投影 ajs-button 时,例如:

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

抛出此错误:

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

有什么办法解决这个问题吗?我知道我可以重写周围的类,但是很多其他应用程序都在使用它们,我需要能够逐步升级这些应用程序。

工作示例:

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

以上片段来自 index.htmlcommon-filter.component.ts。这是一个(或多或少)基于更复杂组件的最小示例。出于性能原因,我将采用 downgradeModule 方法。

维护者 gkalpak 在我为此发布的 Github 问题中提供了解决此问题的方法: https://github.com/angular/angular/issues/24846#issuecomment-404448494

简而言之,这是不同控制器初始化顺序的问题。 要绕过它,您可以使父控制器 require 可选,然后使用 setTimeout 等待父控制器初始化:

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

可以在此处查看解决方法: https://stackblitz.com/edit/ngupgradestatic-playground-a6zkwb