For organizational reasons, we've clustered our components into a number of const arrays that we add to our root module's declarations. The language service does seem to handle this array unwrapping fine. However there is one particular array that doesn't seem to be read/acknowledged, causing all of its components to be incorrectly marked as included, for example:
Component 'AppComponent' is not included in a module and will not be available inside a template. Consider adding it to an NgModule declaration
Correspondingly, uses of those components in templates are marked as unknown elements.
The array in question is constructed using concat:
const PAGE_ELEMENTS = [
ComponentA,
ComponentB,
// ...
].concat(MODAL_DIALOGS, FUTURE_MODULE_TO_BE_SEPARATED);
We do this simply so we can reuse MODAL_DIALOGS in the module's entryComponents and prepare for a new submodule in a way that won't require us to delete the references one at a time in the future. Unfortunately, I believe it is the running of concat that prevents these items from being identified; when the sub-arrays are listed directly in the declarations, the problems disappear.
Is there some sort of a runtime vs compile-time limitation in play here? I hadn't considered this design choice in detail before, but I suppose from some angles it might be a bad pattern to use, especially in the module file.
For the time being, I will probably factor it out, but I thought it would still be useful to identify this case where the language service mysteriously failed to recognize components that worked when actually built & run.
Here's a minimal Plunklr that demonstrates the issue on my machine: http://plnkr.co/edit/8lVFE81oRiSDfupDsyyz?p=preview
The issue also occurs with other array mutations I've tested, such as push.
concat is supposed to work but it doesn't. For now you can use,
const COMPONENTS: any[] = [ChildComponent, ...ROOT_COMPONENTS];
instead which is turned into a concat by tsc.
When importing an array I couldn't get any method to work.
I have a separate components/ directory that has an index.ts and it exports an array of all the components to be used in the module.
Inside of the root module I import this array
import SharedComponents from './components';
...
@NgModule({
declarations: [
AppComponent,
SomeOtherComponent,
...SharedComponents
]
});
And any component within that SharedComponents array throw the warnings seen in this issue.
@tdsmithATabc I want to reaffirm you choice. It is not a bad pattern! Mutations like push, concat, etc. if they are all using const arguments should be fine - as the compiler can reason about its contents and see how the components flow into modules. This kind of set up (with concat specifically) makes it very easy to reason about sets of adjustable component lists that can be reused across modules.
@chuckjaz thanks for the ... suggestion - works perfectly for me.
@adam-beck you may want to open a new issue. import a constant array might be a different kind of issue - can you make a plunkr or small repo that reproduces that? The use cases seem related, but there may be something quite different underneath (I just import a bunch from a re-export).
Tracking in https://github.com/angular/angular/issues/21265
@adam-beck, I use a similar code you did and have the same issue. Did you find a workaround for that?
This will be fixed for free when the language service moves to using the Ivy compiler as its backend. Today (since Angular 9), AOT compilation supports static resolution of pure array methods like concat :)
This has been fixed by the new Ivy-native language service, released in v11.1.0.
Most helpful comment
When importing an array I couldn't get any method to work.
I have a separate
components/directory that has anindex.tsand it exports an array of all the components to be used in the module.Inside of the root module I import this array
And any component within that SharedComponents array throw the warnings seen in this issue.