Skip to content

Using multiple components in different modules causing "Type X is part of the declarations of 2 modules" error #10646

Closed

Description

@pleerock

I'm submitting a ... (check one with "x")

[ ] bug report
[x] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Right now you cannot import two directives of the same module

Expected/desired behavior

You can use same directives/components in multiple modules without errors.

What is the motivation / use case for changing the behavior?

Concept of the shared module with its shared resources is good for components that are used everywhere. But if you have a complex application (ours contain more then 50 modules and 1500 components, services and pipes all together) multiple components can be reused different way. For example QuestionListPage uses UserActiveListPanel from User module. Also PhotoListPage uses same UserActiveListPanel, and VideoListPage uses UserActiveListPanel. How do I reuse this panel? Best way is see is to provide it in directives of the component as it was before rc.5. But right now I can't do it. But I can create lets say UserShareModule and put it there... its good, but if I put everything that is used by other modules from user module, i'll LOT of things there unnecessary in most of times. Then what to do? Create UserActiveListPanelShareModule? It will create lot of such trash files. So what to do? Maybe we simply can provide same directives multiple times without having this annoying error Type X is part of the declarations of 2 modules?

If you tell me that its by design, then my answer is: in my opinion its a bad design.

Please tell us about your environment:

  • Angular version: 2.0.0-rc.5
  • Browser: [all ]
  • Language: [all]

Activity

  1. brandonroberts commented on Aug 10, 2016

    @brandonroberts
    Contributor

    If you want to use a component across multiple modules, you'll need to create a "shared" module and add that component the shared module's exports. Then you add that shared module into your other modules imports.

  2. pleerock commented on Aug 10, 2016

    @pleerock
    Author

    yeah I did it and it works. The problem is that it is very very inconvenient for multiple directives/components that are used only few times by other components.

  3. brandonroberts commented on Aug 11, 2016

    @brandonroberts
    Contributor

    Once you have your shared module setup it should be easier to add
    components to multiple modules. In the long run it helps you to organize
    your code into modules with a standard convention.

    On Aug 10, 2016 4:26 PM, "Umed Khudoiberdiev" notifications@github.com
    wrote:

    yeah I did it and it works. The problem is that it is very very
    inconvenient for multiple directives/components that are used only few
    times by other components.

    —
    You are receiving this because you commented.
    Reply to this email directly, view it on GitHub
    #10646 (comment),
    or mute the thread
    https://github.com/notifications/unsubscribe-auth/AACk49A-4QoQzcytHFasORcAgTbXJlftks5qekIhgaJpZM4JhYBD
    .

  4. pleerock commented on Aug 11, 2016

    @pleerock
    Author

    No it really harms organization in a long run.

    Lets bring an example. I have two modules. QuestionModule, PostModule and UserModule. QuestionModule and PostModule need UserListPanelComponent from a UserModule. How to handle such situation?

    option1. Create a UserSharedModule and add there UserListPanelComponent. But it looks like we gonna put to UserSharedModule all other components too (lets say other 20 components out of 50 UserModule has), because other modules (30) also uses different components of the UserModule. QuestionModule (or any other that uses UserModule's components) loads lot of stuff that he don't really need. It loads all 20 components when it needs only one. So it seems to be a bad option.

    option2. Create UserListPanelSharedModule. that shares only one component -UserListPanelComponent. Now I can load only one file and not worry that it loads all 20 components. But wait... its annoying and simply stupid to create one another separate file for each component you want to share. So, it definitely bad option.

    Any good options here?

  5. jusefb commented on Aug 11, 2016

    @jusefb

    This is indeed very confusing. I have moved some functionality into a module and now I am getting this error: "NgSelectOption is part of the declarations of 2 modules: InternalFormsSharedModule and SpFormsModule!" SpFormsModule is my module but I am not importing InternalFormsSharedModule anywhere. Any idea why would these two modules clash?

  6. brandonroberts commented on Aug 11, 2016

    @brandonroberts
    Contributor

    @jusefb this usually means you've added the NgSelectOption to your declarations instead of importing the appropriate forms module. The rule is if you created the directive, you need to declare it. If you didn't create it and it comes with a module, it doesn't go in your declarations. Instead you add that module to your imports.

    For your previous comment, if you are concerned with modules sharing components that other modules don't need, then you organize further. I used the term "shared" module loosely because there doesn't have to be just one. You can create a set of small modules to import into other modules to share components.

  7. jusefb commented on Aug 11, 2016

    @jusefb

    Thanks, i will go through my code more thoroughly and try get this to work

  8. DaSchTour commented on Aug 11, 2016

    @DaSchTour

    If you want to use the same Component you can maybe try to cheat angular by using extends.
    Try something like this.

    @Component({
        selector: "selector",
        templateUrl: "/component.html"
    })
    class BaseComponent {}
    
    export class AComponent extends BaseComponent {}
    export class BComponent extends BaseComponent {}

    No you have two components that are basically the same.
    To avoid conflicts you might add different selectors to the components.

  9. vicb commented on Aug 12, 2016

    @vicb
    Contributor

    Your question sounds like a support request.

    Please use the issue tracker only for bugs and feature requests.

    Use gitter and StackOverflow for support request.

  10. pleerock commented on Aug 12, 2016

    @pleerock
    Author

    where do you see a question here? :speachless:

  11. PunkSage commented on Aug 12, 2016

    @PunkSage

    @vicb I think that you need to read more carefully before you close an issue.

  12. ootwch commented on Aug 14, 2016

    @ootwch

    Same issue here: "Type NgSelectOption is part of the declarations of 2 modules". Fulltext search through my code has not found any occurence of 'NgSelectOption', so I am pretty sure that this is caused by a third party module (?) or library... but which one?

    => Please add "better error message" to this feature request, I think it might make the migration to post rc.5 with ngmodules a lot less painful

    Note: The solutions presented in #10784 did not help either...

  13. SnareChops commented on Aug 15, 2016

    @SnareChops

    Agreed, this is a legitimate feature request and not a question requesting support. This should be reopened for discussion.

  14. brandonroberts commented on Aug 15, 2016

    @brandonroberts
    Contributor

    @PunkSage @ootwch @SnareChops can someone reproduce this in a plunker? It would help in getting the issue reopened. If its a third party module that's causing it, my guess is that two modules have the NgSelectOption in their declarations instead of importing the appropriate module.

    It could also happen if you haven't removed all the directives from your components and still have a lingering directive in a component used in multiple modules.

  15. 70 remaining items

  16. mlc-mlapis commented on Nov 10, 2017

    @mlc-mlapis
    Contributor

    @rmcsharry ... generally the situation is simple ... you need to import (for example to module M) always a module (for example module S) when you want to use a component that is declared in that module (module S).

    This is the same principle both for eager or lazy module.

  17. rmcsharry commented on Nov 10, 2017

    @rmcsharry

    @mic-mlapis It seems I am not doing a very good job of explaining a situation that appears to NOT follow these rules, yet should.

    Here are the prerequisites for this problem:
    1 ThisComponent is declared in Module S.
    2 Module S declares two routes, A and B, each of which lazy load their own modules: ModuleLazyA, ModuleLazyB.

    So in this situation, modules A and B are not importing Module S.
    Module S is not importing either A or B, but is lazy loading them.
    A and B are essentially children of S.

    But neither A or B can see ThisComponent EVEN THOUGH it is declared in their parent module. They seem to just completely ignore it.

    So I have to put ThisComponent into another module and then that module has to be imported by BOTH of the lazy modules.

    Does that not seem odd to anyone else?

    Why do I have to create another module just for ThisComponent...surely it makes more sense for the lazy loaded modules to have 'access' to whatever components are declared in their parent module?

    If I am wrong and they CAN DO THAT, then clearly there is some extra plumbing code I need to add to make that happen.

    If they CANNOT DO THAT then the statement by @jcmordan is incorrect when he said "your component will be available for all sub modules childrens of the module where you put your shared component."

    I would also contend your statement "This the same principle both for eager or lazy module."

    If A and B were eager loaded, then they are imported into S (not the same as lazy loaded). So in that scenario they would both get access to ThisComponent declared in their parent, S (and an extra module would not be required).

  18. mlc-mlapis commented on Nov 10, 2017

    @mlc-mlapis
    Contributor

    @rmcsharry ... you have to create a new module, even for this one component, we can call it SHARED, and this one will be imported to the eager module and both lazy loaded modules. The concept of modules is relative easy but what is SUPER IMPORTANT is the context in which components are declared, compiled, and used ...

    And this is the reason why you need the new module ... and when lazy loaded modules will be loaded in future ... that new module will be already loaded before of course in this case. Creating a new module is about 5 lines of code so it is nothing to care about.

  19. rmcsharry commented on Nov 22, 2017

    @rmcsharry

    @mlc-mlapis Thank you for the reply but I believe you are still not understanding what I am saying, which is this:

    1. You don't need the shared module if a parent is eager loading children. The parent can contain the component and both children will be able to use it.

    2. You do need the shared module if a parent is lazy loading children. The children will not see the component if you declare it in the parent so in this scenario you must use a shared module.

    So I would say it is something to care about - otherwise you are in danger of creating a huge shared module with maybe hundreds of components and then end up loading that on a lazy route just to get access to one single component. So how you structure shared code is quite important I think. I imagine most large Angular projects will require several shared modules, not just one, for this reason. And on routes which are not lazy loaded, they might not need a shared module at all - they can simply put the component in the parent.

  20. GO3LIN commented on Jan 4, 2019

    @GO3LIN

    Well, I join this discussion another year after your discussion.

    I just wanted to specify that the components/directives that are not widely used in the app should not be included in the shared module !!!

    The feature modules can also export components and directives, don't just get stuck in the shared module and put in it everything that is only used few times in all your app. This is an anti-pattern ;-) Enjoy :)

  21. m3hm3tk commented on Jan 16, 2019

    @m3hm3tk

    I have a shared module but I am getting the following. "ModuleX and ModuleX" 😄

    Type PipeA is part of the declarations of 2 modules: ModuleX and ModuleX! Please consider moving PipeA to a higher module that imports ModuleX and ModuleX. You can also create a new NgModule that exports and includes PipeA then import that NgModule in ModuleX and ModuleX .

    ModuleX has root and child providers too in addition to PipeA

    I had to create another module for PipeA

  22. rjamesnw commented on Feb 5, 2019

    @rjamesnw

    I'm doing research before I start using Angular CLI (first time dealing with Angular). Based on this thread I'm deciding the best way to build my first Angular app is to create one module for every component or group of components that perform a single function, like:

    1. All basic/primitive functionality controls have one module and one component per module.
      Example:
      - 1 module and component for Avatar picture.
      - 1 module and component for an image file upload button.

    2. Specialized "controls", such as a module that imports the avatar picture control (module) and the image upload control (module) together.

    I believe thinking about modules as single "controls", not website "widgets" is the best way to modularize Angular apps (well, any web app really). That said, a widget of course will also be a module, so it's just a way of thinking (control modules vs widget modules, all placed on a view module).

  23. kennygitchen commented on Feb 7, 2019

    @kennygitchen

    I don't think this thread is about what is the best practice, I think most of people here are not asking for what is the best practice to do this. Other than, this is quite a specific problem that people trying to get Angular to solve.

    The issue here is really dependency management and probably, especially for people managing large applications with lazy loading components (loading components on the fly, so the app gets loaded in parts)

    I have been working with a web application contains 60+ screens, and based on the configurations, each client gets different screens. Each screen implementation is complex enough to warrant its own module. So, we don't want to load the whole application when the user hit the url the first time. The whole app download will probably exceed 5MB+ in size. For us to be able to manage the 'dependencies' probably, it will be easier to just declare the components in the module and let the code/framework sort out the duplications. You can argue this is custom but as there are quite a few people coming in this thread to express their 'feedback' as 'this is not good enough' (well at least I am), is probably something worth look into.

    In situation like this, declaring shared module specifically for the combination of 'screens' will be mad, therefore there is no choice but to put all the shared components into one single shared module. That defects the purpose of splitting the code. Of course there is always a solution to a problem, such as declare a shared module for each, for example client, but the point is 'we are giving our feedback to improve this framework'.

    This might be an old concept now as most of the web apps I have been working are just one JS file contains everything. Therefore, putting all shared component as one shared module isn't an issue at all.

    cheers, Kenny

  24. mlc-mlapis commented on Feb 7, 2019

    @mlc-mlapis
    Contributor

    @kennygitchen ...

    just one JS file contains everything / only one single shared module

    Certainly not. Creating a module means adding 5 lines of code over the code for just a component, so it's nothing that should be a problem.

    There is an idea to have a so-called default module (when Ivy will be ready) which should allow to lazily-load only a single component.

  25. rjamesnw commented on Feb 11, 2019

    @rjamesnw

    @mlc-mlapis The default module wrapping sounds great, and makes sense. I find myself wondering the same thing as I keep creating a module just to wrap a component for the purpose of sharing in other modules. Seems that should be implicit.

  26. phutaneVinayak commented on Feb 14, 2019

    @phutaneVinayak

    as @brandonroberts saids, create a shared module like this:

    import { NgModule }       from '@angular/core';
    import { CommonModule }   from '@angular/common';
    import { FormsModule }    from '@angular/forms';
    
    import {SharedComponentA} from "./SharedComponentA";
    import {SharedComponentB} from "./SharedComponentA";
    
    @NgModule({
        imports: [
            CommonModule,
            FormsModule,
    
        ],
        declarations: [
          SharedComponentA,
          SharedComponentB
    
        ],
        providers: [
        ],
        exports: [
          SharedComponentA,
          SharedComponentB
        ]
    })
    export class SharedModule {}
    

    then use the SharedModule like this..

    import { NgModule }       from '@angular/core';
    import { CommonModule }   from '@angular/common';
    import { FormsModule }    from '@angular/forms';
    
    import {SharedModule } from './SharedModule';
    
    @NgModule({
        imports: [
            CommonModule,
            FormsModule,
    
            SharedModule
    
          //..
        ],
        declarations: [
         // ....
        ],
        providers: [
            // ....
        ]
    })
    export class PersonModule{}
    

    yes i try this way.

    but angular giving error for shared components

    error: Template parse errors:
    'trip-card' is not a known element:

    1. If 'trip-card' is an Angular component, then verify that it is part of this module.
  27. Angelita31 commented on Mar 26, 2019

    @Angelita31

    it works fine for me
    In my case: this is my component that is repeating ModalFilterOptionPage :
    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    import { Routes, RouterModule } from '@angular/router';

    import { IonicModule } from '@ionic/angular';

    import { ReactiveFormsModule } from '@angular/forms';

    const routes: Routes = [
    {
    path: '',
    /* component: ModalFilterOptionPage remove it */
    }
    ];

    @NgModule({
    imports: [
    CommonModule,
    ReactiveFormsModule,
    IonicModule,
    RouterModule.forChild(routes)
    ]
    /* declarations: [ModalFilterOptionPage] remove it*/
    })
    export class ModalFilterOptionPageModule {}

    Then, only add it in your app module:
    ...
    @NgModule({
    declarations: [
    AppComponent,
    ModalFilterOptionPage],
    entryComponents: [
    ModalFilterOptionPage
    ],

    .....})
    it works for me!

  28. proto6 commented on Aug 18, 2019

    @proto6

    I can only agree w/ @rmcsharry, he explained the problem multiple times in detail, specifically for lazy loaded modules.

    question is also if I have to import that shared module again and again in lazy loaded modules (although the shared module was already loaded by the main app.module and should be accessible), will this bloat my lazy loaded chunks w/ redundant code ?

  29. angular-automatic-lock-bot commented on Sep 18, 2019

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  30. locked and limited conversation to collaborators on Sep 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions