Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
pleerock opened this issue Aug 10, 2016 · 98 comments

Comments

@pleerock
Copy link

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]
@brandonroberts
Copy link
Contributor

brandonroberts commented Aug 10, 2016

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.

@pleerock
Copy link
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.

@brandonroberts
Copy link
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
.

@pleerock
Copy link
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?

@jusefb
Copy link

jusefb commented Aug 11, 2016

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?

@brandonroberts
Copy link
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.

@jusefb
Copy link

jusefb commented Aug 11, 2016

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

@DaSchTour
Copy link

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.

@vicb
Copy link
Contributor

vicb commented Aug 12, 2016

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.

@vicb vicb closed this as completed Aug 12, 2016
@pleerock
Copy link
Author

pleerock commented Aug 12, 2016

where do you see a question here? :speachless:

@PunkSage
Copy link

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

@ootwch
Copy link

ootwch commented Aug 14, 2016

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...

@SnareChops
Copy link

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

@brandonroberts
Copy link
Contributor

brandonroberts commented Aug 15, 2016

@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.

@kylecordes
Copy link

@brandonroberts and others - I think this error occurs when a component (either user-written or built-in) has been accidentally, implicitly, transitively included in more than one module - which can easily happen even without the now-deprecated "directives" list. Maybe that tip will make it easier to make a repro plunker. Still, I suspect it will turn out to be a "feature, not a bug".

@brandonroberts
Copy link
Contributor

@kylecordes Right, but if you've upgraded to RC5 and you haven't removed all your directives from your components, and one of those components is still including one of the old FORM_DIRECTIVES arrays, I could see why it would happen.

@ootwch
Copy link

ootwch commented Aug 16, 2016

@brandonroberts Thanks, that has bee helpful. I guess it could also be FormControl or ControlValueAccessor?

New full-text search on @angular/forms found that some of the imported components included @angular/forms. Removing the components where those are used seems to make the error message disappear.

Note that I was mentioning the error message above, seems in one case it gave me the name of the offending module... in another case it mentioned InternalFormsSharedModulewhich was less helpful. However, this was with angular2-material, which offers a module-based version I should update to...

Conclusion: The update from RC4 to RC5 requires to update (all?) third party libraries in use, or at least the ones designed for ng2? Seems the whole thing is less backward compatible than I had been hoping from the documentation ;-), but it also seems solvable now...

Would it have been helpful to have import { ... } from "@angular/forms"; provide an error or warning? That might be a feature request for a future migration... :-)

@kylecordes
Copy link

@ootwch With the new module system, it will be most typical for third-party Angular based libraries to present themselves to your application as a module. So yes, they will need to be updated to embrace RC5+. However, in the meantime there is a fairly easy workaround. You can simply add a single file in your application which defines a module which includes the contents of the older (RC4 or less) angular based library you were using. Then depend on this module from each of your own modules. That should get you past the trouble between now and when those libraries are updated.

@lacma2011
Copy link

I am having problems with ng2-select. I can't drop its directive into multiple modules now in rc5. It was a good library for making nice multi-select dropdowns but now it's ridiculous that it can't be used twice in the application.

@jcmordan
Copy link

jcmordan commented Sep 28, 2016

Hi, I have the same problem here. I try moving the common components to the AppModule. but then i got and error

Can't bind to 'option' since it isn't a known property of 'panel-detail'

<panel-detail option="data"></panel-detail>

@zoechi
Copy link
Contributor

zoechi commented Sep 28, 2016

@jcmordan
GitHub issues are for bug reports and feature requests.
For support questions please use other channels like the ones listed in CONTRIBUTING - Got a Question or Problem?

@Parthchokshi
Copy link

Any update on this? It really is a headache.

http://stackoverflow.com/questions/39925818/type-xyzcomponent-is-a-part-of-two-modules-module-gets-confused-what-route-to-t

@jredd
Copy link

jredd commented Oct 12, 2016

Seems pretty silly to have to do this extra work to be able to use a component thats meant to be shared... Feels like an extra layer of configuration on top of all the other configuration thats needed...

@jcmordan
Copy link

jcmordan commented Oct 12, 2016

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{}

@Yamilquery
Copy link

Domo arigato gozaimasu, from México! 👍

@mesopotamia
Copy link

I would have to agree that this is a big inconvenience. I have to create a module for something as trivial as one component.

@wsloth
Copy link

wsloth commented Nov 16, 2016

This is extremely inconvenient, as well as being plain ugly. You should really be able to inherit components from a parent module, because I think a shared module is extremely ugly.

You're going to end up with one huge shared module with all your components, which is one of the things I hate in more conventional platforms like .NET

I think you should be able to at least explicitly be able to share components between parent-child modules, in the sense of something like this:

@NgModule({
    imports: [
        CommonModule,
        FormsModule
    ],
    declarations: [
        Component1,
        Component2,
        // etc..
    ],
    expose: [ // Or really any word that would better describe what you're trying to do.
        Component1,
        Component2
    ]
})
export class AppModule{}

And then in your child module:

@NgModule({
    imports: [
        CommonModule,
        FormsModule
    ],
    inherit: [ // Inherit the exposed components from the parent module!
        Component1,
        Component2
    ]
})
export class SubModule{}

I'm sure there is a better way to do this. You just don't want to have ten thousand components in a Shared module.

@EinarValholl
Copy link

@jcmordan
But how use different selectors from SharedComponentA and SharedComponentB in some template of PersonModule in different places?

@rob-balfre
Copy link

FYI If you're like me and use angular-cli. When you refactor your code to use a shared module restart the cli server or you'll see console errors about the components not being registered. It doesn't detect the changes otherwise. That had me stumped for a while!

@duannx
Copy link

duannx commented Sep 4, 2017

Is there any update on this? One year latter since this issue was opened and i still can not find the answer.

@mlc-mlapis
Copy link
Contributor

@duannx ... a component can be included in the declaration section only of one module - it is by design.

@duannx
Copy link

duannx commented Sep 5, 2017

@mlc-mlapis Yeah. I khow it is by design but it is not the problem. The main question is how to share component between modules and the only answer is use share.module.ts. This way cause many annoying

@jcmordan
Copy link

jcmordan commented Sep 5, 2017

@duannx I know is annoying, but usually an app is build in a hirarchycal group of modules. With that in mind just put your shared component as top as you need, this way your component wil be avialable for all sub modules childrens of the module where you put your shared component.

@duannx
Copy link

duannx commented Sep 5, 2017

@jcmordan Yeah. I think i should do it for my project. Thanks.

@raysuelzer
Copy link

@jcmordan -- I was wondering about this. If I declare a component in my AppModule it should be available to any modules that need it that are in my imports? That seems to be how it is working for me.

@zoechi
Copy link
Contributor

zoechi commented Nov 2, 2017

@raysuelzer if you want to use components, directives, pipes from module B in module A, you have to import B (or any other module that exports B) in A.
What you import in AppModule doesn't matter to module A except for services.

@rmcsharry
Copy link

@jcmordan In the case of lazy loaded modules I am not sure what you said is correct:

"With that in mind just put your shared component as top as you need, this way your component wil be avialable for all sub modules childrens of the module where you put your shared component."

If you have ModuleMain which has routes defined, and it lazy loads ModuleLazyA and ModuleLazyB and both of those need ThisComponent, putting ThisComponent into ModuleMain does not seem to work, even though hierarchically speaking it is above the two lazy modules.

I found that I needed to put ThisComponent into ThisSharedModule and import into BOTH of the lazy modules. Did I miss something?

@zoechi
Copy link
Contributor

zoechi commented Nov 10, 2017

@rmcsharry No, you need to import every module everwhere where you want to use components, directives, or pipes.
A module can export another module, this way you can import a set of modules with a single import.
Besides that my first sentence still holds.

@rmcsharry
Copy link

@zoechi Thanks for the fast response. Are you saying I am correct or I am wrong? I thought that I could declare ThisComponent in ModuleMain and have it available to the two lazy loaded modules.

If ModuleLazyA and ModuleLazyB were NOT lazy loaded, then I presume that declaring ThisComponent inside ModuleMain (which would have to import A & B as NON lazy loaded) would then work, right?

It is only because A&B are lazily loaded that I CANNOT declare ThisComponent in ModuleMain, but must instead put that into a shared module for both lazy modules to import. This seems very counter-intuitive - since A&B are both lazy loaded by Main, why do they not get access to all components declared in Main?

@mlc-mlapis
Copy link
Contributor

mlc-mlapis commented Nov 10, 2017

@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.

@rmcsharry
Copy link

rmcsharry commented Nov 10, 2017

@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).

@mlc-mlapis
Copy link
Contributor

mlc-mlapis commented Nov 10, 2017

@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.

@rmcsharry
Copy link

@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.

@GO3LIN
Copy link

GO3LIN commented Jan 4, 2019

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 :)

@mehmettkocc
Copy link

mehmettkocc commented Jan 16, 2019

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

@rjamesnw
Copy link

rjamesnw commented Feb 5, 2019

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).

@kennygitchen
Copy link

kennygitchen commented Feb 7, 2019

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

@mlc-mlapis
Copy link
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.

@rjamesnw
Copy link

@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.

@phutaneVinayak
Copy link

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.

@Angelita31
Copy link

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!

@proto6
Copy link

proto6 commented Aug 18, 2019

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 ?

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests