docs: remove coremodule references (#28434)

PR Close #28434
This commit is contained in:
Kapunahele Wong 2019-01-22 14:11:35 -05:00 committed by Jason Aden
parent 009acd2a6c
commit e8921365b7
86 changed files with 703 additions and 2524 deletions

View File

@ -1,24 +0,0 @@
{
"description": "Contact NgModule v.1",
"files": [
"src/app/app.component.1b.ts",
"src/app/app.module.1b.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.service.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/main.1b.ts",
"src/styles.css",
"src/index.1b.html"
],
"main": "src/index.1b.html",
"tags": ["NgModule"]
}

View File

@ -1,26 +0,0 @@
{
"description": "Contact NgModule v.2",
"files": [
"src/app/app.component.2.ts",
"src/app/app.module.2.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.module.2.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/main.2.ts",
"src/styles.css",
"src/index.2.html"
],
"main": "src/index.2.html",
"tags": ["NgModule"]
}

View File

@ -1,223 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('NgModule', function () {
// helpers
const gold = 'rgba(255, 215, 0, 1)';
const powderblue = 'rgba(176, 224, 230, 1)';
const lightgray = 'rgba(211, 211, 211, 1)';
const white = 'rgba(0, 0, 0, 0)';
function getCommonsSectionStruct() {
const buttons = element.all(by.css('nav a'));
return {
title: element.all(by.tagName('h1')).get(0),
welcome: element.all(by.css('app-title p i')).get(0),
contactButton: buttons.get(0),
crisisButton: buttons.get(1),
heroesButton: buttons.get(2)
};
}
function getContactSectionStruct() {
const buttons = element.all(by.css('app-contact form button'));
return {
header: element.all(by.css('app-contact h2')).get(0),
popupMessage: element.all(by.css('app-contact div')).get(0),
contactNameHeader: element.all(by.css('app-contact form h3')).get(0),
input: element.all(by.css('app-contact form input')).get(0),
validationError: element.all(by.css('app-contact form .alert')).get(0),
saveButton: buttons.get(0), // can't be tested
nextContactButton: buttons.get(1),
newContactButton: buttons.get(2)
};
}
function getCrisisSectionStruct() {
return {
title: element.all(by.css('ng-component h3')).get(0),
items: element.all(by.css('ng-component a')),
itemId: element.all(by.css('ng-component div')).get(0),
listLink: element.all(by.css('ng-component a')).get(0),
};
}
function getHeroesSectionStruct() {
return {
header: element.all(by.css('ng-component h2')).get(0),
title: element.all(by.css('ng-component h3')).get(0),
items: element.all(by.css('ng-component a')),
itemId: element.all(by.css('ng-component ng-component div div')).get(0),
itemInput: element.all(by.css('ng-component ng-component input')).get(0),
listLink: element.all(by.css('ng-component ng-component a')).get(0),
};
}
// tests
function appTitleTests(color: string, name?: string) {
return function() {
it('should have a gray header', function() {
const commons = getCommonsSectionStruct();
expect(commons.title.getCssValue('backgroundColor')).toBe(color);
});
it('should welcome us', function () {
const commons = getCommonsSectionStruct();
expect(commons.welcome.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
});
};
}
function contactTests(color: string, name?: string) {
return function() {
it('shows the contact\'s owner', function() {
const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
});
it('can cycle between contacts', function () {
const contacts = getContactSectionStruct();
const nextButton = contacts.nextContactButton;
expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spade');
expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color);
nextButton.click().then(function () {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Nick Danger');
return nextButton.click();
}).then(function () {
expect(contacts.contactNameHeader.getText()).toBe('Awesome Nancy Drew');
});
});
it('can change an existing contact', function () {
const contacts = getContactSectionStruct();
contacts.input.sendKeys('a');
expect(contacts.input.getCssValue('backgroundColor')).toBe(color);
expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spadea');
});
it('can create a new contact', function () {
const contacts = getContactSectionStruct();
const newContactButton = contacts.newContactButton;
newContactButton.click().then(function () {
expect(contacts.validationError.getText()).toBe('Name is required');
contacts.input.sendKeys('John Doe');
expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe');
expect(contacts.validationError.getText()).toBe('');
});
});
};
}
describe('index.html', function () {
beforeEach(function () {
browser.get('');
});
describe('app-title', appTitleTests(white, 'Miss Marple'));
describe('contact', contactTests(lightgray, 'Miss Marple'));
describe('crisis center', function () {
beforeEach(function () {
getCommonsSectionStruct().crisisButton.click();
});
it('shows a list of crisis', function () {
const crisis = getCrisisSectionStruct();
expect(crisis.title.getText()).toBe('Crisis List');
expect(crisis.items.count()).toBe(4);
expect(crisis.items.get(0).getText()).toBe('1 - Dragon Burning Cities');
});
it('can navigate to one crisis details', function () {
const crisis = getCrisisSectionStruct();
crisis.items.get(0).click().then(function() {
expect(crisis.itemId.getText()).toBe('Crisis id: 1');
return crisis.listLink.click();
}).then(function () {
// We are back to the list
expect(crisis.items.count()).toBe(4);
});
});
});
describe('heroes', function () {
beforeEach(function () {
getCommonsSectionStruct().heroesButton.click();
});
it('shows a list of heroes', function() {
const heroes = getHeroesSectionStruct();
expect(heroes.header.getText()).toBe('Heroes of Miss Marple');
expect(heroes.title.getText()).toBe('Hero List');
expect(heroes.items.count()).toBe(6);
expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice');
});
it('can navigate and edit one hero details', function () {
const heroes = getHeroesSectionStruct();
heroes.items.get(0).click().then(function () {
expect(heroes.itemId.getText()).toBe('Id: 11');
heroes.itemInput.sendKeys(' try');
return heroes.listLink.click();
}).then(function () {
// We are back to the list
expect(heroes.items.count()).toBe(6);
expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice try');
});
});
});
});
// describe('index.0.html', function() {
// beforeEach(function () {
// browser.get('index.0.html');
// });
// it('has a title', function () {
// const title = element.all(by.tagName('h1')).get(0);
// expect(title.getText()).toBe('Minimal NgModule');
// });
// });
// describe('index.1.html', function () {
// beforeEach(function () {
// browser.get('index.1.html');
// });
// describe('app-title', appTitleTests(powderblue));
// });
// describe('index.1b.html', function () {
// beforeEach(function () {
// browser.get('index.1b.html');
// });
// describe('app-title', appTitleTests(powderblue));
// describe('contact', contactTests(powderblue));
// });
// describe('index.2.html', function () {
// beforeEach(function () {
// browser.get('index.2.html');
// });
// describe('app-title', appTitleTests(gold));
// describe('contact', contactTests(powderblue));
// });
// describe('index.3.html', function () {
// beforeEach(function () {
// browser.get('index.3.html');
// });
// describe('app-title', appTitleTests(gold));
// });
});

View File

@ -1,12 +0,0 @@
{
"description": "Minimal NgModule",
"files": [
"src/app/app.component.0.ts",
"src/app/app.module.0.ts",
"src/main.0.ts",
"src/styles.css",
"src/index.0.html"
],
"main": "src/index.0.html",
"tags": ["NgModule"]
}

View File

@ -1,40 +0,0 @@
{
"description": "NgModule v.3",
"files": [
"src/app/app.component.3.ts",
"src/app/app.module.3.ts",
"src/app/app-routing.module.3.ts",
"src/app/highlight.directive.ts",
"src/app/title.component.html",
"src/app/title.component.ts",
"src/app/user.service.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/awesome.pipe.ts",
"src/app/contact/contact.component.3.ts",
"src/app/contact/contact.module.3.ts",
"src/app/contact/contact-routing.module.3.ts",
"src/app/contact/contact-highlight.directive.ts",
"src/app/crisis/*.ts",
"src/app/hero/hero-detail.component.ts",
"src/app/hero/hero-list.component.ts",
"src/app/hero/hero.service.ts",
"src/app/hero/hero.component.3.ts",
"src/app/hero/hero.module.3.ts",
"src/app/hero/hero-routing.module.3.ts",
"src/app/hero/highlight.directive.ts",
"src/main.3.ts",
"src/styles.css",
"src/index.3.html"
],
"main": "src/index.3.html",
"tags": ["NgModule"]
}

View File

@ -1,19 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactModule } from './contact/contact.module.3';
const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
{ path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: './hero/hero.module.3#HeroModule' }
];
@NgModule({
imports: [
ContactModule,
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -1,30 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactModule } from './contact/contact.module';
// #docregion routes
const routes: Routes = [
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
// #docregion lazy-routes
{ path: 'crisis', loadChildren: './crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: './hero/hero.module#HeroModule' }
// #enddocregion lazy-routes
];
// #enddocregion routes
@NgModule({
// #docregion imports
imports: [
ContactModule,
// #docregion forRoot
RouterModule.forRoot(routes),
// #enddocregion forRoot
],
// #enddocregion imports
// #docregion exports
exports: [RouterModule]
// #enddocregion exports
})
export class AppRoutingModule {}

View File

@ -1,10 +0,0 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: '<h1>{{title}}</h1>',
})
export class AppComponent {
title = 'Angular Modules';
}

View File

@ -1,17 +0,0 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #enddocregion
/*
// #docregion template
template: '<h1 highlight>{{title}}</h1>'
// #enddocregion template
*/
// #docregion
template: '<app-title></app-title>'
})
export class AppComponent {}
// #enddocregion

View File

@ -1,13 +0,0 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<app-title></app-title>
<app-contact></app-contact>
`
// #enddocregion template
})
export class AppComponent {}

View File

@ -1,10 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<app-contact></app-contact>
`
})
export class AppComponent {}

View File

@ -1,17 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<app-title></app-title>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
<a routerLink="heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
// #enddocregion template
})
export class AppComponent {}

View File

@ -1,17 +0,0 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
<a routerLink="heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}

View File

@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component.0';
@NgModule({
// #docregion imports
imports: [ BrowserModule ],
// #enddocregion imports
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,52 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.1';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Related Imports */
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './contact/awesome.pipe';
import { ContactComponent } from './contact/contact.component.3';
import {
ContactHighlightDirective as ContactHighlightDirective
} from './contact/contact-highlight.directive';
@NgModule({
// #docregion imports
imports: [ BrowserModule, FormsModule ],
// #enddocregion imports
// #docregion declarations, directive, component
declarations: [
AppComponent,
HighlightDirective,
// #enddocregion directive
TitleComponent,
// #enddocregion component
AwesomePipe,
ContactComponent,
ContactHighlightDirective
// #docregion directive, component
],
// #enddocregion declarations, directive, component
// #docregion providers
providers: [ UserService ],
// #enddocregion providers
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,46 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.1b';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Imports */
// #enddocregion
import { ContactComponent } from './contact/contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact/contact.component';
// #enddocregion
*/
// #docregion
import { AwesomePipe } from './contact/awesome.pipe';
import { ContactService } from './contact/contact.service';
import { ContactHighlightDirective } from './contact/contact-highlight.directive';
@NgModule({
imports: [ BrowserModule, FormsModule ],
// #docregion declarations
declarations: [
AppComponent, HighlightDirective, TitleComponent,
AwesomePipe, ContactComponent, ContactHighlightDirective
],
// #docregion providers
providers: [ ContactService, UserService ],
// #enddocregion providers
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -1,36 +0,0 @@
// #docplaster
// #docregion
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
// #enddocregion
import { AppComponent } from './app.component.2';
/*
// #docregion
import { AppComponent } from './app.component';
// #enddocregion
*/
// #docregion
import { HighlightDirective } from './highlight.directive';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
/* Contact Imports */
// #enddocregion
import { ContactModule } from './contact/contact.module.2';
/*
// #docregion
import { ContactModule } from './contact/contact.module';
// #enddocregion
*/
// #docregion
@NgModule({
imports: [ BrowserModule, ContactModule ],
declarations: [ AppComponent, HighlightDirective, TitleComponent ],
providers: [ UserService ],
bootstrap: [ AppComponent ],
})
export class AppModule { }

View File

@ -1,39 +0,0 @@
// #docplaster
// #docregion
// #docregion v4
/* Angular Imports */
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
/* App Imports */
import { AppComponent } from './app.component';
/* Core Modules */
import { CoreModule } from './core/core.module';
/* Routing Module */
import { AppRoutingModule } from './app-routing.module';
@NgModule({
// #docregion import-for-root
imports: [
BrowserModule,
// #enddocregion v4
// #enddocregion import-for-root
/*
// #docregion v4
CoreModule,
// #enddocregion v4
*/
// #docregion import-for-root
CoreModule.forRoot({userName: 'Miss Marple'}),
// #docregion v4
AppRoutingModule
],
// #enddocregion import-for-root
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
// #enddocregion v4
// #enddocregion

View File

@ -1,14 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component.3';
const routes = [
{ path: 'contact', component: ContactComponent}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class ContactRoutingModule {}

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component';
// #docregion routing
const routes = [
{ path: 'contact', component: ContactComponent}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class ContactRoutingModule {}
// #enddocregion

View File

@ -1,53 +0,0 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../user.service';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: [ './contact.component.css' ]
})
export class ContactComponent implements OnInit {
contact: Contact;
contacts: Contact[];
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
this.userName = userService.userName;
}
ngOnInit() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
});
}
next() {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
}
newContact() {
this.displayMessage('New contact');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
/** Display a message briefly, then remove it. */
displayMessage(msg: string) {
this.msg = msg;
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,32 +0,0 @@
/* #docregion */
.ng-valid[required] {
border-left: 5px solid #42A948; /* green */
}
.ng-invalid {
border-left: 5px solid #a94442; /* red */
}
.alert {
padding: 15px;
margin: 8px 0;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.msg {
color: blue;
background-color: whitesmoke;
border: 1px solid transparent;
border-radius: 4px;
margin-bottom: 20px;
}
.button-group {
padding-top: 12px;
}

View File

@ -1,37 +0,0 @@
<!-- #docregion -->
<h2>Contact of {{userName}}</h2>
<div *ngIf="msg" class="msg">{{msg}}</div>
<form *ngIf="contacts" (ngSubmit)="onSubmit()" #contactForm="ngForm">
<!-- #docregion awesome -->
<h3 highlight>{{ contact.name | awesome }}</h3>
<!-- #enddocregion awesome -->
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion ngModel -->
<input type="text" class="form-control" required
[(ngModel)]="contact.name"
name="name" #name="ngModel" >
<!-- #enddocregion ngModel -->
<div [hidden]="name.valid" class="alert alert-danger">
Name is required
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-default"
[disabled]="!contactForm.form.valid">
Save</button>
<button type="button" class="btn" (click)="next()"
[disabled]="!contactForm.form.valid">
Next Contact</button>
<button type="button" class="btn" (click)="newContact()">
New Contact</button>
</div>
</form>
<!-- #enddocregion -->

View File

@ -1,54 +0,0 @@
// Exact copy except import UserService from core
// #docregion
import { Component, OnInit } from '@angular/core';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: [ './contact.component.css' ]
})
export class ContactComponent implements OnInit {
contact: Contact;
contacts: Contact[];
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
this.userName = userService.userName;
}
ngOnInit() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
});
}
next() {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
}
newContact() {
this.displayMessage('New contact');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
/** Display a message briefly, then remove it. */
displayMessage(msg: string) {
this.msg = msg;
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,11 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class ContactModule { }

View File

@ -1,37 +0,0 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
// #enddocregion
import { ContactComponent } from './contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact.component';
// #enddocregion
*/
// #docregion
import { ContactHighlightDirective } from './contact-highlight.directive';
import { ContactService } from './contact.service';
// #docregion class
@NgModule({
imports: [
CommonModule,
FormsModule
],
declarations: [
AwesomePipe,
ContactComponent,
ContactHighlightDirective
],
// #docregion exports
exports: [ ContactComponent ],
// #enddocregion exports
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class
// #enddocregion

View File

@ -1,44 +0,0 @@
// #docplaster
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
// #enddocregion
import { ContactComponent } from './contact.component.3';
/*
// #docregion
import { ContactComponent } from './contact.component';
// #enddocregion
*/
// #docregion
import { ContactHighlightDirective } from './contact-highlight.directive';
import { ContactService } from './contact.service';
// #enddocregion
import { ContactRoutingModule } from './contact-routing.module.3';
/*
// #docregion
import { ContactRoutingModule } from './contact-routing.module';
// #enddocregion
*/
// #docregion
// #docregion class
@NgModule({
imports: [
CommonModule,
FormsModule,
ContactRoutingModule
],
declarations: [
AwesomePipe,
ContactComponent,
ContactHighlightDirective
],
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class
// #enddocregion

View File

@ -1,19 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { ContactComponent } from './contact.component';
import { ContactService } from './contact.service';
import { ContactRoutingModule } from './contact-routing.module';
// #docregion class
@NgModule({
imports: [
SharedModule,
ContactRoutingModule
],
declarations: [ ContactComponent ],
providers: [ ContactService ]
})
export class ContactModule { }
// #enddocregion class

View File

@ -1,37 +0,0 @@
// #docplaster
// #docregion
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Contact {
constructor(public id: number, public name: string) { }
}
const CONTACTS: Contact[] = [
new Contact(21, 'Sam Spade'),
new Contact(22, 'Nick Danger'),
new Contact(23, 'Nancy Drew')
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves contacts from a server */
@Injectable()
export class ContactService implements OnDestroy {
// #enddocregion
constructor() { console.log('ContactService instance created.'); }
ngOnDestroy() { console.log('ContactService instance destroyed.'); }
// #docregion
getContacts(): Observable<Contact[]> {
return of(CONTACTS).pipe(delay(FETCH_LATENCY));
}
getContact(id: number | string): Observable<Contact> {
return of(CONTACTS.find(contact => contact.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}
// #enddocregion

View File

@ -1,48 +0,0 @@
/* tslint:disable:member-ordering no-unused-variable */
// #docplaster
// #docregion
// #docregion v4
import {
ModuleWithProviders, NgModule,
Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
import { UserService } from './user.service';
// #enddocregion
import { UserServiceConfig } from './user.service';
// #docregion v4
@NgModule({
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
providers: [ UserService ]
})
export class CoreModule {
// #enddocregion v4
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
// #docregion v4
}
// #enddocregion v4
// #enddocregion

View File

@ -1,6 +0,0 @@
<!-- Exact copy from earlier app.component.html -->
<h1 highlight>{{title}}</h1>
<p *ngIf="user">
<i>Welcome, {{user}}</i>
<p>

View File

@ -1,16 +0,0 @@
// Exact copy of app/title.component.ts except import UserService from shared
import { Component, Input } from '@angular/core';
import { UserService } from '../core/user.service';
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
})
export class TitleComponent {
title = 'Angular Modules';
user = '';
constructor(userService: UserService) {
this.user = userService.userName;
}
}

View File

@ -1,32 +0,0 @@
// Crazy copy of the app/user.service
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable, Optional } from '@angular/core';
let nextId = 1;
export class UserServiceConfig {
userName = 'Philip Marlowe';
}
@Injectable()
export class UserService {
id = nextId++;
private _userName = 'Sherlock Holmes';
// #docregion ctor
constructor(@Optional() config: UserServiceConfig) {
if (config) { this._userName = config.userName; }
}
// #enddocregion ctor
get userName() {
// Demo: add a suffix if this service has been created more than once
const suffix = this.id > 1 ? ` times ${this.id}` : '';
return this._userName + suffix;
}
}

View File

@ -1,19 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
template: `
<h3 highlight>Crisis Detail</h3>
<div>Crisis id: {{id}}</div>
<br>
<a routerLink="../list">Crisis List</a>
`
})
export class CrisisDetailComponent implements OnInit {
id: number;
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.id = parseInt(this.route.snapshot.paramMap.get('id'), 10);
}
}

View File

@ -1,21 +0,0 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Crisis,
CrisisService } from './crisis.service';
@Component({
template: `
<h3 highlight>Crisis List</h3>
<div *ngFor='let crisis of crises | async'>
<a routerLink="{{'../' + crisis.id}}">{{crisis.id}} - {{crisis.name}}</a>
</div>
`
})
export class CrisisListComponent {
crises: Observable<Crisis[]>;
constructor(private crisisService: CrisisService) {
this.crises = this.crisisService.getCrises();
}
}

View File

@ -1,18 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
const routes: Routes = [
{ path: '', redirectTo: 'list', pathMatch: 'full'},
{ path: 'list', component: CrisisListComponent },
{ path: ':id', component: CrisisDetailComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CrisisRoutingModule {}

View File

@ -1,14 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CrisisListComponent } from './crisis-list.component';
import { CrisisDetailComponent } from './crisis-detail.component';
import { CrisisService } from './crisis.service';
import { CrisisRoutingModule } from './crisis-routing.module';
@NgModule({
imports: [ CommonModule, CrisisRoutingModule ],
declarations: [ CrisisDetailComponent, CrisisListComponent ],
providers: [ CrisisService ]
})
export class CrisisModule {}

View File

@ -1,33 +0,0 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Crisis {
constructor(public id: number, public name: string) { }
}
const CRISES: Crisis[] = [
new Crisis(1, 'Dragon Burning Cities'),
new Crisis(2, 'Sky Rains Great White Sharks'),
new Crisis(3, 'Giant Asteroid Heading For Earth'),
new Crisis(4, 'Procrastinators Meeting Delayed Again'),
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves crises from a server */
@Injectable()
export class CrisisService implements OnDestroy {
constructor() { console.log('CrisisService instance created.'); }
ngOnDestroy() { console.log('CrisisService instance destroyed.'); }
getCrises(): Observable<Crisis[]> {
return of(CRISES).pipe(delay(FETCH_LATENCY));
}
getCrisis(id: number | string): Observable<Crisis> {
return of(CRISES.find(crisis => crisis.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}

View File

@ -1,31 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Hero,
HeroService } from './hero.service';
@Component({
template: `
<h3 highlight>Hero Detail</h3>
<div *ngIf="hero">
<div>Id: {{hero.id}}</div><br>
<label>Name:
<input [(ngModel)]="hero.name">
</label>
</div>
<br>
<a routerLink="../">Hero List</a>
`
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private route: ActivatedRoute,
private heroService: HeroService) { }
ngOnInit() {
let id = parseInt(this.route.snapshot.paramMap.get('id'), 10);
this.heroService.getHero(id).subscribe(hero => this.hero = hero);
}
}

View File

@ -1,20 +0,0 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Hero,
HeroService } from './hero.service';
@Component({
template: `
<h3 highlight>Hero List</h3>
<div *ngFor='let hero of heroes | async'>
<a routerLink="{{hero.id}}">{{hero.id}} - {{hero.name}}</a>
</div>
`
})
export class HeroListComponent {
heroes: Observable<Hero[]>;
constructor(private heroService: HeroService) {
this.heroes = this.heroService.getHeroes();
}
}

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component.3';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',
component: HeroComponent,
children: [
{ path: '', component: HeroListComponent },
{ path: ':id', component: HeroDetailComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HeroRoutingModule {}

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes,
RouterModule } from '@angular/router';
import { HeroComponent } from './hero.component';
import { HeroListComponent } from './hero-list.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '',
component: HeroComponent,
children: [
{ path: '', component: HeroListComponent },
{ path: ':id', component: HeroDetailComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HeroRoutingModule {}

View File

@ -1,18 +0,0 @@
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { UserService } from '../user.service';
@Component({
template: `
<h2>Heroes of {{userName}}</h2>
<router-outlet></router-outlet>
`,
providers: [ HeroService ]
})
export class HeroComponent {
userName = '';
constructor(userService: UserService) {
this.userName = userService.userName;
}
}

View File

@ -1,19 +0,0 @@
// Exact copy except import UserService from core
import { Component } from '@angular/core';
import { HeroService } from './hero.service';
import { UserService } from '../core/user.service';
@Component({
template: `
<h2>Heroes of {{userName}}</h2>
<router-outlet></router-outlet>
`,
providers: [ HeroService ]
})
export class HeroComponent {
userName = '';
constructor(userService: UserService) {
this.userName = userService.userName;
}
}

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HeroComponent } from './hero.component.3';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HeroRoutingModule } from './hero-routing.module.3';
import { HighlightDirective } from './highlight.directive';
// #docregion class
@NgModule({
imports: [ CommonModule, FormsModule, HeroRoutingModule ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
HighlightDirective
]
})
export class HeroModule { }
// #enddocregion class

View File

@ -1,16 +0,0 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HeroComponent } from './hero.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroListComponent } from './hero-list.component';
import { HeroRoutingModule } from './hero-routing.module';
@NgModule({
imports: [ SharedModule, HeroRoutingModule ],
declarations: [
HeroComponent, HeroDetailComponent, HeroListComponent,
]
})
export class HeroModule { }

View File

@ -1,36 +0,0 @@
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
export class Hero {
constructor(public id: number, public name: string) { }
}
const HEROES: Hero[] = [
new Hero(11, 'Mr. Nice'),
new Hero(12, 'Narco'),
new Hero(13, 'Bombasto'),
new Hero(14, 'Celeritas'),
new Hero(15, 'Magneta'),
new Hero(16, 'RubberMan')
];
const FETCH_LATENCY = 500;
/** Simulate a data service that retrieves heroes from a server */
@Injectable()
export class HeroService implements OnDestroy {
constructor() { console.log('HeroService instance created.'); }
ngOnDestroy() { console.log('HeroService instance destroyed.'); }
getHeroes(): Observable<Hero[]> {
return of(HEROES).pipe(delay(FETCH_LATENCY));
}
getHero(id: number | string): Observable<Hero> {
return of(HEROES.find(hero => hero.id === +id))
.pipe(delay(FETCH_LATENCY));
}
}

View File

@ -1,10 +0,0 @@
// Exact copy of contact.awesome.pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'awesome' })
/** Precede the input string with the word "Awesome " */
export class AwesomePipe implements PipeTransform {
transform(phrase: string) {
return phrase ? 'Awesome ' + phrase : '';
}
}

View File

@ -1,12 +0,0 @@
// Exact copy of contact/highlight.directive except for color and message
import { Directive, ElementRef } from '@angular/core';
@Directive({ selector: '[highlight], input' })
// Highlight the host element or any InputElement in gray
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'lightgray';
console.log(
`* Shared highlight called for ${el.nativeElement.tagName}`);
}
}

View File

@ -1,18 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AwesomePipe } from './awesome.pipe';
import { HighlightDirective } from './highlight.directive';
// #docregion module
@NgModule({
imports: [ CommonModule ],
declarations: [ AwesomePipe, HighlightDirective ],
exports: [ AwesomePipe, HighlightDirective,
CommonModule, FormsModule ]
})
export class SharedModule { }
// #enddocregion module
// #enddocregion

View File

@ -1,8 +0,0 @@
// #docregion
import { Injectable } from '@angular/core';
@Injectable()
/** Dummy version of an authenticated user service */
export class UserService {
userName = 'Sherlock Holmes';
}

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Minimal</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="/">
<title>NgModule Deluxe</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,13 +0,0 @@
// #docplaster
/*
// #docregion
// The browser platform without a compiler
import { platformBrowser } from '@angular/platform-browser';
// The app module factory produced by the static offline compiler
import { AppModuleNgFactory } from './app/app.module.ngfactory';
// Launch with the app module factory.
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
// #enddocregion
*/

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.0';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.1';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.1b';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.2';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,11 +0,0 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.3';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,12 +0,0 @@
// #docregion
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,40 +0,0 @@
{
"description": "NgModule Final",
"files": [
"src/app/app.component.ts",
"src/app/app.module.ts",
"src/app/app-routing.module.ts",
"src/app/contact/contact.component.css",
"src/app/contact/contact.component.html",
"src/app/contact/contact.service.ts",
"src/app/contact/contact.component.ts",
"src/app/contact/contact.module.ts",
"src/app/contact/contact-routing.module.ts",
"src/app/crisis/*.ts",
"src/app/hero/hero-detail.component.ts",
"src/app/hero/hero-list.component.ts",
"src/app/hero/hero.service.ts",
"src/app/hero/hero.component.ts",
"src/app/hero/hero.module.ts",
"src/app/hero/hero-routing.module.ts",
"src/app/core/*.css",
"src/app/core/*.html",
"src/app/core/*.ts",
"src/app/shared/*.css",
"src/app/shared/*.html",
"src/app/shared/*.ts",
"src/main.ts",
"src/styles.css",
"src/index.html"
],
"main": "src/index.html",
"tags": ["NgModule"]
}

View File

@ -5,8 +5,6 @@ import { browser, element, by } from 'protractor';
describe('NgModule-example', function () {
// helpers
const gold = 'rgba(255, 215, 0, 1)';
const powderblue = 'rgba(176, 224, 230, 1)';
const lightgray = 'rgba(239, 238, 237, 1)';
const white = 'rgba(0, 0, 0, 0)';
@ -15,7 +13,7 @@ describe('NgModule-example', function () {
return {
title: element.all(by.tagName('h1')).get(0),
subtitle: element.all(by.css('app-title p i')).get(0),
subtitle: element.all(by.css('app-root p i')).get(0),
contactButton: buttons.get(0),
itemButton: buttons.get(1),
customersButton: buttons.get(2)
@ -67,7 +65,7 @@ describe('NgModule-example', function () {
it('should welcome us', function () {
const commons = getCommonsSectionStruct();
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Miss Marple'));
});
};
}
@ -76,7 +74,7 @@ describe('NgModule-example', function () {
return function() {
it('shows the contact\'s owner', function() {
const contacts = getContactSectionStruct();
expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
expect(contacts.header.getText()).toBe((name || 'Miss Marple') + '\'s Contacts');
});
it('can cycle between contacts', function () {
@ -92,21 +90,22 @@ describe('NgModule-example', function () {
});
});
it('can change an existing contact', function () {
const contacts = getContactSectionStruct();
contacts.input.sendKeys('a');
expect(contacts.input.getCssValue('backgroundColor')).toBe(color);
expect(contacts.contactNameHeader.getText()).toBe('Awesome Yashaa');
});
it('can create a new contact', function () {
const contacts = getContactSectionStruct();
const newContactButton = contacts.newContactButton;
const nextButton = contacts.nextContactButton;
const input = contacts.input;
const saveButton = contacts.saveButton;
newContactButton.click().then(function () {
expect(contacts.validationError.getText()).toBe('Name is required');
contacts.input.sendKeys('John Doe');
expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe');
expect(contacts.validationError.getText()).toBe('');
input.click();
nextButton.click()
expect(contacts.validationError.getText()).toBe('Name is required.');
input.click();
contacts.input.sendKeys('Watson');
saveButton.click()
expect(contacts.contactNameHeader.getText()).toBe('Awesome Watson');
});
});
};

View File

@ -3,7 +3,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<app-title></app-title>
<app-greeting></app-greeting>
<nav>
<a routerLink="contact" routerLinkActive="active">Contact</a>
<a routerLink="items" routerLinkActive="active">Items</a>

View File

@ -1,3 +1,5 @@
// #docplaster
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
@ -7,28 +9,30 @@ import { AppComponent } from './app.component';
/* Feature Modules */
import { ContactModule } from './contact/contact.module';
// #docregion import-for-root
import { CoreModule } from './core/core.module';
import { GreetingModule } from './greeting/greeting.module';
// #enddocregion import-for-root
/* Routing Module */
import { AppRoutingModule } from './app-routing.module';
// #docregion import-for-root
@NgModule({
imports: [
// #enddocregion import-for-root
BrowserModule,
ContactModule,
CoreModule.forRoot({userName: 'Miss Marple'}),
// #docregion import-for-root
GreetingModule.forRoot({userName: 'Miss Marple'}),
// #enddocregion import-for-root
AppRoutingModule
// #docregion import-for-root
],
// #enddocregion import-for-root
providers: [],
declarations: [
AppComponent
],
bootstrap: [AppComponent]
// #docregion import-for-root
})
export class AppModule { }
// #enddocregion import-for-root
export class AppModule { }

View File

@ -0,0 +1,17 @@
.button-group {
display: flex;
flex-direction: row;
}
button {
margin: 1rem 1rem 0 0;
}
input {
padding: .5rem;
margin-left: .5rem;
}
.alert {
color: red;
font-style: italic;
}

View File

@ -1,32 +1,30 @@
<h2>Contact of {{userName}}</h2>
<h2>{{userName}}'s Contacts</h2>
<div *ngIf="msg" class="msg">{{msg}}</div>
<form *ngIf="contacts" (ngSubmit)="onSubmit()" #contactForm="ngForm">
<form *ngIf="contacts" (ngSubmit)="onSubmit()" [formGroup]="contactForm">
<h3 highlight>{{ contact.name | awesome }}</h3>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" required
[(ngModel)]="contact.name"
name="name" #name="ngModel" >
<div [hidden]="name.valid" class="alert alert-danger">
Name is required
<input type="text" formControlName="name" required>
<div *ngIf="!contactForm.controls['name'].valid && contactForm.controls['name'].touched" class="alert">
Name is required.
</div>
</div>
<div class="button-group">
<button type="submit" class="btn btn-default"
[disabled]="!contactForm.form.valid">
[disabled]="!contactForm.valid">
Save</button>
<button type="button" class="btn" (click)="next()"
[disabled]="!contactForm.form.valid">
[disabled]="!contactForm.valid && contactForm.touched">
Next Contact</button>
<button type="button" class="btn" (click)="newContact()">
New Contact</button>
</div>
</form>

View File

@ -1,8 +1,9 @@
// Exact copy except import UserService from core
// Exact copy except import UserService from greeting
import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Contact, ContactService } from './contact.service';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
selector: 'app-contact',
@ -16,15 +17,24 @@ export class ContactComponent implements OnInit {
msg = 'Loading contacts ...';
userName = '';
constructor(private contactService: ContactService, userService: UserService) {
contactForm = this.fb.group({
name: ['', Validators.required]
});
constructor(private contactService: ContactService, userService: UserService, private fb: FormBuilder) {
this.userName = userService.userName;
}
ngOnInit() {
this.setupForm();
}
setupForm() {
this.contactService.getContacts().subscribe(contacts => {
this.msg = '';
this.contacts = contacts;
this.contact = contacts[0];
this.contactForm.get('name').setValue(this.contact.name);
});
}
@ -32,15 +42,18 @@ export class ContactComponent implements OnInit {
let ix = 1 + this.contacts.indexOf(this.contact);
if (ix >= this.contacts.length) { ix = 0; }
this.contact = this.contacts[ix];
console.log(this.contacts[ix]);
}
onSubmit() {
// POST-DEMO TODO: do something like save it
this.displayMessage('Saved ' + this.contact.name);
let newName = this.contactForm.get('name').value;
this.displayMessage('Saved ' + newName);
this.contact.name = newName;
}
newContact() {
this.displayMessage('New contact');
this.contactForm.get('name').setValue('');
this.contact = {id: 42, name: ''};
this.contacts.push(this.contact);
}
@ -51,4 +64,3 @@ export class ContactComponent implements OnInit {
setTimeout(() => this.msg = '', 1500);
}
}

View File

@ -1,5 +1,6 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { ReactiveFormsModule } from '@angular/forms';
import { ContactComponent } from './contact.component';
import { ContactService } from './contact.service';
@ -8,7 +9,8 @@ import { ContactRoutingModule } from './contact-routing.module';
@NgModule({
imports: [
SharedModule,
ContactRoutingModule
ContactRoutingModule,
ReactiveFormsModule
],
declarations: [ ContactComponent ],
providers: [ ContactService ]

View File

@ -1,46 +0,0 @@
// #docregion whole-core-module
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TitleComponent } from './title.component';
// #docregion user-service
import { UserService } from './user.service';
// #enddocregion user-service
import { UserServiceConfig } from './user.service';
// #docregion user-service
@NgModule({
// #enddocregion user-service
imports: [ CommonModule ],
declarations: [ TitleComponent ],
exports: [ TitleComponent ],
// #docregion user-service
providers: [ UserService ]
})
export class CoreModule {
// #enddocregion user-service
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error(
'CoreModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
// #docregion user-service
}
// #enddocregion user-service
// #enddocregion whole-core-module

View File

@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { CustomersService } from './customers.service';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
template: `

View File

@ -1,11 +1,11 @@
import { Component, Input } from '@angular/core';
import { UserService } from '../core/user.service';
import { UserService } from '../greeting/user.service';
@Component({
selector: 'app-title',
templateUrl: './title.component.html',
selector: 'app-greeting',
templateUrl: './greeting.component.html',
})
export class TitleComponent {
export class GreetingComponent {
title = 'NgModules';
user = '';

View File

@ -0,0 +1,36 @@
// #docregion whole-greeting-module
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GreetingComponent } from './greeting.component';
import { UserServiceConfig } from './user.service';
@NgModule({
imports: [ CommonModule ],
declarations: [ GreetingComponent ],
exports: [ GreetingComponent ]
})
export class GreetingModule {
// #docregion ctor
constructor (@Optional() @SkipSelf() parentModule: GreetingModule) {
if (parentModule) {
throw new Error(
'GreetingModule is already loaded. Import it in the AppModule only');
}
}
// #enddocregion ctor
// #docregion for-root
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: GreetingModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
// #enddocregion for-root
}
// #enddocregion whole-greeting-module

View File

@ -1,8 +1,3 @@
// Proves that UserService is an app-wide singleton and only instantiated once
// IFF shared.module follows the `forRoot` pattern.
//
// If it didn't, a new instance of UserService would be created
// after each lazy load and the userName would double up.
import { Injectable, Optional } from '@angular/core';
@ -12,7 +7,9 @@ export class UserServiceConfig {
userName = 'Philip Marlowe';
}
@Injectable()
@Injectable({
providedIn: 'root'
})
export class UserService {
id = nextId++;
private _userName = 'Sherlock Holmes';

View File

@ -6,16 +6,16 @@ which that injector uses to provide the concrete, runtime version of a dependenc
The injector relies on the provider configuration to create instances of the dependencies
that it injects into components, directives, pipes, and other services.
You must configure an injector with a provider, or it won't know how to create the dependency.
The most obvious way for an injector to create an instance of a service class is with the class itself.
If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with `new`.
You must configure an injector with a provider, or it won't know how to create the dependency.
The most obvious way for an injector to create an instance of a service class is with the class itself.
If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with `new`.
In the following typical example, the `Logger` class itself provides a `Logger` instance.
In the following typical example, the `Logger` class itself provides a `Logger` instance.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example>
You can, however, configure an injector with an alternative provider,
You can, however, configure an injector with an alternative provider,
in order to deliver some other object that provides the needed logging functionality.
For instance:
* You can provide a substitute class.
@ -43,10 +43,10 @@ The expanded provider configuration is an object literal with two properties.
* The `provide` property holds the [token](guide/dependency-injection#token)
that serves as the key for both locating a dependency value and configuring the injector.
* The second property is a provider definition object, which tells the injector how to create the dependency value.
* The second property is a provider definition object, which tells the injector how to create the dependency value.
The provider-definition key can be `useClass`, as in the example.
It can also be `useExisting`, `useValue`, or `useFactory`.
Each of these keys provides a different type of dependency, as discussed below.
Each of these keys provides a different type of dependency, as discussed below.
{@a class-provider}
@ -82,7 +82,7 @@ The injector needs providers for both this new logging service and its dependent
Suppose an old component depends upon the `OldLogger` class.
`OldLogger` has the same interface as `NewLogger`, but for some reason
you can't update the old component to use it.
you can't update the old component to use it.
When the old component logs a message with `OldLogger`,
you want the singleton instance of `NewLogger` to handle it instead.
@ -90,7 +90,7 @@ In this case, the dependency injector should inject that singleton instance
when a component asks for either the new or the old logger.
`OldLogger` should be an *alias* for `NewLogger`.
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example>
@ -104,8 +104,8 @@ To make sure there is only one instance of `NewLogger`, alias `OldLogger` with t
## Value providers
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
To inject an object you have already created,
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
To inject an object you have already created,
configure the injector with the `useValue` option
The following code defines a variable that creates such an object to play the logger role.
@ -137,9 +137,9 @@ They can be object literals, as shown in the following example.
**TypeScript interfaces are not valid tokens**
The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
Unfortunately, you cannot use a TypeScript interface as a token.
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example>
@ -194,7 +194,7 @@ it supports typing of the configuration object within the class.
## Factory providers
Sometimes you need to create a dependent value dynamically,
based on information you won't have until run time.
based on information you won't have until run time.
For example, you might need information that changes repeatedly in the course of the browser session.
Also, your injectable service might not have independent access to the source of the information.
@ -240,7 +240,7 @@ The injector resolves these tokens and injects the corresponding services into t
Notice that you captured the factory provider in an exported variable, `heroServiceProvider`.
This extra step makes the factory provider reusable.
You can configure a provider of `HeroService` with this variable wherever you need it.
You can configure a provider of `HeroService` with this variable wherever you need it.
In this sample, you need it only in `HeroesComponent`,
where `heroServiceProvider` replaces `HeroService` in the metadata `providers` array.
@ -258,10 +258,10 @@ The following shows the new and the old implementations side-by-side.
## Predefined tokens and multiple providers
Angular provides a number of built-in injection-token constants that you can use to customize the behavior of
various systems.
Angular provides a number of built-in injection-token constants that you can use to customize the behavior of
various systems.
For example, you can use the following built-in tokens as hooks into the frameworks bootstrapping and initialization process.
For example, you can use the following built-in tokens as hooks into the frameworks bootstrapping and initialization process.
A provider object can associate any of these injection tokens with one or more callback functions that take app-specific initialization actions.
* [PLATFORM_INITIALIZER](api/core/PLATFORM_INITIALIZER): Callback is invoked when a platform is initialized.
@ -283,19 +283,19 @@ export const APP_TOKENS = [
];
```
Multiple providers can be associated with a single token in other areas as well.
For example, you can register a custom form validator using the built-in [NG_VALIDATORS](api/forms/NG_VALIDATORS) token,
Multiple providers can be associated with a single token in other areas as well.
For example, you can register a custom form validator using the built-in [NG_VALIDATORS](api/forms/NG_VALIDATORS) token,
and provide multiple instances of a given validator provider by using the `multi: true` property in the provider object.
Angular adds your custom validators to the existing collection.
The Router also makes use of multiple providers associated with a single token.
The Router also makes use of multiple providers associated with a single token.
When you provide multiple sets of routes using [RouterModule.forRoot](api/router/RouterModule#forroot)
and [RouterModule.forChild](api/router/RouterModule#forchild) in a single module,
the [ROUTES](api/router/ROUTES) token combines all the different provided sets of routes into a single value.
<div class="alert is-helpful>
Search for [Constants in API documentation](api?type=const) to find more built-in tokens.
Search for [Constants in API documentation](api?type=const) to find more built-in tokens.
</div>
@ -304,19 +304,19 @@ Search for [Constants in API documentation](api?type=const) to find more built-i
## Tree-shakable providers
Tree shaking refers to a compiler option that removes code from the final bundle if that code not referenced in an application.
Tree shaking refers to a compiler option that removes code from the final bundle if the app doesn't reference that code.
When providers are tree-shakable, the Angular compiler removes the associated
services from the final output when it determines that they are not used in your application.
services from the final output when it determines that your application doesn't use those services.
This significantly reduces the size of your bundles.
<div class="alert is-helpful">
Ideally, if an application isn't injecting a service, it shouldn't be included in the final output.
However, Angular has to be able to identify at build time whether the service will be required or not.
Because it's always possible to inject a service directly using `injector.get(Service)`,
Angular can't identify all of the places in your code where this injection could happen,
so it has no choice but to include the service in the injector.
Thus, services provided at the NgModule or component level are not tree-shakable.
Ideally, if an application isn't injecting a service, Angular shouldn't include it in the final output.
However, Angular has to be able to identify at build time whether the app will require the service or not.
Because it's always possible to inject a service directly using `injector.get(Service)`,
Angular can't identify all of the places in your code where this injection could happen,
so it has no choice but to include the service in the injector.
Thus, services in the NgModule `providers` array or at component level are not tree-shakable.
</div>
@ -324,9 +324,9 @@ The following example of non-tree-shakable providers in Angular configures a ser
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" header="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
This module can then be imported into your application module
to make the service available for injection in your app,
as shown in the following example.
You can then import this module into your application module
to make the service available for injection in your app,
as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" header="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
@ -350,4 +350,4 @@ The service can be instantiated by configuring a factory function, as in the fol
To override a tree-shakable provider, configure the injector of a specific NgModule or component with another provider, using the `providers: []` array syntax of the `@NgModule()` or `@Component()` decorator.
</div>
</div>

View File

@ -217,6 +217,7 @@ knows that the route list is only responsible for providing additional routes an
`forRoot()` contains injector configuration which is global; such as configuring the Router. `forChild()` has no injector configuration, only directives such as `RouterOutlet` and `RouterLink`.
For more information, see the [`forRoot()` deep dive](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
<hr>

View File

@ -204,20 +204,18 @@ Apps pass a `Routes` object to `RouterModule.forRoot()` in order to configure th
`RouterModule.forRoot()` returns a [ModuleWithProviders](api/core/ModuleWithProviders).
You add that result to the `imports` list of the root `AppModule`.
Only call and import a `.forRoot()` result in the root application module, `AppModule`.
Importing it in any other module, particularly in a lazy-loaded module,
is contrary to the intent and will likely produce a runtime error.
For more information, see [Singleton Services](guide/singleton-services).
Only call and import a `forRoot()` result in the root application module, `AppModule`.
Avoid importing it in any other module, particularly in a lazy-loaded module. For more
information on `forRoot()` see [the `forRoot()` pattern](guide/singleton-services#the-forroot-pattern) section of the [Singleton Services](guide/singleton-services) guide.
For a service, instead of using `forRoot()`, specify `providedIn: 'root'` on the service's `@Injectable()` decorator, which
For a service, instead of using `forRoot()`, specify `providedIn: 'root'` on the service's `@Injectable()` decorator, which
makes the service automatically available to the whole application and thus singleton by default.
`RouterModule` also offers a `forChild` static method for configuring the routes of lazy-loaded modules.
`RouterModule` also offers a `forChild()` static method for configuring the routes of lazy-loaded modules.
`forRoot()` and `forChild()` are conventional names for methods that
configure services in root and feature modules respectively.
Angular doesn't recognize these names but Angular developers do.
Follow this convention when you write similar modules with configurable service providers.
@ -384,7 +382,7 @@ This means that lazy-loaded modules can't reach them.
Providers should be configured using `@Injectable` syntax. If possible, they should be provided in the application root (`providedIn: 'root'`). Services that are configured this way are lazily loaded if they are only used from a lazily loaded context.
If it's the consumer's decision whether a provider is available application-wide or not,
If it's the consumer's decision whether a provider is available application-wide or not,
then register providers in modules (`@NgModule.providers`) instead of registering in components (`@Component.providers`).
Register a provider with a component when you _must_ limit the scope of a service instance
@ -478,9 +476,9 @@ from the root app injector. If the injection succeeds, the class has been loaded
You can throw an error or take other remedial action.
Certain NgModules, such as `BrowserModule`, implement such a guard.
Here is a custom constructor for an NgModule called `CoreModule`.
Here is a custom constructor for an NgModule called `GreetingModule`.
<code-example path="ngmodule-faq/src/app/core/core.module.ts" region="ctor" header="src/app/core/core.module.ts (Constructor)" linenums="false">
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)" linenums="false">
</code-example>
<hr/>
@ -591,19 +589,6 @@ Nor should any of its imported or re-exported modules have `providers`.
Import the `SharedModule` in your _feature_ modules,
both those loaded when the app starts and those you lazy load later.
### `CoreModule`
`CoreModule` is a conventional name for an `NgModule` with `providers` for
the singleton services you load when the application starts.
Import `CoreModule` in the root `AppModule` only.
Never import `CoreModule` in any other module.
Consider making `CoreModule` a pure services module
with no `declarations`.
For more information, see [Sharing NgModules](guide/sharing-ngmodules)
and [Singleton Services](guide/singleton-services).
### Feature Modules
Feature modules are modules you create around specific application business domains, user workflows, and utility collections. They support your app by containing a particular feature,

View File

@ -23,18 +23,18 @@ This command creates the following `UserService` skeleton:
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.0.ts" linenums="false"> </code-example>
You can now inject `UserService` anywhere in your application.
You can now inject `UserService` anywhere in your application.
The service itself is a class that the CLI generated and that's decorated with `@Injectable`. By default, this decorator is configured with a `providedIn` property, which creates a provider for the service. In this case, `providedIn: 'root'` specifies that the service should be provided in the root injector.
The service itself is a class that the CLI generated and that's decorated with `@Injectable()`. By default, this decorator has a `providedIn` property, which creates a provider for the service. In this case, `providedIn: 'root'` specifies that Angular should provide the service in the root injector.
## Provider scope
When you add a service provider to the root application injector, its available throughout the app. Additionally, these providers are also available to all the classes in the app as long they have the lookup token.
When you add a service provider to the root application injector, its available throughout the app. Additionally, these providers are also available to all the classes in the app as long they have the lookup token.
You should always provide your service in the root injector unless there is a case where you want the service to be available only if the consumer imports a particular `@NgModule`.
## providedIn and NgModules
## `providedIn` and NgModules
It's also possible to specify that a service should be provided in a particular `@NgModule`. For example, if you don't want `UserService` to be available to applications unless they import a `UserModule` you've created, you can specify that the service should be provided in the module:

View File

@ -96,7 +96,7 @@ When the browser's URL changes, that router looks for a corresponding `Route`
from which it can determine the component to display.
A router has no routes until you configure it.
The following example creates five route definitions, configures the router via the `RouterModule.forRoot` method,
The following example creates five route definitions, configures the router via the `RouterModule.forRoot()` method,
and adds the result to the `AppModule`'s `imports` array.
@ -110,7 +110,7 @@ and adds the result to the `AppModule`'s `imports` array.
The `appRoutes` array of *routes* describes how to navigate.
Pass it to the `RouterModule.forRoot` method in the module `imports` to configure the router.
Pass it to the `RouterModule.forRoot()` method in the module `imports` to configure the router.
Each `Route` maps a URL `path` to a component.
There are _no leading slashes_ in the _path_.
@ -148,8 +148,8 @@ If you need to see what events are happening during the navigation lifecycle, th
### Router outlet
The `RouterOutlet` is a directive from the router library that is used like a component.
It acts as a placeholder that marks the spot in the template where the router should
The `RouterOutlet` is a directive from the router library that is used like a component.
It acts as a placeholder that marks the spot in the template where the router should
display the components for that outlet.
@ -199,9 +199,9 @@ The `RouterLinkActive` directive toggles css classes for active `RouterLink` bin
On each anchor tag, you see a [property binding](guide/template-syntax#property-binding) to the `RouterLinkActive` directive that look like `routerLinkActive="..."`.
The template expression to the right of the equals (=) contains a space-delimited string of CSS classes
that the Router will add when this link is active (and remove when the link is inactive). You set the `RouterLinkActive`
directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` or bind it to a component
property that returns such a string.
that the Router will add when this link is active (and remove when the link is inactive). You set the `RouterLinkActive`
directive to a string of classes such as `[routerLinkActive]="'active fluffy'"` or bind it to a component
property that returns such a string.
Active route links cascade down through each level of the route tree, so parent and child router links can be active at the same time. To override this behavior, you can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL.
@ -912,10 +912,11 @@ In order to use the Router, you must first register the `RouterModule` from the
<div class="alert is-important">
**Note:** The `RouterModule.forRoot` method is a pattern used to register application-wide providers. Read more about application-wide providers in the [Singleton services](guide/singleton-services#forroot) guide.
**Note:** The `RouterModule.forRoot` method is a pattern used to register application-wide providers. Read more about application-wide providers in the [Singleton services](guide/singleton-services#forRoot-router) guide.
</div>
<code-example path="router/src/app/app.module.1.ts" linenums="false" header="src/app/app.module.ts (first-config)" region="first-config">
</code-example>
@ -1071,7 +1072,7 @@ You've learned how to do the following:
* Load the router library.
* Add a nav bar to the shell template with anchor tags, `routerLink` and `routerLinkActive` directives.
* Add a `router-outlet` to the shell template where views will be displayed.
* Configure the router module with `RouterModule.forRoot`.
* Configure the router module with `RouterModule.forRoot()`.
* Set the router to compose HTML5 browser URLs.
* handle invalid routes with a `wildcard` route.
* navigate to the default route when the app launches with an empty path.
@ -1147,7 +1148,7 @@ The starter app's structure looks like this:
hero-list.component.ts
</div>
</div>
<div class='file'>
@ -1173,7 +1174,7 @@ The starter app's structure looks like this:
page-not-found.component.ts
</div>
</div>
<div class='file'>
@ -1280,9 +1281,8 @@ The **Routing Module** has several characteristics:
### Integrate routing with your app
The sample routing application does not include routing by default.
When you create a new workspace and initial application, the [Angular CLI](cli) [`ng new`](cli/new) command prompts you to add "Angular routing". Enter `Y`.
When you generate a new application using the [`ng generate app`](cli/generate) command, specify the `--routing` option. These options tell the CLI to include the `@angular/router` npm package and create a file named `app-routing.module.ts`.
When you use the [Angular CLI](cli) to create a project that will use routing, set the `--routing` option for the project or app, and for each NgModule.
When you create or initialize a new project (using the CLI [`ng new`](cli/new) command) or a new app (using the [`ng generate app`](cli/generate) command), specify the `--routing` option. This tells the CLI to include the `@angular/router` npm package and create a file named `app-routing.module.ts`.
You can then use routing in any NgModule that you add to the project or app.
For example, the following command generates an NgModule that can use routing.
@ -1307,7 +1307,7 @@ Create an `AppRouting` module in the `/app` folder to contain the routing config
Import the `CrisisListComponent`, `HeroListComponent`, and `PageNotFoundComponent` symbols
just like you did in the `app.module.ts`. Then move the `Router` imports
and routing configuration, including `RouterModule.forRoot`, into this routing module.
and routing configuration, including `RouterModule.forRoot()`, into this routing module.
Re-export the Angular `RouterModule` by adding it to the module `exports` array.
By re-exporting the `RouterModule` here the components declared in `AppModule` will have access to router directives such as `RouterLink` and `RouterOutlet`.
@ -1318,7 +1318,7 @@ After these steps, the file should look like this.
</code-example>
Next, update the `app.module.ts` file, removing `RouterModule.forRoot` in
Next, update the `app.module.ts` file, removing `RouterModule.forRoot()` in
the `imports` array.
<code-example path="router/src/app/app.module.2.ts" header="src/app/app.module.ts">
@ -1427,7 +1427,7 @@ Follow these steps:
* Change the component class name to `HeroListComponent`.
* Change the `selector` to `app-hero-list`.
<div class="alert is-helpful">
Selectors are **not required** for _routed components_ due to the components are dynamically inserted when the page is rendered, but are useful for identifying and targeting them in your HTML element tree.
@ -1501,7 +1501,7 @@ When you're done, you'll have these *hero management* files:
<div class='file'>
hero.service.ts
</div>
</div>
<div class='file'>
hero.ts
@ -1509,7 +1509,7 @@ When you're done, you'll have these *hero management* files:
<div class='file'>
heroes-routing.module.ts
</div>
</div>
<div class='file'>
heroes.module.ts
@ -1551,7 +1551,7 @@ Now that you have routes for the `Heroes` module, register them with the `Router
`RouterModule` _almost_ as you did in the `AppRoutingModule`.
There is a small but critical difference.
In the `AppRoutingModule`, you used the static **`RouterModule.forRoot`** method to register the routes and application level service providers.
In the `AppRoutingModule`, you used the static **`RouterModule.forRoot()`** method to register the routes and application level service providers.
In a feature module you use the static **`forChild`** method.
@ -1559,7 +1559,7 @@ In a feature module you use the static **`forChild`** method.
Only call `RouterModule.forRoot` in the root `AppRoutingModule`
Only call `RouterModule.forRoot()` in the root `AppRoutingModule`
(or the `AppModule` if that's where you register top level application routes).
In any other module, you must call the **`RouterModule.forChild`** method to register additional routes.
@ -2243,7 +2243,7 @@ This file does the following:
You could also create more transitions for other routes. This trigger is sufficient for the current milestone.
Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInAnimation` from
Back in the `AppComponent`, import the `RouterOutlet` token from the `@angular/router` package and the `slideInAnimation` from
`'./animations.ts`.
Add an `animations` array to the `@Component` metadata's that contains the `slideInAnimation`.
@ -2325,7 +2325,7 @@ After these changes, the folder structure looks like this:
crisis-list.component.ts
</div>
</div>
</div>
<div class='file'>
heroes
@ -2375,7 +2375,7 @@ After these changes, the folder structure looks like this:
<div class='file'>
hero.service.ts
</div>
</div>
<div class='file'>
hero.ts
@ -2383,7 +2383,7 @@ After these changes, the folder structure looks like this:
<div class='file'>
heroes-routing.module.ts
</div>
</div>
<div class='file'>
heroes.module.ts
@ -2418,7 +2418,7 @@ After these changes, the folder structure looks like this:
page-not-found.component.ts
</div>
</div>
</div>
@ -2453,7 +2453,7 @@ After these changes, the folder structure looks like this:
<div class='file'>
message.service.ts
</div>
</div>
<div class='file'>
index.html
@ -2487,7 +2487,7 @@ Here are the relevant files for this version of the sample application.
<code-pane header="animations.ts" path="router/src/app/animations.ts">
</code-pane>
</code-pane>
<code-pane header="app.component.html" path="router/src/app/app.component.2.html">
@ -2507,11 +2507,11 @@ Here are the relevant files for this version of the sample application.
<code-pane header="hero-list.component.css" path="router/src/app/heroes/hero-list/hero-list.component.css">
</code-pane>
</code-pane>
<code-pane header="hero-list.component.html" path="router/src/app/heroes/hero-list/hero-list.component.html">
</code-pane>
</code-pane>
<code-pane header="hero-list.component.ts" path="router/src/app/heroes/hero-list/hero-list.component.ts">
@ -2539,7 +2539,7 @@ Here are the relevant files for this version of the sample application.
<code-pane header="message.service.ts" path="router/src/app/message.service.ts">
</code-pane>
</code-pane>
</code-tabs>
@ -2721,7 +2721,7 @@ _before_ the `AppRoutingModule`:
<code-pane path="router/src/app/crisis-center/crisis-center.module.ts"header="src/app/crisis-center/crisis-center.module.ts">
</code-pane>
</code-pane>
<code-pane path="router/src/app/app.module.4.ts" linenums="false" header="src/app/app.module.ts (import CrisisCenterModule)" region="crisis-center-module">
@ -3382,7 +3382,7 @@ update the admin route with a `canActivate` guard property that references it:
</code-example>
The admin feature is now protected by the guard, albeit protected poorly.
@ -3461,7 +3461,7 @@ Register a `/login` route in the `auth/auth-routing.module.ts`. In `app.module.t
<code-pane header="src/app/auth/login/login.component.html" path="router/src/app/auth/login/login.component.html">
</code-pane>
</code-pane>
<code-pane header="src/app/auth/login/login.component.ts" path="router/src/app/auth/login/login.component.1.ts">
@ -4131,9 +4131,9 @@ You could try this now and confirm that the `CrisisCenterModule` loads after yo
To enable preloading of all lazy loaded modules, import the `PreloadAllModules` token from the Angular router package.
The second argument in the `RouterModule.forRoot` method takes an object for additional configuration options.
The second argument in the `RouterModule.forRoot()` method takes an object for additional configuration options.
The `preloadingStrategy` is one of those options.
Add the `PreloadAllModules` token to the `forRoot` call:
Add the `PreloadAllModules` token to the `forRoot()` call:
<code-example path="router/src/app/app-routing.module.6.ts" linenums="false" header="src/app/app-routing.module.ts (preload all)" region="forRoot">
@ -4220,7 +4220,7 @@ Shortly, you'll extend the `AdminDashboardComponent` to inject this service and
But first, make a few changes to the `AppRoutingModule`.
1. Import `SelectivePreloadingStrategyService` into `AppRoutingModule`.
1. Replace the `PreloadAllModules` strategy in the call to `forRoot` with this `SelectivePreloadingStrategyService`.
1. Replace the `PreloadAllModules` strategy in the call to `forRoot()` with this `SelectivePreloadingStrategyService`.
1. Add the `SelectivePreloadingStrategyService` strategy to the `AppRoutingModule` providers array so it can be injected
elsewhere in the app.
@ -4477,7 +4477,7 @@ The router supports both styles with two `LocationStrategy` providers:
1. `PathLocationStrategy`&mdash;the default "HTML5 pushState" style.
1. `HashLocationStrategy`&mdash;the "hash URL" style.
The `RouterModule.forRoot` function sets the `LocationStrategy` to the `PathLocationStrategy`,
The `RouterModule.forRoot()` function sets the `LocationStrategy` to the `PathLocationStrategy`,
making it the default strategy.
You can switch to the `HashLocationStrategy` with an override during the bootstrapping process if you prefer it.
@ -4592,7 +4592,7 @@ Those developers may still use HTML5 URLs by taking two remedial steps:
#### *HashLocationStrategy*
You can go old-school with the `HashLocationStrategy` by
providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot`
providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot()`
in the `AppModule`.

View File

@ -14,46 +14,99 @@ For a sample app using the app-wide singleton service that this page describes,
There are two ways to make a service a singleton in Angular:
* Declare that the service should be provided in the application root.
* Include the service in the `AppModule` or in a module that is only imported by the `AppModule`.
* Declare `root` for the value of the `@Injectable()` `providedIn` property
* Include the service in the `AppModule` or in a module that is only imported by the `AppModule`
Beginning with Angular 6.0, the preferred way to create a singleton service is to specify on the service that it should be provided in the application root. This is done by setting `providedIn` to `root` on the service's `@Injectable` decorator:
{@a providedIn}
### Using `providedIn`
Beginning with Angular 6.0, the preferred way to create a singleton service is to set `providedIn` to `root` on the service's `@Injectable()` decorator. This tells Angular
to provide the service in the application root.
<code-example path="providers/src/app/user.service.0.ts" header="src/app/user.service.0.ts" linenums="false"> </code-example>
For more detailed information on services, see the [Services](tutorial/toh-pt4) chapter of the
[Tour of Heroes tutorial](tutorial).
### NgModule `providers` array
## `forRoot()`
In apps built with Angular versions prior to 6.0, services are registered NgModule `providers` arrays as follows:
If a module provides both providers and declarations (components, directives, pipes) then loading it in a child injector such as a route, would duplicate the provider instances. The duplication of providers would cause issues as they would shadow the root instances, which are probably meant to be singletons. For this reason Angular provides a way to separate providers out of the module so that same module can be imported into the root module with `providers` and child modules without `providers`.
```ts
@NgModule({
...
providers: [UserService],
...
})
1. Create a static method `forRoot()` (by convention) on the module.
2. Place the providers into the `forRoot` method as follows.
```
<!-- MH: show a simple example how to do that without going to deep into it. -->
If this NgModule were the root `AppModule`, the `UserService` would be a singleton and available
throughout the app. Though you may see it coded this way, using the `providedIn` property of the `@Injectable()` decorator on the service itself is preferable as of Angular 6.0 as it makes your services tree-shakable.
To make this more concrete, consider the `RouterModule` as an example. `RouterModule` needs to provide the `Router` service, as well as the `RouterOutlet` directive. `RouterModule` has to be imported by the root application module so that the application has a `Router` and the application has at least one `RouterOutlet`. It also must be imported by the individual route components so that they can place `RouterOutlet` directives into their template for sub-routes.
{@a forRoot}
If the `RouterModule` didnt have `forRoot()` then each route component would instantiate a new `Router` instance, which would break the application as there can only be one `Router`. For this reason, the `RouterModule` has the `RouterOutlet` declaration so that it is available everywhere, but the `Router` provider is only in the `forRoot()`. The result is that the root application module imports `RouterModule.forRoot(...)` and gets a `Router`, whereas all route components import `RouterModule` which does not include the `Router`.
## The `forRoot()` pattern
If you have a module which provides both providers and declarations, use this pattern to separate them out.
Generally, you'll only need `providedIn` for providing services and `forRoot()`/`forChild()` for routing. However, understanding how `forRoot()` works to make sure a service is a singleton will inform your development at a deeper level.
A module that adds providers to the application can offer a
facility for configuring those providers as well through the
`forRoot()` method.
If a module defines both providers and declarations (components, directives, pipes),
then loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton.
There are multiple ways to prevent this:
* Use the [`providedIn` syntax](guide/singleton-services#providedIn) instead of registering the service in the module.
* Separate your services into their own module.
* Define `forRoot()` and `forChild()` methods in the module.
<div class="alert is-helpful">
**Note:** There are two example apps where you can see this scenario; the more advanced <live-example noDownload>NgModules live example</live-example>, which contains `forRoot()` and `forChild()` in the routing modules and the `GreetingModule`, and the simpler <live-example name="lazy-loading-ngmodules" noDownload>Lazy Loading live example</live-example>. For an introductory explanation see the [Lazy Loading Feature Modules](guide/lazy-loading-ngmodules) guide.
</div>
Use `forRoot()` to
separate providers from a module so you can import that module into the root module
with `providers` and child modules without `providers`.
1. Create a static method `forRoot()` on the module.
2. Place the providers into the `forRoot()` method.
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts" linenums="false"> </code-example>
{@a forRoot-router}
### `forRoot()` and the `Router`
`RouterModule` provides the `Router` service, as well as router directives, such as `RouterOutlet` and `routerLink`. The root application module imports `RouterModule` so that the application has a `Router` and the root application components can access the router directives. Any feature modules must also import `RouterModule` so that their components can place router directives into their templates.
If the `RouterModule` didnt have `forRoot()` then each feature module would instantiate a new `Router` instance, which would break the application as there can only be one `Router`. By using the `forRoot()` method, the root application module imports `RouterModule.forRoot(...)` and gets a `Router`, and all feature modules import `RouterModule.forChild(...)` which does not instantiate another `Router`.
<div class="alert is-helpful">
**Note:** If you have a module which has both providers and declarations,
you _can_ use this
technique to separate them out and you may see this pattern in legacy apps.
However, since Angular 6.0, the best practice for providing services is with the
`@Injectable()` `providedIn` property.
</div>
### How `forRoot()` works
`forRoot()` takes a service configuration object and returns a
[ModuleWithProviders](api/core/ModuleWithProviders), which is
a simple object with the following properties:
* `ngModule`: in this example, the `CoreModule` class.
* `providers`: the configured providers.
* `ngModule`: in this example, the `GreetingModule` class
* `providers`: the configured providers
In the <live-example name="ngmodules">live example</live-example>
the root `AppModule` imports the `CoreModule` and adds the
the root `AppModule` imports the `GreetingModule` and adds the
`providers` to the `AppModule` providers. Specifically,
Angular accumulates all imported providers
before appending the items listed in `@NgModule.providers`.
@ -61,25 +114,26 @@ This sequence ensures that whatever you add explicitly to
the `AppModule` providers takes precedence over the providers
of imported modules.
Import `CoreModule` and use its `forRoot()` method one time, in `AppModule`, because it registers services and you only want to register those services one time in your app. If you were to register them more than once, you could end up with multiple instances of the service and a runtime error.
The sample app imports `GreetingModule` and uses its `forRoot()` method one time, in `AppModule`. Registering it once like this prevents multiple instances.
You can also add a `forRoot()` method in the `CoreModule` that configures
the core `UserService`.
You can also add a `forRoot()` method in the `GreetingModule` that configures
the greeting `UserService`.
In the following example, the optional, injected `UserServiceConfig`
extends the core `UserService`. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
extends the greeting `UserService`. If a `UserServiceConfig` exists, the `UserService` sets the user name from that config.
<code-example path="ngmodules/src/app/core/user.service.ts" region="ctor" header="src/app/core/user.service.ts (constructor)" linenums="false">
<code-example path="ngmodules/src/app/greeting/user.service.ts" region="ctor" header="src/app/greeting/user.service.ts (constructor)" linenums="false">
</code-example>
Here's `forRoot()` that takes a `UserServiceConfig` object:
<code-example path="ngmodules/src/app/core/core.module.ts" region="for-root" header="src/app/core/core.module.ts (forRoot)" linenums="false">
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="for-root" header="src/app/greeting/greeting.module.ts (forRoot)" linenums="false">
</code-example>
Lastly, call it within the `imports` list of the `AppModule`.
Lastly, call it within the `imports` list of the `AppModule`. In the following
snippet, other parts of the file are left out. For the complete file, see the <live-example name="ngmodules"></live-example>, or continue to the next section of this document.
<code-example path="ngmodules/src/app/app.module.ts" region="import-for-root" header="src/app/app.module.ts (imports)" linenums="false">
@ -87,61 +141,50 @@ Lastly, call it within the `imports` list of the `AppModule`.
The app displays "Miss Marple" as the user instead of the default "Sherlock Holmes".
Remember to _import_ `CoreModule` as a Javascript import at the top of the file; don't add it to more than one `@NgModule` `imports` list.
Remember to import `GreetingModule` as a Javascript import at the top of the file and don't add it to more than one `@NgModule` `imports` list.
<!-- KW--Does this mean that if we need it elsewhere we only import it at the top? I thought the services would all be available since we were importing it into `AppModule` in `providers`. -->
## Prevent reimport of the `GreetingModule`
## Prevent reimport of the `CoreModule`
Only the root `AppModule` should import the `CoreModule`. If a
Only the root `AppModule` should import the `GreetingModule`. If a
lazy-loaded module imports it too, the app can generate
[multiple instances](guide/ngmodule-faq#q-why-bad) of a service.
To guard against a lazy-loaded module re-importing `CoreModule`, add the following `CoreModule` constructor.
To guard against a lazy loaded module re-importing `GreetingModule`, add the following `GreetingModule` constructor.
<code-example path="ngmodules/src/app/core/core.module.ts" region="ctor" header="src/app/core/core.module.ts" linenums="false">
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts" linenums="false">
</code-example>
The constructor tells Angular to inject the `CoreModule` into itself.
The constructor tells Angular to inject the `GreetingModule` into itself.
The injection would be circular if Angular looked for
`CoreModule` in the _current_ injector. The `@SkipSelf`
decorator means "look for `CoreModule` in an ancestor
`GreetingModule` in the _current_ injector, but the `@SkipSelf()`
decorator means "look for `GreetingModule` in an ancestor
injector, above me in the injector hierarchy."
If the constructor executes as intended in the `AppModule`,
there would be no ancestor injector that could provide an instance of `CoreModule` and the injector should give up.
By default, the injector throws an error when it can't
find a requested provider.
The `@Optional` decorator means not finding the service is OK.
The `@Optional()` decorator means not finding the service is OK.
The injector returns `null`, the `parentModule` parameter is null,
and the constructor concludes uneventfully.
It's a different story if you improperly import `CoreModule` into a lazy-loaded module such as `CustomersModule`.
It's a different story if you improperly import `GreetingModule` into a lazy loaded module such as `CustomersModule`.
Angular creates a lazy-loaded module with its own injector,
a _child_ of the root injector.
`@SkipSelf` causes Angular to look for a `CoreModule` in the parent injector, which this time is the root injector.
Angular creates a lazy loaded module with its own injector,
a child of the root injector.
`@SkipSelf()` causes Angular to look for a `GreetingModule` in the parent injector, which this time is the root injector.
Of course it finds the instance imported by the root `AppModule`.
Now `parentModule` exists and the constructor throws the error.
Here are the two files in their entirety for reference:
<code-tabs linenums="false">
<code-pane
header="app.module.ts"
path="ngmodules/src/app/app.module.ts">
<code-pane header="app.module.ts" path="ngmodules/src/app/app.module.ts">
</code-pane>
<code-pane
header="core.module.ts"
region="whole-core-module"
path="ngmodules/src/app/core/core.module.ts">
<code-pane header="greeting.module.ts" region="whole-greeting-module" path="ngmodules/src/app/greeting/greeting.module.ts">
</code-pane>
</code-tabs>
<hr>
<hr />
## More on NgModules

File diff suppressed because it is too large Load Diff