0

I have went through the below SO Question for a solution:

Angular: 7.2.1 ES6 class ReferenceError : Cannot access 'X' before initialization

Firstly, I'm getting the below warnings on my VS Code terminal:

WARNING in Circular dependency detected: src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-details\fixed-deposits-details.component.ts -> src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-form\fixed-deposits-form.component.ts -> src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-details\fixed-deposits-details.component.ts

WARNING in Circular dependency detected: src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-form\fixed-deposits-form.component.ts -> src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-details\fixed-deposits-details.component.ts -> src\app\modules\asset-classes\fixed-income\fixed-deposits\components\fixed-deposits-form\fixed-deposits-form.component.ts

WARNING in Circular dependency detected: src\app\modules\shared\components\common-dialog\common-dialog.component.ts -> src\app\view-manager.service.ts -> src\app\modules\shared\components\common-dialog\common-dialog.component.ts

WARNING in Circular dependency detected: src\app\view-manager.service.ts -> src\app\modules\shared\components\common-dialog\common-dialog.component.ts -> src\app\view-manager.service.ts

Now, that obviously because of the below code in my respective components:

fixed-deposits-form.component.ts

On Save click >>

this.viewService.loadSideNavWithCallback(
    FixedDepositsDetailsComponent,
    (compRef: ComponentRef<any>) => {
    compRef.instance.fixedDeposit = fixedDepositEntity;
});

fixed-deposits-details.component.ts

On Edit click >>

this.viewService.loadSideNavWithCallback(
  FixedDepositsFormComponent,
  (compRef: ComponentRef<any>) => {
    compRef.instance.fixedDeposit = this.fixedDeposit;
  });

The viewService is basically my ViewManagerService that helps achieved loose coupling between angular components and also serves for view navigation. Since the FixedDepositsFormsComponent & FixedDepositsDetailsComponent open in a side-nav (which is present on the AppComponent), the ViewManagerService does the job of load those components.

Although it is pretty obvious that I have a circular dependency, I don't know how to fix it.

Also I'm getting the below error in my browser console:

Uncaught ReferenceError: Cannot access 'FixedDeposit' before initialization at Module.FixedDeposit (main.js:4274) at Module../src/app/modules/asset-classes/fixed-income/fixed-deposits/components/fixed-deposits-details/fixed-deposits-details.component.ts (fixed-deposits-details.component.ts:21)

Now the line no.21 simply points to my @Input property on the details component:

export class FixedDepositsDetailsComponent implements OnInit {

  @Input() fixedDeposit: FixedDeposit;

If I remove the loadSideNavWithCallback code (called on save click) from my FixedDepositsFormComponent, there's no error. But I cannot do away with it since I need to have that code to load the details component.

Appreciate any help in advance.

Lucifer
  • 2,317
  • 9
  • 43
  • 67
  • 1
    I had a similar issue and I spent hours finding out the cyclic dependency. Finally, I found this tool which help me to find it. github.com/acrazing/dpdm#readme – Tharanga Hewavithana Oct 04 '21 at 12:01

2 Answers2

2

Cyclic dependencies are bad because it makes it impossible for webpack to initialize modules in dependency order. Normally webpack ensures that whenever a module's export is imported elsewhere, the exporting module executes before the importing one, thereby ensuring that all values are defined and initialized before they are imported elsewhere.

However, if circular dependencies exist, no such initialization order exists, making it possible that a value is imported before it is defined, which causes the import to return undefined. Because this is surprising and can be hard to debug, Angular CLI emits a warning if circular dependencies are detected. And well it did, because it hints how your FixedDeposit came to be used before its initialization.

It is therefore a good idea to heed the CLI's warning and avoid creating circular dependencies in your code.

The viewService is basically my ViewManagerService that helps achieved loose coupling between angular components and also serves for view navigation

As we have seen, your "loose" coupling is still too tight. Moreover, using a stateful service for navigation bypasses the angular router, making it impossible for users to bookmark individual views, and causes live reloads during development to fall back to a different view.

For all of these reasons, navigation should be performed using the angular router rather than a stateful service. Then, a component can navigate to a different view without knowing the component responsible for this view, and thus without creating a circular dependency among component classes. Further information on how to configure and use the angular router is available in the official angular tutorial.

meriton
  • 68,356
  • 14
  • 108
  • 175
  • I do use router navigation. But in my case, I'm not entirely changing the view in the router-outlet. For ex. my FixedDepositsComponent serves as a dashboard for fixed deposits. If user wishes to see details of a particular deposit or add/edit a deposit, then I load the FixedDepositsDetails & FixedDepositsForm components. Besides the my app-component also has a which I use to load any additional mini-component(instead of popping them). And I created the ViewManagerService which will 'dynamically' create the requested component and load them in the . – Lucifer Jan 03 '21 at 06:36
  • 1
    Using the router does not imply the entire view needs to be changed. With hierarchical routes, you can nest router-outlets. In your case, the AppComponent's router-outlet would contain the FixedDepositsComponent, whose router-outlet contains either the FixedDeposistsDetails or FixedDepositsForm depending on the current url. – meriton Jan 03 '21 at 13:33
0

To avoid circular dependencies I typically move action code (edit/save etc) into common service and keep the component strictly to display elements in html. i.e move loadSideNavWithCallback from FixedDepositsFormComponent to a common service.

A good thumb-rule which has worked for me is to avoid importing a component into another component / service, and use a service instead.

Caroline D.
  • 119
  • 3
  • I already have the common service - the viewService (ViewManagerService), and loadSideNavWithCallback() is a method of that service. But the thing is that the method takes a component type as a parameter. So every component that wishes to load another component in the side-nav, must pass the component type as a parameter. public loadSideNavWithCallback(component: Type, inputInitializerCallback?: ComponentInputInitializer) – Lucifer Jan 03 '21 at 06:25
  • 1
    Seems like you are dynamically loading the component in the ViewManagerService /loadSideNavWithCallback.... in my view this approach is complexity and probably difficult to manage state also.. as @merton suggested routers will be a cleaner approach ... you can use multiple router-outlets in your scenario --refer to https://medium.com/angular-in-depth/angular-router-series-secondary-outlets-primer-139206595e2 – Caroline D. Jan 03 '21 at 19:02