Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular dependency in recursive objects (linked by calling static methods) in Angular

It is possible to create tree / recursive objects in separate files and avoid Circular dependency detected warning using Angular CLI if I call methods from classes recursive?

(Tested in angular 7) I understand that this is a problem in dependency injection (article), but what about the objects using as constructor for data from external api or for internal purposes? And if it is not good idea anyway, how do you solve it? (I suppose the answer for this 'subquestion' is a change of model, then how should I modify my model?) I search for solution but the closest question did not help me apply the same approach to my situation.

I have Emails and Attachments (emails with attachments - and the entire Email with attachments can also be an attachment, Attachment is separated object with attributes and content can be File or Email, but here, for simplicity, suppose the attachment is always one Email or null).
Each class has static method for create instance from JSON object (I get JSON from API, then I will create instance of object representing API values by calling static fromJson method on top object):

file: app/model/email.ts (part of code)

import { Attachment } from './attachment';

export class Email {
  // some attributes
  public attachment?: Attachment;

  public static fromJson(js: JSON): Email {
    const e = new Email();
    // fill some attributes here
    if (js['attachment']) { e.attachment = Attachment.fromJson(js['attachment']); }
    return e;
  }
}

file: app/model/attachment.ts (part of code)

import { Email } from './email';
export class Attachment {
  // some attributes
  public content: Email;

  public static fromJson(js: JSON): Attachment {
    const a = new Attachment();
    // fill some attributes here
    if (js['email']) { a.content = Email.fromJson(js['email']); }
    return a;
  }
}

Initial call is: this.email = Email.fromJson(apiResponseForEmail);

This situation result into Circular dependency detected warning after ng build | serve. If this is not a good example for you, you can imagine Recipe class for food which contains Ingredients with attributes such as quantity and temperature where the result of one Recipe is a ingredient for another recipe.

If these classes are in one file, no warning is reported. However, placing everything in one big file is not the best solution to me (if there is any other).

Here is a Stackblitz with Circular dependency demo (but without warnings in console, stackblitz obviously removes warnings, you need to download this code and run from localhost to see warning).

I am interested if it is possible to place such classes (which are called in the code recursive by static method like in the example) in separate files (files named by classes) and avoid Circular dependency detected message?

like image 905
Atiris Avatar asked Nov 06 '18 07:11

Atiris


3 Answers

I think we all have habits coming from compiled languages. In JS files are modules. I think this is the root of the problem : these are not class files but module files.

Maybe you should use some other packing tools to merge you class files in a single module (so in a single file).

If you can put different classes in different namespaces there's a high chance that you won't have interdependent classes across these namespaces, so you can make one module per namespace. However interdependent classes should probably belong to a single namespace (=> same module => same file => need extra tools to pack)

EDIT: old question but I landed here an scratched my head to here are my two cents

like image 23
JulienCC Avatar answered Sep 28 '22 07:09

JulienCC


One of the ways to get rid of circular dependency is to introduce index.ts

file: index.ts

export { Attachment } from './attachment';
export { Email } from './email';

file: app/model/attachment.ts (part of code)

import * as index_file from './index';
index_file.Attachment {
  ...
  content: index_file.Email;
}

file: app/model/email.ts (part of code)

import * as index_file from './index';
index_file.Email {
  ...
  attachment?: index_file.Attachment;
}

PS: please check the syntax, I'm not confident on the syntax

like image 124
argo Avatar answered Sep 28 '22 06:09

argo


Hi @Atiris !

I already had this problem in a complex project with a lot of circular dependencies (56 warnings).
After many tries, I don't think I have found the right solution.

But I can possibly tell you how I did to solve each of the warnings.
I moved each implemented selector with a warning to the main file.
But it's not very clean.

Have you tried to use only 1 file ?
You can create a namespace in which you add the two classes Email and Attachment.

Here is a Stackblitz without Circular dependency demo


ANSWER :

I am interested if it is possible to place such classes (which are called in the code recursive by static method like in the example) in separate files (files named by classes) and avoid Circular dependency detected message?

I don't think you can make like that. This is structural issue. But I'm not 100% sure.

like image 20
Christophe Chevalier Avatar answered Sep 28 '22 07:09

Christophe Chevalier