I'm studying Angular 2 internal components and behaviors and I'm having some questions concerning the component tree management.
In a web app based on components, it's clear that we have a component tree. One component is composed of another one, from top to bottom, and it's really powerful.
But now, I m wondering how does angular 2 manages the representation of this component tree internally ?
What I mean there is that we never say in an angular component, what components will be inside of it, except in the template.
For example, I never say in my HomeComponent definition that it owns a PrestaCardComponent :
import { Component, OnInit, Inject } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
prestations: Array<any>;
featurettes: Array<any>;
constructor( #Inject('AppStore') private appStore: any) {
this.prestations = [];
this.featurettes = [];
}
ngOnInit() {
}
}
Except in my template :
<div *ngFor="let prestation of prestations" class="col-md-4 m-b">
<app-presta-card [title]="prestation.title" [content]="prestation.content" [image]="prestation.image"></app-presta-card>
</div>
What I understand it means
It means that Angular 2 is able to create the virtual component tree, by reading the different templates.
How can it be possible ? How does it work ?
Use Augury. you will get a clear insight.
NgModules are key to understanding how Angular deciphers the template when it parses it.
Look into the definition of these properties while decorating with #NgModule,
declarations : List of components, directives, and pipes that belong to this module.
imports : List of modules to import into this module. Everything from the imported modules is available to declarations of this module.
exports : List of components, directives, and pipes visible to modules that import this module.
using this knowledge Angular knows what selector means what, and using Reflection it gets Metadata for the component.
Of course there is more to it, but this may be a start.
Hope this helps!!
All the configuration of your components are rooted in an NgModule.
As Madhu Ranjan already mentioned in his answer there are the following 3 important parts in an NgModule, namely being:
declarations : List of components, directives, and pipes that belong to this module.
imports : List of modules to import into this module. Everything from the imported modules is available to declarations of this module.
exports : List of components, directives, and pipes visible to modules that import this module.
Actually there is even an FAQ for NgModule as it was a major change in the angular2 architecture since (I think) RC5.
Each and every component has to be part of an NgModule. It declares a part of your application which functionalities belong to each other. You can even nest NgModules inside each other with the imports part of it.
A positive part IMO is that you can organize your application very well with this structure as each angular module has its own routing configuration.
Furthermore you can limit accessability of services that should be used by declaring them inside e.g. a (sub-) module of another module just to name a few important features.
Check out the Angular2 Docs for more information about this and many more subjects. It is pretty detailed and IMO very easy to understand as the angular team took alot of effort of keeping it up to date and clean (when you don't mind searching a bit for the parts you need as the topic grouping is kinda crappy in the docs).
Related
I have a file with defined interfaces I'd like to share with my app's global namespace. My first attempt to do this was that I imported the interfaces.ts file into my app.module.ts and placed the interface into the exports array, but types/interfaces cannot be used in this way, so I thought of a few questions related to this small issue.
Does app.module.ts need to be involved for exporting to the global namespaces?
Is there a different way to do it (probably a native javascript way)?
If app.module.ts is required to do this, how do I import an interface in a way that my app.module.ts shares it with the rest of my app, so that I wont have to constantly import it in my components?
Checkout the global namespace here: https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
/*~ Note: If your global-modifying module is callable or constructable, you'll
*~ need to combine the patterns here with those in the module-class or module-function
*~ template files
*/
declare global {
/*~ Here, declare things that go in the global namespace, or augment
*~ existing declarations in the global namespace
*/
interface String {
fancyFormat(opts: StringFormatOptions): string;
}
}
Beware though:
This pattern is somewhat dangerous due to the possibility of runtime conflicts, but we can still write a declaration file for it.
It's better to have a SharedModules for everting that you want to use in the entire application more than one place according to the Angular official document. In this SharedModules you can have different directory app structure for all common things such as Models, Services, Interceptors, Pipe, components and etc.
Then Use this module on your main app.module.ts and import what you need.
For more information study this helpful & quick guide on PluralSight.
I was implementing dynamic components for one of my project. The concept of dynamic components is that they come into the memory once they are needed and they have no reference in any template.
According to the official docs we declare such components in entryComponents to prevent them from getting discarded in the tree shaking process as they have no template reference.
Following is the app.module.ts where I have declared my two dynamic components SlideOneComponent and SlideTwoComponent in the entryComponents array:
#NgModule({
declarations: [
AppComponent,
ButtonComponent,
AdDirective
],
imports: [
BrowserModule
],
providers: [],
entryComponents: [
SlideOneComponent,
SlideTwoComponent,
],
bootstrap: [AppComponent]
})
export class AppModule { }
With above app.module.ts I am getting following error:
The above error fades away as soon as I add my both dynamic components to declarations array. The aforementioned official docs also says that we do not need to declare components that are reachable from entryComponents or bootstrap component. I visited this answer as well but that does not seem to be satisfactory enough as it is in reference to Ionic.
Please help me to know where I am missing on this. Thanks in advance! :)
As seen in this quote (while reading between the lines):
Though the #NgModule decorator has an entryComponents array, most of
the time you won't have to explicitly set any entry components because
Angular adds components listed in #NgModule.bootstrap and those in
route definitions to entry components automatically.
You add usual components to EntryComponents array implicitly, meaning they are added in both arrays - Declarations and EntryComponents (while you added it only in Declarations array). So you must add dynamic components explicitly to both arrays as well.
Declarations purpose is to make directive (component etc.) available for other classes in your module and match it's selector with HTML.
EntryComponents tells Angular to create a componentFactory, which is being created by a compiler from the metadata you provide in #Component decorator, so you later can use it in createComponent().
As seen, these two arrays serve very different purposes and both are needed in order to create a component. If component is not created dynamically, compiler reads it's metadata and creates a componentFactory, but compiler is not aware of dynamic components, so you must inform it about dynamic components before it runs, as he runs only once - at compile time :)
I have an AngularJS project with a shared directory, in which there are several shared components.
- shared
- index.js
- alert
- index.js
- alert.component.js
- alert.controller.js
- alert.tpl.html
- alert.scss
- logging
- index.js
- logging.component.js
- logging.controller.js
- logging.tpl.html
- logging.scss
The code is written in a modular way in ES6. So for instance alert.component.js might look like this:
import controller from './alert.controller'
export const Alert = {
controller,
templateUrl: 'shared/alert/alert.tpl.html'
};
I would like to have one AngularJS module called shared in which both components are defined. My question is on what level should I actually define the component. Should it be inside the component directory (shared/alert/index.js) or in the shared directory (shared/index.js).
In the first case, the file shared/alert/index.js, would look like this:
import { Alert } from './alert.component';
angular.module('shared').component('Alert', Alert);
And in the second case, the file would look like this:
export { Alert } from './alert.component';
And then both components would be defined in shared/index.js:
import { Alert } from './alert';
angular.module('shared').component('Alert', Alert);
import { Logging } from './logging';
angular.module('shared').component('Logging', Logging);
The first case seems a bit odd to me, since I kind of let the component add itself to the application. In the second case however I end up with a huge index.js file, if I have many shared components. So I was wondering what other up and down sides each of these approaches has and if there any best practices?
As with any 'best practice', the preferable way to do this is deduced from possible problems that can appear in this situation.
angular.module('shared') module getter is generally an antipattern, especially in modular environment. It should be evaluated after the module was defined with angular.module('shared', []) module setter. Doing the opposite will result in race condition and error.
In the first case importing shared/alert/index.js in tests or another module will result in error because shared module wasn't defined yet.
In the second case there will be no such problem, but only if shared module is defined in shared/index.js.
The approach that plays well with ES modules is one module per file. The result is highly modular application with relatively low amount of boilerplate code. The benefits are reusability and testability, submodules can depend on each other but aren't coupled to share.
This is important consideration if some of submodules contain items that affect the entire application - decorators, config/run blocks, third-party modules that contain them (router).
I want to create an application wide variable accessible between various angular2 components and imported modules.
I have looked at dependency injection in my app.module and thought maybe I could set a class with a property in that. However, with the new angular-RC5 release, this looks like an overcomplication for what I want to do.
Alternatively, I thought of using a #Inputs and #Outputs to cascade the data and subscribe to changes, however, that doesn't seem to be possible between modules.
I would be really grateful for a suggestion of the easiest way of doing this.
In terms of my particular application, I have a navbar component which I want to show on all routes except one. So I have that on my app.component template, with an *NgIf condition, which I then wanted to be able to change from various child components to display the navbar, without having to embed the navbar component in all of my child modules and components (which gets tricky with components being shared between modules. Some of my routes are imported in a module.
You can create a shared service.
something like that :
import { Injectable } from '#angular/core';
#Injectable()
export class GenericService {
data:any = {};
}
and then add it to your app.module.
after that you can inject it to your component and add datas on it who will be accessible from all your components.
This is usually done using a shared service.
For details see
https://angular.io/docs/ts/latest/cookbook/component-communication.html
https://angular.io/docs/ts/latest/cookbook/rc4-to-rc5.html
https://angular.io/docs/ts/latest/guide/ngmodule.html#!#q-why-it-is-bad
For every example I come across in Angular 2 the template lives in the component decorator.
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: '<h1>Hello world</h1>'
})
export class AppComponent { }
Is this the correct convention? When templates become complex I would imagine this syntax becomes cumbersome.
I have tried using templateUrl instead of template. But as I understand it Angular will then load the template via ajax. (Slightly related, but since I've updated to 2.0.0-rc.3 templateUrl no longer seems to be working, but maybe I'm doing something wrong).
What is the best way to handle templates?
You can use templateUrl in rc3; see below the template code for a basic component:
import { Component, OnInit } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'app-my-component',
templateUrl: 'my-component.component.html',
styleUrls: ['my-component.component.css']
})
export class MyComponentComponent implements OnInit {
constructor() {}
ngOnInit() {
}
}
templateUrl should be used as a normal practice to keep the component definition clean.
The official ng2 tutorial will help clarify some of the base-questions , it helped me : https://angular.io/docs/ts/latest/tutorial/
From the Angular2 style guide
| Do extract templates and styles into a separate file, when more than 3 lines.
The upcoming offline template compiler will inline templates linked by templateUrl. There are also Gulp tasks available to do this already.
The official style guide
https://angular.io/styleguide#!#05-04
may suggest you to isolate styles and templates into different files.
But at the same time if you are shipping a component the single component file will make it easy and independent. So it is about your requirement. If you want your component to be reused independently this approach where embedding the template file into the component would win. And in the end it's your choice.
Hope this help you. Thank you.
Template url should be used for writing html code because of two reasons:
1. Seperation of concerns : A convention used for project development.
2. Easy debugging: in various IDE's you can use html lint plugins so as to find issue in html.
If your html is of one or two line then you can use it inline.