【问题标题】:Circular-Dependency with IVY partialIVY 部分的循环依赖
【发布时间】:2021-11-22 17:41:38
【问题描述】:

在一个项目中,我想将我的库切换到 ivy 部分编译模式(角度 12)。但是现在出现了一些令人讨厌的循环依赖错误:

错误 来自示例

✖ Compiling with Angular sources in Ivy partial compilation mode.
An unhandled exception occurred: projects/circ/src/lib/tab-container/tab-container.component.ts:4:1 - error NG3003: One or more import cycles would need to be created to compile this component, which is not supported by the current compiler configuration.

  4 @Component({
    ~~~~~~~~~~~~
  5   selector: 'my-tab-container',
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
 42   }
    ~~~
 43 }
    ~

  projects/circ/src/lib/container/container.component.ts:4:1
      4 @Component({
        ~~~~~~~~~~~~
      5   selector: 'my-container',
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ...
     41   }
        ~~~
     42 }
        ~
    The component 'ContainerComponent' is used in the template but importing it would create a cycle: C:/projects/ng-test-projects/circular-dep-lib/projects/circ/src/lib/tab-container/tab-container.component.ts -> C:/projects/ng-test-projects/circular-dep-lib/projects/circ/src/lib/container/container.component.ts -> C:/projects/ng-test-projects/circular-dep-lib/projects/circ/src/lib/tab-container/tab-container.component.ts

很清楚为什么会有一个循环,但我无法找到一个解决方案来让它工作。组件 A 内部有组件 B,而 B 内部有组件 A。它创建了类似于用户可定义 UI 的东西。一个组件可以包含另一组动态添加的组件,如果你喜欢它是递归的。

编辑:请考虑文档中的此信息:https://angular.io/errors/NG3003#libraries“正常”项目可以使用此循环-dep,但库不能!在 stackblitz 中所有示例都有效,甚至是我的,因为在 stackblitz 上它不是一个库。

原始项目相当大,有一些这样的循环。所以这是一个简单的例子:

container.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { UiItemLike } from '../ui-item-like';

@Component({
  selector: 'my-container',
  template: `
    <h2>Container</h2>
    <ng-container *ngFor="let item of uiItems">

      <!-- Container -->
      <!-- This recursion is working! The component itself is not "importing" from an other file -->
      <ng-container *ngSwitchCase="item.type === 'Container'">
        <my-container [uiItems]="item.uiItems"></my-container>
      </ng-container>

      <!-- Tab-Container -->
      <ng-container *ngSwitchCase="item.type === 'TabContainer'">
        <!-- Thats the circular dependency -->
        <my-tab-container [uiItems]="item.uiItems"></my-tab-container>
      </ng-container>

      <!-- Button -->
      <ng-container *ngSwitchCase="item.type === 'Button'">
        <my-button></my-button>
      </ng-container>

    </ng-container>
  `,
  styles: [``]
})
export class ContainerComponent implements OnInit, UiItemLike {
  @Input() uiItems: UiItemLike[];
  readonly type: "Container";

  constructor() {}
  ngOnInit(): void {}
}

tab-container.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { UiItemLike } from '../ui-item-like';

@Component({
  selector: 'my-tab-container',
  template: `
    <h2>TabContainer</h2>
    <div>
      <h3>Fake tab 1</h3>
      <!-- Can contain more items -->
      <!-- Thats the circular dependency -->
      <my-container [uiItems]="uiItems.uiItems"></my-container>
    </div>
    
    <div>
      <h3>Fake tab 2</h3>
      <!-- ... -->
    </div>
  `,
  styles: [``]
})
export class TabContainerComponent implements OnInit, UiItemLike {
  @Input() uiItems: UiItemLike[];
  readonly type: "TabContainer";

  constructor() { }
  ngOnInit(): void {
  }
}

【问题讨论】:

标签: angular circular-dependency angular-library angular12 angular-ivy


【解决方案1】:

递归组件调用可以通过使用ng-templatehere you can see a working stackblitz sample来实现。以下是主要要求:

父组件:
initialData = [
  { content: 'hello2' },
  { content: 'hello3' },
  {
    content: 'hello4',
    recursiveData: [
      { content: 'hello4.1' },
      { content: 'hello4.2' },
      {
        content: 'hello4.3',
        recursiveData: [
          { content: 'hello4.3.1' },
          { content: 'hello4.3.1' }
        ]
      }
    ]
  }
];
<!-- initial call -->
<app-my-component [recursiveData]="initialData || []">helloMain</app-my-component>
递归组件:

你可以看到组件&lt;app-my-component/&gt;的递归部分在第8行被调用:

<!-- app-my-component html-template -->
<ng-template #myComponentTemplate>
  <div>
    ... some content ...
    <ng-content></ng-content>
  </div>
  <div *ngFor="let d of recursiveData">
    <!-- recursive call -->
    <app-my-component [recursiveData]="d.recursiveData || []">{{d.content || ''}}</app-my-component>
  </div>
</ng-template>
<ng-container *ngTemplateOutlet='myComponentTemplate'></ng-container>

这样做的原因是 Angular 为每个 ng-template 创建了单独的实例。我之前也遇到过类似的问题,在this other question解决了。

【讨论】:

  • 您只在同一个文件中对第一个组件 -> 本身进行了递归。即使没有模板,这也可以正常工作,因为没有导致此错误的导入。对于“工作”的 stackblitz 示例,我编辑了我的问题。
  • 谢谢,我没有注意到实际的问题。但是我仍然认为您的问题可以通过使用TemplateRef 来解决。而不是绘制嵌套组件。您可以直接渲染模板。这样你就可以完全摆脱第二个组件的导入。这意味着您的整个定义需要在您的头部组件中,而不是沿着它们分布。我会试着举个例子。
  • 举个例子就好了。不知道我是否完全理解你。
  • 这种方法对我不起作用,它不能解决具有圆形组件时出现 NG3003 错误的问题
【解决方案2】:

ng-container 元素上使用*ngIf 而不是*ngSwitchCase 可以解决您的问题。

而且您的代码中还有很多逻辑问题。

我提供了一个stackblits,它适用于您为这两个组件提供的任何圆形组件结构。

【讨论】:

  • 遗憾的是,这并没有什么区别。在 stackblitz 中它可以工作,因为它不是一个库。此外,我在文档angular.io/errors/NG3003#libraries 中发现了这一点,我将编辑我的帖子以指出更多。
  • 我也编辑了您使用的模型。你也用过那个模型吗?我建议您生成一个新项目并在其中使用我的代码来查看它是否在本地工作@staxx6
  • 你到底改变了什么?如果 在 TabContainerComponent 内部或其他方式将不起作用。
  • 我是说你的模型(uiItems 类)有一些逻辑问题,你应该用我在 stackblits 中提供的代码和模型替换你的代码和模型。
  • 该模型将永远无法工作。尝试多少都没关系。
【解决方案3】:

在迁移到 Angular 12 后遇到同样的问题。能够在启用 Ivy 的情况下解决循环 DI 问题。

我的问题在于链的循环 DI:ConfirmationModalComponent,其中包含通过 OutputHtmlComponent 的动态内容,它通过 ClickableLabelDirective 支持可点击元素,它重用了 ActionsService 持有业务逻辑,并且信中包含 @ 上的链接987654329@ 再次传入 ModalService 用于 open 方法,这会导致 DI 中描述的循环以及在打开 Ivy“parttail”模式的情况下构建 lib 期间的错误。

显示问题解决前状态的图表:

问题解决后的状态图:

换句话说,我使用高级组件FormPlayerComponent 并将所需组件ConfirmationModalComponent 注入高级服务ScreenService,然后通过定期导入该@987654335 在低级服务ActionService 上重用@服务。

因此会稍微增加运行时的内存使用量,因为到 ConfirmationModalComponent 的“链接”将在服务的道具中分配,但在我的情况下,这是可以接受的决定,因为我不必重写和重新排列整个应用程序来实现新的 Angular请求 - 避免递归组件,就好像它在编程世界中是不自然的一样。

【讨论】:

    猜你喜欢
    • 2019-06-30
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 2014-05-13
    • 1970-01-01
    • 2010-09-12
    相关资源
    最近更新 更多