Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular-Dependency with IVY partial

in a project I wanted to switch my libs to the ivy partial compilation mode (angular 12). But got now some nasty circular dependency errors:

Error from example

✖ 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

It's clear why there are a cycles but I can't get a solution who to make it work. The component A has component B inside while B has a component A inside. It creates something like a user-definable UI. A component can contain a other set of dynamicly added components, recursive if you like it.

EDIT: Please consider this info from the doc: https://angular.io/errors/NG3003#libraries A "normal" project would work with this circular-dep but a library not! In stackblitz all examples works, even my, because on stackblitz it's not a lib.

The original project is quite large with some cylcles like this. So here is a bare bone example:

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 {
  }
}
like image 610
staxx6 Avatar asked Apr 18 '26 13:04

staxx6


1 Answers

Encounter same issue after migrating onto Angular 12. Was able to resolve circular DI with Ivy enabled with little hack.

My issue was in cicrular DI of chain: ConfirmationModalComponent, which contains dynamical content via OutputHtmlComponent, which supports clickable elements via ClickableLabelDirective, which reuses ActionsService holding business-logic and the letter contains link on ConfirmationModalComponent again to pass in ModalService for open method, which leads to described loops in DI and errors during building lib with turned on Ivy "partail" mode.

Diagram showing state before issue resolved: enter image description here

Diagram showing state after issue resolved: enter image description here

In other words, I used high-level component FormPlayerComponent and inject desired component ConfirmationModalComponent to high-level service ScreenService, which then reused on low-level service ActionService via regular import of that ScreenService service.

Thus will slightly increase memory-usage in runtime, as "link" to ConfirmationModalComponent will be allocated in service's props, but in my case it was acceptable decision as I don't have to rewrite and rearrange whole application to fulfill new Angular requerments - avoid recurcive components, as if it's something unnatural in programming world.

like image 194
Daniil Vysotskiy Avatar answered Apr 29 '26 02:04

Daniil Vysotskiy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!