What is the difference between declarations and entryComponents

Angular

Angular Problem Overview


I have this in my app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { HttpModule, Http } from '@angular/http';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { EliteApi } from '../shared/shared';
import { MyApp } from './app.component';
import { MyTeams, Tournaments, TeamDetails, Teams, TeamHome, Standings } from '../pages/pages';

import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

@NgModule({
	declarations: [
		MyApp,
		MyTeams,
		TeamDetails,
		Tournaments,
		Teams,
		TeamHome,
		Standings
	],
	imports: [
		BrowserModule,
		IonicModule.forRoot(MyApp),
		HttpModule
	],
	bootstrap: [IonicApp],
	entryComponents: [
		MyApp,
		MyTeams,
		TeamDetails,
		Tournaments,
		Teams,
		TeamHome,
		Standings
	],
	providers: [
		HttpModule,
		StatusBar,
		SplashScreen,
		{ provide: ErrorHandler, useClass: IonicErrorHandler },        
		EliteApi
	]
})
export class AppModule { }

At the moment my declarations and entryComponents both are exactly the same. They contain all of the page/components that I built for my app. If I remove any entry from any of the properties I get error in angular2.

My question is if they are always the same then what is the need for these properties? I think I am definitely missing some point here. When would entryComponents and declaractions be different from one another?

Angular Solutions


Solution 1 - Angular

The entryComponents array is used to define only components that are not found in html and created dynamically with ComponentFactoryResolver. Angular needs this hint to find them and compile. All other components should just be listed in the declarations array.

Here's the documentation on angular site

Solution 2 - Angular

Adding to what @Julia has answered to this question. I would like to add use case of Modal.


Lets say, you have a component called as ModalComponent. To make it more reusable, you would like to pass a component name and expect that component to be rendered inside the ModalComponent*.

Sample module.ts would be something like:

import:[ModalModule],  // As a best practice, we can create Modal as a separate Feature
entryComponent : [TheCompYouWantToRenderInsideModalComponent]

We would pass TheCompYouWantToRenderInsideModalComponent as entryComponent because this component will not be present while writing the website code (i.e there would not be selector of TheCompYouWantToRenderInsideModalComponent in any HTML file). We will pass this component to Modal and then it'll be rendered dynamically as and when the modal is opened. Something like below:

onSomeButtonClickToOpenModal(){
     this.modalService.openModal(TheCompYouWantToRenderInsideModalComponent);
}

*In the ModalModule, we would create service to use ComponentFactoryResolver and take TheCompYouWantToRenderInsideModalComponent as argument. Later, we can call a function (call it openModal(componentType: ComponentType)) which will Open & render using ComponentFactoryResolver


Side Note: The entryComponent will also play an important part in Angular V6 elements feature for the same purpose.

Update: Angular 9

With Angular 9 being released, we no more need to have entryComponent , Thanks to Ivy compiler

Solution 3 - Angular

In short:

The "real" reason behind the existence of entryComponents is tree-shaking, while declarations exists mainly for module encapsulation. So they aren't even comparable. If tree-shaking wasn't our concern, we would be good with declarations as a single source for both.

Longer answer

All the components that you created in your module don't go to final bundle. Rather, only the components which were declared in template using selectors OR the components that were added to entryComponents array (by you or the framework).

You would want to add the component when you creating the component dynamically like using ComponentFactoryResolver as stated by accepted answer. Or framework can add the component when you declare them in Route array or during other imperative creation.

From official docs:

> In fact, many libraries declare and export components you'll never > use. For example, a material design library will export all components > because it doesn’t know which ones you will use. However, it is > unlikely that you will use them all. For the ones you don't reference, > the tree shaker drops these components from the final code package. > > If a component isn't an entry component and isn't found in a template, > the tree shaker will throw it away. So, it's best to add only the > components that are truly entry components to help keep your app as > trim as possible.

Solution 4 - Angular

for Angular 9 or Angular 8 with Ivy explicitly enabled

Entry Components With Ivy is not required anymore and and now deprecated

Solution 5 - Angular

For this you need to understand how angular actually works behind the scenes when it comes to creating components.

Any component as well as directives and pipes you plan on working with, you need to add them to your declarations array in @NgModule of app.module.ts(while working with multiple modules, we import the feature module in our app.module.ts imports array, and that feature module has all the components in its declarations array).

The above mentioned step is important for angular to understand what's a component or which components and directives you have in your app because, it does not automatically scan all your files. You'd need to tell it which components exist, after creating a new component.

Still this alone only makes angular aware of it so that it is able to create such a component when it finds it in one of two places--

  1. The first place would be in your templates if in your templates angular finds a selector of a component--> then it basically looks into the declarations array for that particular component--> finds that there and then is able to create that component.

  2. The other place where angular will look for this component is in your routsin your rout config,--> when you point at a component there angular will also check that in the declarations array and--> if it finds that there it is able to create such a component and load it.

Now one place that does not work by default is when you want to create a component manually in code. Like when you want to create a dynamic componentwith component factory like an alert component maybe which only shows up when there is any error and you neither mention the selector of it in any template nor in the rout config.

And now, here angular does not automatically reach out to the declarations array. It simply doesn't do that. You can complain about that. Still it's the case.

You instead deliberately need to inform angular that in this case the alert component will need to be created at some place and that angular basically should be prepared for this.

Generally angular will prepare itself for this creation when it finds a component in the template or in a rout config.But in our case as we haven't done either of the two things as mentioned above, angular won't do prepare itself.

Now to tell angular to be prepared for the creation of that component you need to add a special property to the object you pass to Ngmodule besides declarations import and so on. That property is entry components, it is an array of component types, but only those components that will eventually be created without routs or selectors.

But with the launch of Angular 9, there were changes made behind the scenes, and it does all these work for you, you do not need to mention the entry components manually anymore.

Credits--Learned these concepts from the Udemy course "Angular the Complete Guide" -by Maximilian Schwarzmüller .

Solution 6 - Angular

An entry component is any component that Angular loads imperatively, which means you’re not referencing it in the template, by type. For Example :

@Component({
  selector: 'app-entry-component', // <== component select tags
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppEntryComponent {
  // <== Component Class
}

add this selector in the component you are using

<app-entry-component></app-entry-component>

add this component in appmodule

const routes: Routes = [
  {
    path: '',
    component: ComponentClassName,
    children: []
  }
]

finally

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    ...
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [
    AppEntryComponent
  ]
})
export class AppModule { }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTim LibertyView Question on Stackoverflow
Solution 1 - AngularJulia PassynkovaView Answer on Stackoverflow
Solution 2 - AngularShashank VivekView Answer on Stackoverflow
Solution 3 - AngulardasfdsaView Answer on Stackoverflow
Solution 4 - AngularFateh MohamedView Answer on Stackoverflow
Solution 5 - AngularArpan BanerjeeView Answer on Stackoverflow
Solution 6 - Angularpriya_21View Answer on Stackoverflow