Menu Search Me

Angular Components and Modules

Components

In toh-pt3\src\index.html, you have 

<app-root></app-root>

This specifies when the root component of your app will be used. In Angular, A component controls a patch of screen called a view.

This is the root component code: 

toh-pt3\src\app\app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Tour of Heroes';
}

The @Component decorator takes a required configuration object with the information Angular needs to create and present the component and its view.

Here are a few of the most useful @Component configuration options:

  • selector: CSS selector that tells Angular to create and insert an instance of this component where it finds a <app-hero-list> tag in parent HTML. For example, if an app's HTML contains <app-hero-list></app-hero-list>, then Angular inserts an instance of the HeroListComponent view between those tags.
  • templateUrl: module-relative address of this component's HTML template, shown above.
  • providers: array of dependency injection providers for services that the component requires. 
  • styleUrls: array of css stylesheets to apply to the templateUrl

You define a component's view with its companion template. A template is a form of HTML that tells Angular how to render the component.

toh-pt3\src\app\app.component.html
<h1>{{title}}</h1>
<app-heroes></app-heroes>

Here are 2 more components and their companion templates:

toh-pt3\src\app\heroes\heroes.component.ts
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';
@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes = HEROES;
  selectedHero: Hero;
  constructor() { }
  ngOnInit() {
  }
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
}

Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional lifecycle hooks (https://angular.io/guide/lifecycle-hooks), like ngOnInit() declared above. 

toh-pt3\src\app\heroes\heroes.component.html
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<app-hero-detail [hero]="selectedHero"></app-hero-detail>

Above template is calling another component (hero-detail) and embeds the view of hero-detail as part of its own view. Below implementation is not calling the hero-detail component.

toh-pt2\src\app\heroes\heroes.component.html
<h2>My Heroes</h2>
<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>
<div *ngIf="selectedHero">
  <h2>{{ selectedHero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="name">
    </label>
  </div>
</div>

In above templates, we see some Angular directives:

  • *ngFor tells Angular to stamp out one <li> per hero in the heroes list.
  • *ngIf includes the HeroDetail component only if a selected hero exists.

toh-pt3\src\app\heroes\heroe-detail.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})

export class HeroDetailComponent implements OnInit {
  @Input() hero: Hero;
  constructor() { }
  ngOnInit() {
  }
}

toh-pt3\src\app\heroes\heroe-detail.component.html
<div *ngIf="hero">
  <h2>{{ hero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name"/>
    </label>
  </div>
</div>

Note in above template htmls, we have the followind data binding:

  • The {{hero.name}} property binding displays the component's hero.name property value within the <li> element.
  • The [hero] property binding in <app-hero-detail [hero]="selectedHero"></app-hero-detail> passes the value of selectedHero from the parent HeroListComponent to the hero property of the child HeroDetailComponent. Note the @Input() decorator in the HereDetailComponent.
  • The (click) event binding in toh-pt3\src\app\heroes\heroes.component.html calls the component's selectHero method when the user clicks a hero's name, and pass the hero object to the function as input.

Two-way data binding uses the ngModel directive. 

<input [(ngModel)]="hero.name">

In two-way binding, a data property value flows to the input box from the component as with property binding. The user's changes also flow back to the component, resetting the property to the latest value, as with event binding.

For more information on Angular directives, see https://angular.io/guide/template-syntax

Modules

Every Angular app has at least one NgModule class, the root module, which is conventionally named AppModule and resides in a file named app.module.ts. You launch your app by bootstrapping the root NgModule.

While a small application might have only one NgModule, most apps have many more feature modules. The root NgModule for an app is so named because it can include child NgModules in a hierarchy of any depth.

Reference: https://angular.io/guide/architecture 

Here's a simple root NgModule definition:

toh-pt3\src\app\app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    HeroDetailComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

  • declarations: The components, directives, and pipes that belong to this NgModule.
  • exports: The subset of declarations that should be visible and usable in the component templates of other NgModules.
  • imports: Other modules whose exported classes are needed by component templates declared in this NgModule.
  • providers: Creators of services that this NgModule contributes to the global collection of services; they become accessible in all parts of the app.
  • bootstrap: The main application view, called the root component, which hosts all other app views. Only the root NgModule should set this bootstrap property.

Javascript Import vs Angular Import

The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are two different and complementary module systems. You can use them both to write your apps.

In JavaScript each file is a module and all objects defined in the file belong to that module. The module declares some objects to be public by marking them with the export key word. Other JavaScript modules use import statements to access public objects from other modules.

import { NgModule }     from '@angular/core';
import { AppComponent } from './app.component';

export class AppModule { }

What is entryComponents in Angular app.module.ts?

Offline template compiler (OTC) only builds components that are actually used. If components aren't used in templates directly the OTC can't know whether they need to be compiled. With entryComponents you can tell the OTC to also compile this components so they are available at runtime.
https://stackoverflow.com/questions/39756192/what-is-entrycomponents-in-angular-ngmodule/39756244

"Feature Modules" and how to make components/pipes available for other modules to use?

A "feature module" contains components, pipes, and services for other modules to use. To make components/pipes available for other modules to use, you need to "export" them in your feature module.

Here is an example non-root module which is a "feature module":

import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BsModalRef, ModalModule, TypeaheadModule } from 'ngx-bootstrap';
import { TranslateModule, TranslatePipe } from '@ngx-translate/core';
import { AuthGuard } from './guards/auth/auth-guard.service';
import { NgxMyDatePickerModule } from 'ngx-mydatepicker';
import { TextMaskModule } from 'angular2-text-mask';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';

import { SkipToContentComponent } from './components/skip-to-content/skip-to-content.component';
import { YellowCardPdfComponent } from './components/yellow-card-pdf/yellow-card-pdf.component';
import { SubmissionPdfComponent } from './components/submission-pdf/submission-pdf.component';
import { PhuTypeaheadComponent } from './components/phu-typeahead/phu-typeahead.component';
import { HostListenerComponent } from './components/host-listener/host-listener.component';
import { HcnScannerModalComponent } from './components/hcn-scanner/hcn-scanner.component';
import { HeaderComponent } from './components/header/header.component';
import { FooterComponent } from './components/footer/footer.component';

import { VaccineRecommendedAgeDisplayPipe } from './pipes/vaccine-recommended-age-display/vaccine-recommended-age-display.pipe';
import { AgeDisplayWithUnitsPipe } from './pipes/age-display-with-units/age-display-with-units.pipe';
import { PhoneDisplayPipe } from './pipes/phone-display/phone-display.pipe';
import { HcnDisplayPipe } from './pipes/hcn-display/hcn-display.pipe';

import { CustomDropdownManipulationService } from './services/custom-dropdown-manipulation/custom-dropdown-manipulation.service';
import { FormManipulationService } from './services/form-manipulation/form-manipulation.service';
import { KeyStreamCaptureService } from './services/key-stream-capture/key-stream-capture.service';
import { FhirSubmissionService } from './services/fhir/fhir-submission.service';
import { FhirRetrievalService } from './services/fhir/fhir-retrieval.service';
import { DatepickerService } from './services/datepicker/datepicker.service';
import { ValidationService } from './services/validation/validation.service';
import { LookupsService } from './services/lookups/lookups.service';
import { LoggingService } from './services/logging/logging.service';

import { CustomDateLocalizationPipe } from './pipes/custom-date-localization/custom-date-localization.pipe';
@NgModule({
  imports: [
    CommonModule,
    RouterModule,
    ModalModule.forRoot(),
    TypeaheadModule.forRoot(),
    FormsModule,
    ReactiveFormsModule,
    TranslateModule,
    TextMaskModule,
    NgxMyDatePickerModule.forRoot()
  ],
  exports: [
    HeaderComponent,
    PhuTypeaheadComponent,
    FooterComponent,
    AgeDisplayWithUnitsPipe,
    HcnScannerModalComponent,
    TranslatePipe,
    VaccineRecommendedAgeDisplayPipe,
    HcnDisplayPipe,
    PhoneDisplayPipe,
    SubmissionPdfComponent,
    YellowCardPdfComponent,
    SkipToContentComponent,
    HostListenerComponent,
    TextMaskModule,
    NgxMyDatePickerModule,
    CustomDateLocalizationPipe
  ],
  declarations: [
    HeaderComponent,
    PhuTypeaheadComponent,
    FooterComponent,
    AgeDisplayWithUnitsPipe,
    HcnScannerModalComponent,
    PhuTypeaheadComponent,
    VaccineRecommendedAgeDisplayPipe,
    HcnDisplayPipe,
    PhoneDisplayPipe,
    SubmissionPdfComponent,
    YellowCardPdfComponent,
    SkipToContentComponent,
    HostListenerComponent,
    CustomDateLocalizationPipe
  ],
  providers: [
    FhirSubmissionService,
    FormBuilder,
    ValidationService,
    LookupsService,
    BsModalRef,
    KeyStreamCaptureService,
    FormManipulationService,
    FhirRetrievalService,
    AuthGuard,
    DatepickerService,
    LoggingService,
    CustomDropdownManipulationService
  ],
  entryComponents: [
    HcnScannerModalComponent
  ]
})
export class SharedModule {
}
 
To use the pipes and components from a feature module, all you need to do in the module.ts is to import the feature module, as in this another example of a root module:
 
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { SharedModule } from './shared/shared.module';
import { Http, HttpModule } from '@angular/http';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';

import { TranslateHttpLoader } from '@ngx-translate/http-loader';

import { AppComponent } from './app.component';

import { LanguageTranslationService } from './shared/services/language-translation/language-translation.service';
import { ImmunizationRecordService } from './shared/services/immunization-record/immunization-record.service';
import { AdministeredByService } from './shared/services/administered-by/administered-by.service';
import { SessionTimerService } from './shared/services/session-timer/session-timer.service';
import { FooterUpdateService } from './shared/services/footer/footer-update.service';
import { TokenHandlerService } from './shared/services/token/token-handler.service';
import { LoggingService } from './shared/services/logging/logging.service';
import { UtilityService } from './shared/services/utility/utility.service';
import { ConsentService } from './shared/services/consent/consent.service';

export function HttpLoaderFactory(http: Http) {
  return new TranslateHttpLoader(http);
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    HttpClientModule,
    AppRoutingModule,
    SharedModule,
    TranslateModule.forRoot({
    loader: {
      provide: TranslateLoader,
      useFactory: HttpLoaderFactory,
      deps: [Http]
    }
    })
  ],
  providers: [
    ImmunizationRecordService,
    AdministeredByService,
    TokenHandlerService,
    FooterUpdateService,
    SessionTimerService,
    LanguageTranslationService,
    LoggingService,
    UtilityService,
    ConsentService
  ],
  bootstrap: [AppComponent]
  })
  export class AppModule {
  } 
 
For how to use the services and models defined in a feature module, refer to the section on "Services and Models".

Angular libraries

https://angular.io/guide/architecture   (overview)
https://angular.io/guide/ngmodules   (more details)

Angular ships as a collection of JavaScript modules. You can think of them as library modules.
Each Angular library name begins with the @angular prefix.
You install them with the npm package manager. To install all the Angular libraries that your project depends on:

cd <your project>
npm install

You import parts of the Angular library you installed with JavaScript import statements. For example, import Angular's Component decorator from the @angular/core library like this:

imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    MaterialModule,
    MdButtonModule,
    MdIconModule,
    MdCardModule,
    FlexLayoutModule,
    BrowserAnimationsModule,
    routing //this is not an angluar library but your app's routing module
],

You also import NgModules from Angular libraries using JavaScript import statements:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MaterialModule,MdButtonModule,MdIconModule,MdCardModule} from '@angular/material';
import {FlexLayoutModule} from '@angular/flex-layout';
import { routing } from './app.routes'; //this is not an angluar library but your app's routing module