1

I was refactoring my project to match the Angular2 styleguide at https://angular.io/guide/styleguide At first I had one module so there was no problem. Now while refactoring and with splitting into modules I got circular dependencies because of the navigation between pages of different modules.

Simplified, I have three modules, each having components:

  • Shared

    • BookListItemComponent
    • AjaxSpinnerComponent
    • FormHelperComponent
    • ...
  • Books

    • BookComponent
  • Shops

    • ShopListComponent

Modules Books and Shops each import Shared. The BookListItemComponent shows a book title, and when tapped navigates to the BookComponent which show the book's details.

The ShopListComponent shows a list of books of a certain shop.

Since the Books module imports the Shared module to use the spinner etc. this creates a circular dependency. How are we supposed to solve this?

In an app you navigate between pages of different modules. I don't see a way to avoid having these pointing at each other. Especially with the BookListItemComponent which is used all over the app to list books.

I have also looked at:

But couldn't really map this to my problem.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Bas van Dijk
  • 9,933
  • 10
  • 55
  • 91
  • The ShopListComponent shows a list of books of a certain shop. this creates a relation between shops and booklist and in turn book list so both these modules are like intermingled so i guess you need to have like a common module for this functionality i:e both book and shop will use to connect to book list item component . this might solve the issue – Rahul Singh Jul 04 '17 at 18:57
  • The thing is that it happens all over the app since pages are linking to each other between stores, books, sellers etc. Then the only way would be to create one module. I was also thinking about something like a shared navigation service or something. Also read about forwardRef, but did't see how this would solve my problem. – Bas van Dijk Jul 04 '17 at 19:02
  • I agree with @RahulSingh, if it's a common feature it should be moved to a common module (a.k.a., Shared). The idea is that FeatureModules don't talk to each others, they use a CommonModule instead. Also, it's more related to the design decisions that you made, how to design a component for it to be reusable. Basically you solve circular dependencies problem between modules not having dependencies among them and centralizing the common dependencies in a new module. – lenilsondc Jul 04 '17 at 19:08
  • I understand, the given example is a simplified version. In the real app I have over 40 components and around 12 modules. So basically this means I have to group all the components which can show a book. This means almost every module since the app is orientated around books. Think about a top100 of books, lists of books sold near you, list of publishers of which the detail view lists their books – Bas van Dijk Jul 04 '17 at 19:16
  • Not exactly, just cut off two way dependencies, you can have your book module and use it the way it is, just don't make the module dependent on the other module, you can move all the book dependencies from the shared module to your book module, or you can move everything to the shared module. The point here is: just don't create circular dependencies. You can avoid circular dependencies by taking a good design decision, architect your modules in such way that a module wouldn't require dependency on its dependents. – lenilsondc Jul 04 '17 at 19:25
  • If I move my bookListItem to the Book module this results in my Top100, user profiles, book shops, book authors, book publishers also need to be part of the book module just because they link to a book. A user profile is part of the user module so all these need to be moved inside the book module as well then. Thats what I meant with I end up with one module again with everything in one :) – Bas van Dijk Jul 04 '17 at 19:35
  • To clarify more, an example navigation chain: top100 -> book -> bookreview -> user -> userBookCollection -> book -> publisher -> publishedBookList -> book -> ShopsSellingBook -> shop -> BooksSoldByShop -> book Now since you can navigate between these components/pages you need all of these in 1 single module which eventually ends up with the whole app in 1 module. – Bas van Dijk Jul 04 '17 at 19:44
  • It seems not to have a way out rather than a refactor, as you can see, BooksModule is required as dependency event before it is declared, because to resolve BooksModule you have to resolve it's dependencies first, but one of the BooksModule dependencies is the module it self. Shared modules shouldn't depend on feature modules, and at this is point, you can't run from a refactor :\ – lenilsondc Jul 04 '17 at 20:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/148356/discussion-between-bas-van-dijk-and-lenilson-de-castro). – Bas van Dijk Jul 04 '17 at 20:55

1 Answers1

1

So I feel your pain in refactoring all of your code. We went through the same thing with one of our apps at work and it was a pain. On the backside of it though, well worth it! The way ionic suggests you to organize your files is not sustainable. I have a couple thoughts and ideas for you based on going through the same thing that might help you out.

First off, putting everything back in the same module is not the best idea, because then you end back where you started with the disorganized code as ionic would have you do it, rather than the organized code as Angular suggests. In general, Angular suggests some great patterns, and I think it's worth the struggle to get your code in line with their suggestions.

Secondly, and this is the main point, are you using deep links in your app? If you are, there is a fantastic, barely documented feature you get with deeplinks to avoid circular dependency within pages. Suppose you have a page with the following deep link config:

{
  component: MyCoolPage, // the page component class
  name: 'MyCoolPage',    // I use the name of the class but can be any sting you want
  segment: 'cool-page'   // optional, not related to the problem,
                         // but it's probably best to use this field as well
}

Whenever you want to navigate to MyCoolPage, instead of doing navCtrl.push(MyCoolPage), you can now do navCtrl.push('MyCoolPage') // or whatever string name you gave the page. So now you're navigating to pages via string names, which eliminates the need for importing pages whenever you want to navigate to it. This feature has existed since ionic 2, although I did not realize you could do this until updating to ionic 3.

Thirdly, more of a design consideration than anything else, you might want to reconsider navigating to pages from within components. Generally what we do is emit events up to the parent page components, and then have the page component handle pushing or popping the nav stack. Your BookListItemComponent shouldn't be causing you problems. If that is something in the shared module, used throughout the app, it shouldn't be depending on other modules. Your shared module shouldn't depend on anything else besides the ionic and angular modules you need to import.

Hayden Braxton
  • 1,151
  • 9
  • 14
  • Thanks for your remarks, however it looks like I am using a different way for deeplinking: `this.deeplinks.route({ '/en/book/:deeplinkBookSlug': Book, '/nl/book/:deeplinkBookSlug': Book, });` – Bas van Dijk Jul 04 '17 at 20:03
  • Well Ionic 2 documentations suggests defining deeplinks in your app module http://ionicframework.com/docs/2.3.0/api/navigation/DeepLinker/. If you do it that way, then you can provide a string name for each page, as well as the url segment that maps to that page, and then you can solve your circular dependency problem. – Hayden Braxton Jul 05 '17 at 12:07