This commit is contained in:
YuCheng Hu 2021-04-03 09:50:33 -04:00
parent eff474b5b1
commit 3d12ab7d35
278 changed files with 683 additions and 1835 deletions

View File

@ -11,8 +11,6 @@ table {
margin:20px;
border:#ccc 1px solid;
-moz-border-radius:3px;
-webkit-border-radius:3px;
border-radius:3px;
}
table th {
@ -46,12 +44,8 @@ table tr:last-child td {
border-bottom:0;
}
table tr:last-child td:first-child {
-moz-border-radius-bottomleft:3px;
-webkit-border-bottom-left-radius:3px;
border-bottom-left-radius:3px;
}
table tr:last-child td:last-child {
-moz-border-radius-bottomright:3px;
-webkit-border-bottom-right-radius:3px;
border-bottom-right-radius:3px;
}

View File

@ -67,9 +67,7 @@
<td>{{movie.hero}}</td>
<td>{{movie.releaseDate | date}}</td>
<td>{{movie.mpaa | uppercase}}</td>
<!-- #docregion currency -->
<td>{{movie.price | currency:'USD':true}}</td>
<!-- #enddocregion currency -->
<td>{{movie.starRating | number:'1.1-2'}}</td>
<td>{{movie.approvalRating | percent: '1.0-0'}}</td>
</tr>

View File

@ -1,8 +1,6 @@
/* tslint:disable:no-unused-variable */
// #docplaster
// #docregion import
import { Component } from '@angular/core';
// #enddocregion import
import { IMovie } from './movie';
import { MovieService } from './movie.service';

View File

@ -1,18 +1,6 @@
// #docplaster
// #docregion imports
import { Component, HostBinding } from '@angular/core';
import {
trigger,
state,
style,
animate,
transition,
// ...
} from '@angular/animations';
// #enddocregion imports
// #docregion decorator, toggle-app-animations
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
@ -21,15 +9,10 @@ import {
// animation triggers go here
]
})
// #enddocregion decorator
export class AppComponent {
@HostBinding('@.disabled')
public animationsDisabled = false;
// #enddocregion toggle-app-animations
@HostBinding('@.disabled') public animationsDisabled = false;
toggleAnimations() {
this.animationsDisabled = !this.animationsDisabled;
}
// #docregion toggle-app-animations
}
// #enddocregion toggle-app-animations

View File

@ -16,7 +16,6 @@ import { Hero } from './hero';
@Component({
selector: 'app-hero-list-enter-leave',
// #docregion template
template: `
<ul class="heroes">
<li *ngFor="let hero of heroes"
@ -28,7 +27,6 @@ import { Hero } from './hero';
</li>
</ul>
`,
// #enddocregion template
styleUrls: ['./hero-list-page.component.css'],
// #docregion animationdef
animations: [

View File

@ -62,8 +62,7 @@
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
font-family: Arial;
font-family: Arial, sans-serif;
}
button:hover {

View File

@ -1,29 +1,12 @@
// #docplaster
// #docregion reusable
import { Component } from '@angular/core';
import { useAnimation, transition, trigger, style, animate } from '@angular/animations';
import { transition, trigger, useAnimation } from '@angular/animations';
import { transAnimation } from './animations';
@Component({
// #enddocregion reusable
selector: 'app-open-close-reusable',
// #docregion runtime
animations: [
transition('open => closed', [
style({
height: '200 px',
opacity: '{{ opacity }}',
backgroundcolor: 'yelow'
}),
animate('{{ time }}'),
], {
params: {
time: '1s',
opacity: '1'
}
}),
// #enddocregion runtime
// #docregion reusable
trigger('openClose', [
transition('open => closed', [
useAnimation(transAnimation, {
@ -36,10 +19,9 @@ import { transAnimation } from './animations';
})
])
])
// #enddocregion reusable
],
templateUrl: 'open-close.component.html',
styleUrls: ['open-close.component.css']
// #docregion reusable
})
// #enddocregion reusable
export class OpenCloseReusableComponent { }

View File

@ -1,13 +1,10 @@
<!-- #docplaster -->
<nav>
<button (click)="toggle()">Toggle Open/Close</button>
</nav>
<!-- #docregion compare, trigger -->
<div [@openClose]="isOpen ? 'open' : 'closed'"
(@openClose.start)="onAnimationEvent($event)"
(@openClose.done)="onAnimationEvent($event)"
class="open-close-container">
<p>The box is now {{ isOpen ? 'Open' : 'Closed' }}!</p>
</div>
<!-- #enddocregion compare, trigger -->

View File

@ -5,7 +5,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
// #docregion component, events1
@Component({
selector: 'app-open-close',
// #docregion trigger, trigger-wildcard1, trigger-transition
// #docregion trigger-wildcard1, trigger-transition
animations: [
trigger('openClose', [
// #docregion state1
@ -33,7 +33,7 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
transition('closed => open', [
animate('0.5s')
]),
// #enddocregion transition2, trigger, component
// #enddocregion transition2, component
// #docregion trigger-wildcard1
transition('* => closed', [
animate('1s')
@ -54,15 +54,14 @@ import { trigger, transition, state, animate, style, AnimationEvent } from '@ang
),
]),
// #enddocregion transition4
// #docregion transition3
transition('* => *', [
animate('1s')
]),
// #enddocregion transition3, trigger-transition
// #docregion trigger, component, trigger-wildcard1, events1
// #enddocregion trigger-transition
// #docregion component, trigger-wildcard1, events1
]),
],
// #enddocregion trigger, trigger-wildcard1
// #enddocregion trigger-wildcard1
templateUrl: 'open-close.component.html',
styleUrls: ['open-close.component.css']
})

View File

@ -9,6 +9,4 @@ import { Component } from '@angular/core';
<app-sales-tax></app-sales-tax>
`
})
// #docregion export
export class AppComponent { }
// #enddocregion export

View File

@ -22,13 +22,11 @@ import { Logger } from './logger.service';
HeroListComponent,
SalesTaxComponent
],
// #docregion providers
providers: [
BackendService,
HeroService,
Logger
],
// #enddocregion providers
bootstrap: [ AppComponent ]
})
// #docregion export

View File

@ -7,9 +7,7 @@ export class Logger {
log(message: string) { console.log(message); }
}
// #docregion import-core-component
import { Component } from '@angular/core';
// #enddocregion import-core-component
@Component({
selector: 'app-root',
@ -35,9 +33,7 @@ import { BrowserModule } from '@angular/platform-browser';
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
// #docregion export
export class AppModule { }
// #enddocregion export
// #enddocregion module
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

View File

@ -1,7 +1,6 @@
<h1>Attribute, class, and style bindings</h1>
<h2>Attribute binding</h2>
<!-- #docregion attrib-binding-colspan -->
<table border=1>
<!-- #docregion colspan -->
<!-- expression calculates colspan=2 -->
@ -18,7 +17,6 @@
<tr><td>Five</td><td>Six</td></tr>
</table>
<!-- #enddocregion attrib-binding-colspan -->
<div>
<!-- #docregion attrib-binding-aria -->
@ -66,4 +64,6 @@
<comp-with-host-binding dirWithHostBinding></comp-with-host-binding>
<!-- #enddocregion style-delegation -->
<!-- #docregion attribute-decorator -->
<app-my-input-with-attribute-decorator type="number"></app-my-input-with-attribute-decorator>
<!-- #enddocregion attribute-decorator -->

View File

@ -1,15 +1,15 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { CompWithHostBindingComponent } from './comp-with-host-binding.component';
import { MyInputWithAttributeDecoratorComponent } from './my-input-with-attribute-decorator.component';
@NgModule({
declarations: [
AppComponent,
CompWithHostBindingComponent
CompWithHostBindingComponent,
MyInputWithAttributeDecoratorComponent
],
imports: [
BrowserModule

View File

@ -4,11 +4,7 @@
<p appHighlight>Highlight me!</p>
<!-- #enddocregion applied -->
<!-- #docregion color-1 -->
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
<!-- #enddocregion color-1 -->
<!-- #docregion color-2 -->
<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p>
<!-- #enddocregion color-2 -->

View File

@ -1,8 +1,8 @@
/* tslint:disable:no-unused-variable member-ordering */
// #docplaster
// #docregion imports,
// #docregion imports
import { Directive, ElementRef, HostListener } from '@angular/core';
// #enddocregion imports,
// #enddocregion imports
import { Input } from '@angular/core';
// #docregion
@ -10,37 +10,22 @@ import { Input } from '@angular/core';
selector: '[appHighlight]'
})
export class HighlightDirective {
// #docregion ctor
constructor(private el: ElementRef) { }
// #enddocregion ctor
// #docregion mouse-methods, host
constructor(private el: ElementRef) { }
// #docregion mouse-methods
@HostListener('mouseenter') onMouseEnter() {
// #enddocregion host
this.highlight('yellow');
// #docregion host
}
@HostListener('mouseleave') onMouseLeave() {
// #enddocregion host
this.highlight(null);
// #docregion host
}
// #enddocregion host
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
// #enddocregion mouse-methods,
// #enddocregion mouse-methods
// #docregion color
@Input() highlightColor: string;
// #enddocregion color
// #docregion color-2
@Input() appHighlight: string;
// #enddocregion color-2
// #docregion
}
// #enddocregion

View File

@ -10,13 +10,13 @@ export class HighlightDirective {
constructor(private el: ElementRef) { }
@Input('appHighlight') highlightColor: string;
// #docregion input
@Input() appHighlight: string;
// #enddocregion input
// #docregion mouse-enter
@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightColor || 'red');
this.highlight(this.appHighlight || 'red');
}
// #enddocregion mouse-enter
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);

View File

@ -12,9 +12,7 @@ export class HighlightDirective {
@Input() defaultColor: string;
// #enddocregion defaultColor
// #docregion color
@Input('appHighlight') highlightColor: string;
// #enddocregion color
// #docregion mouse-enter
@HostListener('mouseenter') onMouseEnter() {

View File

@ -1,6 +1,4 @@
// #docplaster
// #docregion whole-ngmodule
// imports
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
@ -32,5 +30,3 @@ import { ItemDirective } from './item.directive';
bootstrap: [AppComponent]
})
export class AppModule { }
// #enddocregion whole-ngmodule

View File

@ -59,7 +59,7 @@ img {
.study,
.modified {
font-family: "Brush Script MT";
font-family: "Brush Script MT", cursive;
font-size: 2rem;
}

View File

@ -7,11 +7,8 @@
<fieldset><h4>NgModel examples</h4>
<p>Current item name: {{currentItem.name}}</p>
<p>
<!-- #docregion without-NgModel -->
<label for="without">without NgModel:</label>
<input [value]="currentItem.name" (input)="currentItem.name=$event.target.value" id="without">
<!-- #enddocregion without-NgModel -->
<input [value]="currentItem.name" (input)="currentItem.name=getValue($event.target)" id="without">
</p>
<p>
@ -27,10 +24,8 @@
</p>
<p>
<!-- #docregion NgModelChange -->
<label for="example-change">(ngModelChange)="...name=$event":</label>
<input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">
<!-- #enddocregion NgModelChange -->
</p>
<p>
@ -77,12 +72,9 @@
<!-- NgStyle binding -->
<hr><h3>NgStyle Binding</h3>
<!-- #docregion without-ng-style -->
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
This div is x-large or smaller.
</div>
<!-- #enddocregion without-ng-style -->
<h4>[ngStyle] binding to currentStyles - CSS property names</h4>
<p>currentStyles is {{currentStyles | json}}</p>
@ -134,7 +126,6 @@
<hr>
<h4>Show/hide vs. NgIf</h4>
<!-- #docregion NgIf-3 -->
<!-- isSpecial is true -->
<div [class.hidden]="!isSpecial">Show with class</div>
<div [class.hidden]="isSpecial">Hide with class</div>
@ -144,7 +135,6 @@
<div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
<!-- #enddocregion NgIf-3 -->
<hr>

View File

@ -21,7 +21,10 @@ export class AppComponent implements OnInit {
item: Item; // defined to demonstrate template context precedence
items: Item[];
// #docregion item
currentItem: Item;
// #enddocregion item
// trackBy change counting
@ -111,6 +114,9 @@ export class AppComponent implements OnInit {
trackById(index: number, item: any): number { return item.id; }
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
}
}

View File

@ -5,9 +5,13 @@ import { Item } from './item';
selector: 'app-stout-item',
template: `I'm a little {{item.name}}, short and stout!`
})
// #docregion input
export class StoutItemComponent {
@Input() item: Item;
}
// #enddocregion input
@Component({
selector: 'app-best-item',

View File

@ -1,7 +1,6 @@
import { Component, Input } from '@angular/core';
import { Hero } from './hero';
// #docregion styleurls
@Component({
selector: 'app-hero-details',
template: `
@ -12,7 +11,5 @@ import { Hero } from './hero';
styleUrls: ['./hero-details.component.css']
})
export class HeroDetailsComponent {
// #enddocregion styleurls
@Input() hero: Hero;
// #docregion styleurls
}

View File

@ -5,10 +5,8 @@ import { Component, ViewEncapsulation } from '@angular/core';
// #docregion
@Component({
selector: 'app-quest-summary',
// #docregion urls
templateUrl: './quest-summary.component.html',
styleUrls: ['./quest-summary.component.css']
// #enddocregion urls
})
export class QuestSummaryComponent { }
// #enddocregion

View File

@ -1,26 +1,18 @@
// #docregion
import { Component } from '@angular/core';
// #docregion import-services
import { LoggerService } from './logger.service';
import { UserContextService } from './user-context.service';
import { UserService } from './user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
// #enddocregion import-services
private userId = 1;
// #docregion ctor
constructor(logger: LoggerService, public userContext: UserContextService) {
userContext.loadUser(this.userId);
logger.logInfo('AppComponent initialized');
}
// #enddocregion ctor
// #docregion import-services
}
// #enddocregion import-services

View File

@ -8,9 +8,7 @@ import { LoggerService } from './logger.service';
@Injectable({
providedIn: 'root'
})
// #docregion date-logger-service-signature
export class DateLoggerService extends LoggerService
// #enddocregion date-logger-service-signature
{
logInfo(msg: any) { super.logInfo(stamp(msg)); }
logDebug(msg: any) { super.logInfo(stamp(msg)); }

View File

@ -17,11 +17,9 @@ import { LoggerService } from './logger.service';
})
export class HeroBiosComponent {
// #enddocregion simple
// #docregion ctor
constructor(logger: LoggerService) {
logger.logInfo('Creating HeroBiosComponent');
}
// #enddocregion ctor
// #docregion simple
}
// #enddocregion simple
@ -36,9 +34,7 @@ export class HeroBiosComponent {
<app-hero-bio [heroId]="2"> <app-hero-contact></app-hero-contact> </app-hero-bio>
<app-hero-bio [heroId]="3"> <app-hero-contact></app-hero-contact> </app-hero-bio>`,
// #enddocregion template
// #docregion class-provider
providers: [HeroService]
// #enddocregion class-provider
})
export class HeroBiosAndContactsComponent {
constructor(logger: LoggerService) {

View File

@ -30,9 +30,7 @@ export class HeroContactComponent {
this.hasLogger = true;
loggerService.logInfo('HeroContactComponent can log!');
}
// #docregion ctor
}
// #enddocregion ctor
get phoneNumber() { return this.heroCache.hero.phone; }

View File

@ -48,13 +48,11 @@ const someHero = new Hero(42, 'Magma', 'Had a great month!', '555-555-5555');
export class HeroOfTheMonthComponent {
logs: string[] = [];
// #docregion ctor-signature
constructor(
logger: MinimalLogger,
public heroOfTheMonth: Hero,
@Inject(RUNNERS_UP) public runnersUp: string,
@Inject(TITLE) public title: string)
// #enddocregion ctor-signature
{
this.logs = logger.logs;
logger.logInfo('starting up');

View File

@ -35,7 +35,6 @@ export function provideTheParent
///////// C - Child //////////
// #docregion carol
const templateC = `
<div class="c">
<h3>{{name}}</h3>
@ -54,7 +53,6 @@ export class CarolComponent {
// #enddocregion carol-ctor
}
// #enddocregion carol-class
// #enddocregion carol
@Component({
selector: 'chris',
@ -132,7 +130,7 @@ export class BethComponent implements Parent {
///////// A - Grandparent //////
// #docregion alex, alex-1
// #docregion alex-1
@Component({
selector: 'alex',
template: `
@ -157,11 +155,10 @@ export class AlexComponent extends Base
{
name = 'Alex';
}
// #enddocregion alex, alex-1
// #enddocregion alex-1
/////
// #docregion alice
@Component({
selector: 'alice',
template: `
@ -182,7 +179,6 @@ export class AliceComponent implements Parent
{
name = 'Alice';
}
// #enddocregion alice
////// Cathy ///////////
/**

View File

@ -5,9 +5,7 @@ import { InjectionToken } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
// #docregion runners-up
export const RUNNERS_UP = new InjectionToken<string>('RunnersUp');
// #enddocregion runners-up
// #docregion factory-synopsis
export function runnersUpFactory(take: number) {

View File

@ -6,7 +6,7 @@ import { Hero } from './hero';
import { HeroService } from './hero.service';
/////// HeroesBaseComponent /////
// #docregion heroes-base, injection
// #docregion heroes-base
@Component({
selector: 'app-unsorted-heroes',
template: `<div *ngFor="let hero of heroes">{{hero.name}}</div>`,
@ -14,7 +14,6 @@ import { HeroService } from './hero.service';
})
export class HeroesBaseComponent implements OnInit {
constructor(private heroService: HeroService) { }
// #enddocregion injection
heroes: Array<Hero>;
@ -26,9 +25,8 @@ export class HeroesBaseComponent implements OnInit {
// Post-process heroes in derived class override.
protected afterGetHeroes() {}
// #docregion injection
}
// #enddocregion heroes-base,injection
// #enddocregion heroes-base
/////// SortedHeroesComponent /////
// #docregion sorted-heroes

View File

@ -1,14 +1,10 @@
// #docregion
import { Inject, Injectable, InjectionToken } from '@angular/core';
// #docregion storage-token
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
providedIn: 'root',
factory: () => localStorage
});
// #enddocregion storage-token
// #docregion inject-storage-token
@Injectable({
providedIn: 'root'
})
@ -31,4 +27,3 @@ export class BrowserStorageService {
this.storage.clear();
}
}
// #enddocregion inject-storage-token

View File

@ -1,27 +1,19 @@
// #docplaster
// #docregion
import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
import { UserService } from './user.service';
// #docregion injectables, injectable
@Injectable({
providedIn: 'root'
})
export class UserContextService {
// #enddocregion injectables, injectable
name: string;
role: string;
loggedInSince: Date;
// #docregion ctor, injectables
constructor(private userService: UserService, private loggerService: LoggerService) {
// #enddocregion ctor, injectables
this.loggedInSince = new Date();
// #docregion ctor, injectables
}
// #enddocregion ctor, injectables
loadUser(userId: number) {
const user = this.userService.getUserById(userId);
@ -30,6 +22,4 @@ export class UserContextService {
this.loggerService.logDebug('loaded User');
}
// #docregion injectables, injectable
}
// #enddocregion injectables, injectable

View File

@ -1,9 +1,6 @@
// #docregion
// #docregion imports
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
// #enddocregion imports
@Component({
selector: 'app-root',
@ -22,5 +19,3 @@ export class AppComponent {
}
// #enddocregion ctor
}
// #enddocregion

View File

@ -1,11 +1,7 @@
// #docplaster
// #docregion
// #docregion imports
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
import { UserService } from './user.service';
// #enddocregion imports
@Component({
selector: 'app-root',
@ -28,13 +24,11 @@ import { UserService } from './user.service';
export class AppComponent {
title: string;
// #docregion ctor
constructor(
@Inject(APP_CONFIG) config: AppConfig,
private userService: UserService) {
this.title = config.title;
}
// #enddocregion ctor
get isAuthorized() { return this.user.isAuthorized; }
nextUser() { this.userService.getNewUser(); }

View File

@ -15,7 +15,6 @@ import { UserService } from './user.service';
import { ProvidersModule } from './providers.module';
// #docregion ngmodule
@NgModule({
imports: [
BrowserModule,
@ -25,14 +24,12 @@ import { ProvidersModule } from './providers.module';
AppComponent,
CarComponent,
HeroesComponent,
// #enddocregion ngmodule
HeroesTspComponent,
HeroListComponent,
InjectorComponent,
TestComponent
// #docregion ngmodule
],
// #docregion providers, providers-2
// #docregion providers
providers: [
// #enddocregion providers
Logger,
@ -40,7 +37,7 @@ import { ProvidersModule } from './providers.module';
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
// #enddocregion providers, providers-2
// #enddocregion providers
exports: [ CarComponent, HeroesComponent ],
bootstrap: [ AppComponent ]
})

View File

@ -1,47 +1,36 @@
// Examples with car and engine variations
// #docplaster
import { Car, Engine, Tires } from './car';
///////// example 1 ////////////
export function simpleCar() {
// #docregion car-ctor-instantiation
// Simple car with 4 cylinders and Flintstone tires.
const car = new Car(new Engine(), new Tires());
// #enddocregion car-ctor-instantiation
car.description = 'Simple';
return car;
}
///////// example 2 ////////////
// #docregion car-ctor-instantiation-with-param
class Engine2 {
constructor(public cylinders: number) { }
}
// #enddocregion car-ctor-instantiation-with-param
export function superCar() {
// #docregion car-ctor-instantiation-with-param
// Super car with 12 cylinders and Flintstone tires.
const bigCylinders = 12;
const car = new Car(new Engine2(bigCylinders), new Tires());
// #enddocregion car-ctor-instantiation-with-param
car.description = 'Super';
return car;
}
/////////// example 3 //////////
// #docregion car-ctor-instantiation-with-mocks
class MockEngine extends Engine { cylinders = 8; }
class MockTires extends Tires { make = 'YokoGoodStone'; }
// #enddocregion car-ctor-instantiation-with-mocks
export function testCar() {
// #docregion car-ctor-instantiation-with-mocks
// Test car with 8 cylinders and YokoGoodStone tires.
const car = new Car(new MockEngine(), new MockTires());
// #enddocregion car-ctor-instantiation-with-mocks
car.description = 'Test';
return car;
}

View File

@ -3,21 +3,16 @@ import { Injector } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Logger } from '../logger.service';
// #docregion injector
export function useInjector() {
let injector: Injector;
// #enddocregion injector
/*
// #docregion injector-no-new
// Cannot instantiate an Injector like this!
let injector = new Injector([
{ provide: Car, deps: [Engine, Tires] },
{ provide: Engine, deps: [] },
{ provide: Tires, deps: [] }
]);
// #enddocregion injector-no-new
*/
// #docregion injector, injector-create-and-call
injector = Injector.create({
providers: [
{ provide: Car, deps: [Engine, Tires] },
@ -25,9 +20,7 @@ export function useInjector() {
{ provide: Tires, deps: [] }
]
});
// #docregion injector-call
const car = injector.get(Car);
// #enddocregion injector-call, injector-create-and-call
car.description = 'Injector';
injector = Injector.create({

View File

@ -1,10 +1,8 @@
// Car without DI
import { Engine, Tires } from './car';
// #docregion car
export class Car {
// #docregion car-ctor
public engine: Engine;
public tires: Tires;
public description = 'No DI';
@ -13,7 +11,6 @@ export class Car {
this.engine = new Engine();
this.tires = new Tires();
}
// #enddocregion car-ctor
// Method using the engine and tires
drive() {
@ -21,4 +18,3 @@ export class Car {
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
}
}
// #enddocregion car

View File

@ -11,11 +11,9 @@ export class Tires {
@Injectable()
export class Car {
// #docregion car-ctor
public description = 'DI';
constructor(public engine: Engine, public tires: Tires) { }
// #enddocregion car-ctor
// Method using the engine and tires
drive() {

View File

@ -1,4 +1,3 @@
// #docregion
import { Component } from '@angular/core';
import { HEROES } from './mock-heroes';
@ -10,8 +9,6 @@ import { HEROES } from './mock-heroes';
</div>
`
})
// #docregion class
export class HeroListComponent {
heroes = HEROES;
}
// #enddocregion class

View File

@ -22,9 +22,7 @@ import { HeroService } from './hero.service';
export class HeroListComponent {
heroes: Hero[];
// #docregion ctor
constructor(heroService: HeroService) {
this.heroes = heroService.getHeroes();
}
// #enddocregion ctor
}

View File

@ -1,4 +1,3 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';
@ -8,9 +7,7 @@ import { Logger } from '../logger.service';
})
export class HeroService {
// #docregion ctor
constructor(private logger: Logger) { }
// #enddocregion ctor
getHeroes() {
this.logger.log('Getting heroes ...');

View File

@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
@Injectable({
// we declare that this service should be created
// declares that this service should be created
// by the root application injector.
providedIn: 'root',
})

View File

@ -1,16 +1,10 @@
// #docplaster
// #docregion, v1
import { Component } from '@angular/core';
// #enddocregion v1
import { HeroService } from './hero.service';
// #docregion v1
@Component({
selector: 'app-heroes',
// #enddocregion v1
providers: [ HeroService ],
// #docregion v1
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>

View File

@ -8,7 +8,6 @@ import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
// #docregion injector
@Component({
selector: 'app-injectors',
template: `
@ -40,7 +39,6 @@ export class InjectorComponent implements OnInit {
return this.injector.get(ROUS, rousDontExist);
}
}
// #enddocregion injector
/**
* R.O.U.S. - Rodents Of Unusual Size

View File

@ -19,9 +19,9 @@ const template = '{{log}}';
@Component({
selector: 'provider-1',
template,
// #docregion providers-1, providers-logger
// #docregion providers-logger
providers: [Logger]
// #enddocregion providers-1, providers-logger
// #enddocregion providers-logger
})
export class Provider1Component {
log: string;
@ -114,11 +114,9 @@ export class OldLogger {
selector: 'provider-6a',
template,
providers:
// #docregion providers-6a
[ NewLogger,
// Not aliased! Creates two instances of `NewLogger`
{ provide: OldLogger, useClass: NewLogger}]
// #enddocregion providers-6a
})
export class Provider6aComponent {
log: string;
@ -193,9 +191,7 @@ export class Provider8Component {
// must be true else this component would have blown up at runtime
log = 'Hero service injected successfully via heroServiceProvider';
// #docregion provider-8-ctor
constructor(heroService: HeroService) { }
// #enddocregion provider-8-ctor
}
/////////////////
@ -221,9 +217,7 @@ export class Provider9Component implements OnInit {
constructor(private config: AppConfig){ }
// #enddocregion provider-9-ctor-interface
*/
// #docregion provider-9-ctor
constructor(@Inject(APP_CONFIG) private config: AppConfig) { }
// #enddocregion provider-9-ctor
ngOnInit() {
this.log = 'APP_CONFIG Application title is ' + this.config.title;
@ -233,9 +227,7 @@ export class Provider9Component implements OnInit {
//////////////////////////////////////////
// Sample providers 1 to 7 illustrate a required logger dependency.
// Optional logger, can be null
// #docregion import-optional
import { Optional } from '@angular/core';
// #enddocregion import-optional
const someMessage = 'Hello from the injected logger';
@ -246,13 +238,11 @@ const someMessage = 'Hello from the injected logger';
})
export class Provider10Component implements OnInit {
log: string;
// #docregion provider-10-ctor
constructor(@Optional() private logger?: Logger) {
if (this.logger) {
this.logger.log(someMessage);
}
}
// #enddocregion provider-10-ctor
ngOnInit() {
this.log = this.logger ? this.logger.logs[0] : 'Optional logger was not available';

View File

@ -22,7 +22,6 @@ export class TestComponent {
/////////////////////////////////////
function runTests() {
// #docregion spec
const expectedHeroes = [{name: 'A'}, {name: 'B'}]
const mockService = <HeroService> {getHeroes: () => expectedHeroes }
@ -31,7 +30,6 @@ function runTests() {
const component = new HeroListComponent(mockService);
expect(component.heroes.length).toEqual(expectedHeroes.length);
});
// #enddocregion spec
return testResults;
}

View File

@ -8,6 +8,4 @@ if (environment.production) {
enableProdMode();
}
// #docregion bootstrap
platformBrowserDynamic().bootstrapModule(AppModule);
// #enddocregion bootstrap

View File

@ -7,7 +7,6 @@ import { Component } from '@angular/core';
<h2>My favorite hero is: {{myHero}}</h2>
`
})
// #docregion class
export class AppComponent {
title: string;
myHero: string;

View File

@ -1,14 +1,11 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero}}</h2>
`
// #enddocregion template
})
export class AppComponent {
title = 'Tour of Heroes';

View File

@ -1,24 +1,18 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// #docregion template
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero}}</h2>
<p>Heroes:</p>
<ul>
// #docregion li
<li *ngFor="let hero of heroes">
{{ hero }}
</li>
// #enddocregion li
</ul>
`
// #enddocregion template
})
// #docregion class
export class AppComponent {
title = 'Tour of Heroes';
heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];

View File

@ -1,13 +1,9 @@
// #docregion
import { Component } from '@angular/core';
// #docregion import
import { Hero } from './hero';
// #enddocregion import
@Component({
selector: 'app-root',
// #docregion template
template: `
<h1>{{title}}</h1>
<h2>My favorite hero is: {{myHero.name}}</h2>
@ -18,12 +14,9 @@ import { Hero } from './hero';
</li>
</ul>
`
// #enddocregion template
})
// #docregion class
export class AppComponent {
title = 'Tour of Heroes';
// #docregion heroes
heroes = [
new Hero(1, 'Windstorm'),
new Hero(13, 'Bombasto'),
@ -31,5 +24,4 @@ export class AppComponent {
new Hero(20, 'Tornado')
];
myHero = this.heroes[0];
// #enddocregion heroes
}

View File

@ -1,5 +1,3 @@
// #docplaster
// #docregion final
import { Component } from '@angular/core';
import { Hero } from './hero';
@ -15,9 +13,7 @@ import { Hero } from './hero';
{{ hero.name }}
</li>
</ul>
// #docregion message
<p *ngIf="heroes.length > 3">There are many heroes!</p>
// #enddocregion message
`
})
export class AppComponent {

View File

@ -1,9 +1,5 @@
// #docregion
export class Hero {
constructor(
// #docregion id
public id: number,
// #enddocregion id
public name: string) { }
}
// #enddocregion

View File

@ -7,10 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<!-- #docregion body -->
<body>
<app-root></app-root>
</body>
<!-- #enddocregion body -->
</html>

View File

@ -1,5 +1,3 @@
<!-- #docplaster -->
<!-- #docregion -->
<h1>{{title}}</h1>
<h2>My Heroes</h2>
<ul class="heroes">
@ -14,8 +12,6 @@
<div><label>id: </label>{{selectedHero.id}}</div>
<div>
<label>name: </label>
<!-- #docregion selected-hero -->
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
<!-- #enddocregion selected-hero -->
</div>
</div>

View File

@ -1,4 +1,3 @@
// #docregion
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@ -16,9 +15,7 @@ import { AdService } from './ad.service';
HeroJobAdComponent,
HeroProfileComponent,
AdDirective ],
// #docregion entry-components
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],
// #enddocregion entry-components
bootstrap: [ AppComponent ]
})
export class AppModule {

View File

@ -2,13 +2,9 @@
<div class="group">
<h3>Target event</h3>
<!-- #docregion event-binding-1 -->
<button (click)="onSave($event)">Save</button>
<!-- #enddocregion event-binding-1 -->
<!-- #docregion event-binding-2 -->
<button on-click="onSave($event)">on-click Save</button>
<!-- #enddocregion event-binding-2 -->
<!-- #docregion custom-directive -->
<h4>myClick is an event on the custom ClickDirective:</h4>
@ -24,9 +20,9 @@
<!-- #docregion event-binding-3-->
<input [value]="currentItem.name"
(input)="currentItem.name=$event.target.value" >
without NgModel
(input)="currentItem.name=getValue($event.target)">
<!-- #enddocregion event-binding-3-->
without NgModel
</div>
<div class="group">

View File

@ -11,7 +11,7 @@ export class AppComponent {
currentItem = { name: 'teapot'} ;
clickMessage = '';
onSave(event?: KeyboardEvent) {
onSave(event?: MouseEvent) {
const evtMsg = event ? ' Event target is ' + (event.target as HTMLElement).textContent : '';
alert('Saved.' + evtMsg);
if (event) { event.stopPropagation(); }
@ -21,9 +21,14 @@ export class AppComponent {
alert(`Delete the ${item.name}.`);
}
onClickMe(event?: KeyboardEvent) {
onClickMe(event?: MouseEvent) {
const evtMsg = event ? ' Event target class is ' + (event.target as HTMLElement).className : '';
alert('Click me.' + evtMsg);
}
// #docregion getValue
getValue(target: EventTarget): string {
return (target as HTMLInputElement).value;
}
// #enddocregion getValue
}

View File

@ -1,15 +1,10 @@
// #docplaster
// #docregion customer-dashboard
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
// #enddocregion customer-dashboard
// #docregion customer-dashboard-component
// import the new component
import { CustomerDashboardComponent } from './customer-dashboard/customer-dashboard.component';
// #enddocregion customer-dashboard-component
// #docregion customer-dashboard-component
@NgModule({
imports: [
CommonModule
@ -28,7 +23,4 @@ import { CustomerDashboardComponent } from './customer-dashboard/customer-dashbo
// #enddocregion customer-dashboard-component
// #docregion customer-dashboard
export class CustomerDashboardModule { }
// #enddocregion customer-dashboard

View File

@ -20,7 +20,6 @@ export class HeroFormReactiveComponent implements OnInit {
heroForm: FormGroup;
ngOnInit(): void {
// #docregion async-validation
this.heroForm = new FormGroup({
name: new FormControl(this.hero.name, [
Validators.required,
@ -33,7 +32,6 @@ export class HeroFormReactiveComponent implements OnInit {
}),
power: new FormControl(this.hero.power, Validators.required)
});
// #enddocregion async-validation
}
get name() { return this.heroForm.get('name'); }
@ -42,7 +40,5 @@ export class HeroFormReactiveComponent implements OnInit {
get alterEgo() { return this.heroForm.get('alterEgo'); }
// #docregion async-validation
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
// #enddocregion async-validation
}

View File

@ -1,5 +1,3 @@
/* #docregion cross-validation-error-css */
.cross-validation-error input {
border-left: 5px solid red;
}
/* #enddocregion cross-validation-error-css */

View File

@ -25,7 +25,6 @@ export class UniqueAlterEgoValidator implements AsyncValidator {
}
// #enddocregion async-validator
// #docregion async-validator-directive
@Directive({
selector: '[appUniqueAlterEgo]',
providers: [
@ -43,4 +42,3 @@ export class UniqueAlterEgoValidatorDirective {
this.validator.validate(control);
}
}
// #enddocregion async-validator-directive

View File

@ -1,6 +1,6 @@
// #docregion
import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';
import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
// #docregion custom-validator
/** A hero's name can't match the given regular expression */
@ -22,7 +22,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
export class ForbiddenValidatorDirective implements Validator {
@Input('appForbiddenName') forbiddenName: string;
validate(control: AbstractControl): {[key: string]: any} | null {
validate(control: AbstractControl): ValidationErrors | null {
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
: null;
}

View File

@ -4,7 +4,7 @@ import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator,
// #docregion cross-validation-validator
/** A hero's name can't match the hero's alter ego */
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
export const identityRevealedValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => {
const name = control.get('name');
const alterEgo = control.get('alterEgo');

View File

@ -35,13 +35,11 @@
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<!-- #docregion async-validation -->
<input id="alterEgo" class="form-control" name="alterEgo"
#alterEgo="ngModel"
[(ngModel)]="hero.alterEgo"
[ngModelOptions]="{ updateOn: 'blur' }"
appUniqueAlterEgo>
<!-- #enddocregion async-validation -->
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">

View File

@ -1,9 +1,6 @@
/* tslint:disable: member-ordering */
// #docplaster
// #docregion
import { Component } from '@angular/core';
// #docregion component
@Component({
selector: 'app-hero-form-template',
templateUrl: './hero-form-template.component.html',
@ -16,4 +13,3 @@ export class HeroFormTemplateComponent {
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
}
// #enddocregion

View File

@ -102,7 +102,6 @@
<!-- ==================================================== -->
<hr>
<!-- #docregion phase1-->
<style>
.no-style .ng-valid {
border-left: 1px solid #CCC
@ -113,7 +112,6 @@
}
</style>
<div class="no-style" style="margin-left: 4px">
<!-- #docregion start -->
<div class="container">
<h1>Hero Form</h1>
<form>
@ -127,7 +125,6 @@
<input type="text" class="form-control" id="alterEgo">
</div>
<!-- #enddocregion start -->
<!-- #docregion powers -->
<div class="form-group">
<label for="power">Hero Power</label>
@ -137,17 +134,13 @@
</div>
<!-- #enddocregion powers -->
<!-- #docregion start -->
<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>
<!-- #enddocregion start -->
<!-- #enddocregion phase1-->
<!-- ==================================================== -->
<hr>
<!-- #docregion phase2-->
<div class="container">
<h1>Hero Form</h1>
<!-- #docregion template-variable-->
@ -182,7 +175,6 @@
</form>
</div>
<!-- #enddocregion phase2-->
<!-- EXTRA MATERIAL FOR DOCUMENTATION -->
<hr>
@ -193,11 +185,9 @@
TODO: remove this: {{model.name}}
<!-- #enddocregion ngModelName-1 -->
<hr>
<!-- #docregion ngModel-3-->
<input type="text" class="form-control" id="name"
required
[ngModel]="model.name" name="name"
(ngModelChange)="model.name = $event">
TODO: remove this: {{model.name}}
<!-- #enddocregion ngModel-3-->
</div>

View File

@ -49,9 +49,7 @@ export class HeroFormComponent {
// Name via form.controls = {{showFormControls(heroForm)}}
showFormControls(form: any) {
return form && form.controls.name &&
// #docregion form-controls
form.controls.name.value; // Dr. IQ
// #enddocregion form-controls
}
/////////////////////////////

View File

@ -7,10 +7,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- #docregion bootstrap -->
<link rel="stylesheet"
href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<!-- #enddocregion bootstrap -->
<!-- #docregion styles -->
<link rel="stylesheet" href="assets/forms.css">
<!-- #enddocregion styles -->

View File

@ -1,3 +1 @@
p {
font-family: Lato;
}

View File

@ -3,6 +3,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
styleUrls: ['./app.component.css']
})
export class AppComponent {}
export class AppComponent {
}

View File

@ -20,6 +20,8 @@ import { ProductListComponent } from './product-list/product-list.component';
TopBarComponent,
ProductListComponent
],
bootstrap: [ AppComponent ]
bootstrap: [
AppComponent
]
})
export class AppModule { }

View File

@ -1,15 +1,18 @@
export const products = [
{
id: 1,
name: 'Phone XL',
price: 799,
description: 'A large phone with one of the best screens'
},
{
id: 2,
name: 'Phone Mini',
price: 699,
description: 'A great phone with one of the best cameras'
},
{
id: 3,
name: 'Phone Standard',
price: 299,
description: ''

View File

@ -1,15 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-top-bar',
templateUrl: './top-bar.component.html',
styleUrls: ['./top-bar.component.css']
})
export class TopBarComponent implements OnInit {
constructor() { }
ngOnInit() {
}
export class TopBarComponent {
}

View File

@ -1,11 +1,12 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { environment } from './environments/environment';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -1,8 +1,5 @@
// #docplaster
// #docregion
import { Injectable } from '@angular/core';
// #docregion v1
@Injectable({
providedIn: 'root'
})

View File

@ -1,14 +1,15 @@
// #docplaster
// #docregion import-http
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// #enddocregion import-http
@Injectable({
providedIn: 'root'
})
// #docregion props, methods, inject-http, get-shipping
export class CartService {
// #enddocregion get-shipping
items = [];
// #enddocregion props, methods
@ -32,8 +33,10 @@ export class CartService {
}
// #enddocregion methods
// #docregion get-shipping
getShippingPrices() {
return this.http.get('/assets/shipping.json');
return this.http.get<{type: string, price: number}[]>('/assets/shipping.json');
}
// #docregion props, methods, inject-http
}
// #enddocregion props, methods, inject-http

View File

@ -1,15 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit {
export class CartComponent {
constructor() { }
ngOnInit() {
}
}

View File

@ -9,10 +9,10 @@ import { CartService } from '../cart.service';
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
// #docregion inject-cart, items, submit
// #docregion inject-cart, items
export class CartComponent {
// #enddocregion inject-cart
items;
items = this.cartService.getItems();
// #docregion inject-cart
constructor(

View File

@ -1,15 +1,11 @@
// #docplaster
// #docregion imports
import { Component, OnInit } from '@angular/core';
import { CartService } from '../cart.service';
// #enddocregion imports
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
// #docregion props-services, submit
export class CartComponent implements OnInit {
items;
@ -17,7 +13,7 @@ export class CartComponent implements OnInit {
private cartService: CartService
) { }
ngOnInit() {
ngOnInit(): void {
this.items = this.cartService.getItems();
}
}

View File

@ -12,7 +12,7 @@
</div>
<!-- #docregion checkout-form-1 -->
<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit(checkoutForm.value)">
<form [formGroup]="checkoutForm" (ngSubmit)="onSubmit()">
<!-- #enddocregion checkout-form-1 -->
<div>

View File

@ -1,6 +1,6 @@
// #docplaster
// #docregion imports
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { CartService } from '../cart.service';
@ -11,38 +11,26 @@ import { CartService } from '../cart.service';
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
// #docregion props-services, submit, inject-form-builder, checkout-form, checkout-form-group
export class CartComponent implements OnInit {
items;
// #enddocregion inject-form-builder
checkoutForm;
// #enddocregion checkout-form
// #docregion inject-form-builder, checkout-form-group
export class CartComponent {
// #enddocregion inject-form-builder
items = this.cartService.getItems();
checkoutForm = this.formBuilder.group({
name: '',
address: ''
});
// #docregion inject-form-builder
constructor(
private cartService: CartService,
private formBuilder: FormBuilder,
) {
// #enddocregion inject-form-builder
this.checkoutForm = this.formBuilder.group({
name: '',
address: ''
});
// #docregion inject-form-builder
}
ngOnInit() {
this.items = this.cartService.getItems();
}
) {}
// #enddocregion inject-form-builder, checkout-form-group
// #enddocregion props-services
onSubmit(customerData) {
onSubmit(): void {
// Process checkout data here
this.items = this.cartService.clearCart();
console.warn('Your order has been submitted', this.checkoutForm.value);
this.checkoutForm.reset();
console.warn('Your order has been submitted', customerData);
}
// #docregion props-services, inject-form-builder, checkout-form, checkout-form-group
// #docregion inject-form-builder, checkout-form-group
}

View File

@ -11,9 +11,10 @@ import { products } from '../products';
templateUrl: './product-details.component.html',
styleUrls: ['./product-details.component.css']
})
// #docregion props-methods, add-to-cart
// #docregion props-methods, product-prop
export class ProductDetailsComponent implements OnInit {
product;
// #enddocregion product-prop
constructor(
private route: ActivatedRoute,
@ -22,10 +23,15 @@ export class ProductDetailsComponent implements OnInit {
// #enddocregion props-methods
// #docregion get-product
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.product = products[+params.get('productId')];
});
// First get the product id from the current route.
const routeParams = this.route.snapshot.paramMap;
const productIdFromRoute = Number(routeParams.get('productId'));
// Find the product that correspond with the id provided in route.
this.product = products.find(product => product.id === productIdFromRoute);
}
// #enddocregion get-product
// #docregion product-prop
/* ... */
// #docregion props-methods
}

View File

@ -1,10 +1,9 @@
// #docplaster
// #docregion imports, cart-service
// #docregion cart-service
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { products } from '../products';
// #enddocregion imports
import { CartService } from '../cart.service';
// #enddocregion cart-service
@ -13,34 +12,31 @@ import { CartService } from '../cart.service';
templateUrl: './product-details.component.html',
styleUrls: ['./product-details.component.css']
})
// #docregion props-methods, get-product, inject-cart-service, add-to-cart
// #docregion inject-cart-service, add-to-cart
export class ProductDetailsComponent implements OnInit {
// #enddocregion add-to-cart, get-product, inject-cart-service
// #enddocregion add-to-cart, inject-cart-service
product;
// #docregion inject-cart-service
constructor(
private route: ActivatedRoute,
// #enddocregion props-methods
private cartService: CartService
// #docregion props-methods
) { }
// #enddocregion inject-cart-service
// #docregion get-product
ngOnInit() {
// #enddocregion props-methods
this.route.paramMap.subscribe(params => {
this.product = products[+params.get('productId')];
});
// #docregion props-methods
// First get the product id from the current route.
const routeParams = this.route.snapshot.paramMap;
const productIdFromRoute = Number(routeParams.get('productId'));
// Find the product that correspond with the id provided in route.
this.product = products.find(product => product.id === productIdFromRoute);
}
// #enddocregion props-methods, get-product
// #docregion add-to-cart
addToCart(product) {
this.cartService.addToCart(product);
window.alert('Your product has been added to the cart!');
}
// #docregion props-methods, get-product, inject-cart-service
// #docregion inject-cart-service
}

View File

@ -2,13 +2,11 @@
<div *ngFor="let product of products">
<!-- #docregion product-details -->
<h3>
<a [title]="product.name + ' details'">
{{ product.name }}
</a>
</h3>
<!-- #enddocregion product-details -->
<p *ngIf="product.description">
Description: {{ product.description }}

View File

@ -1,26 +1,28 @@
<h2>Products</h2>
<!-- #docregion router-link -->
<div *ngFor="let product of products; index as productId">
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'" [routerLink]="['/products', productId]">
<a [title]="product.name + ' details'" [routerLink]="['/products', product.id]">
{{ product.name }}
</a>
</h3>
<!-- #enddocregion router-link -->
<!-- #enddocregion router-link -->
<p *ngIf="product.description">
Description: {{ product.description }}
</p>
<button (click)="share()">
Share
</button>
<app-product-alerts
[product]="product"
(notify)="onNotify()">
</app-product-alerts>
<!-- #docregion router-link -->
<!-- #docregion router-link -->
</div>
<!-- #enddocregion router-link -->

View File

@ -1,15 +1,18 @@
export const products = [
{
id: 1,
name: 'Phone XL',
price: 799,
description: 'A large phone with one of the best screens'
},
{
id: 2,
name: 'Phone Mini',
price: 699,
description: 'A great phone with one of the best cameras'
},
{
id: 3,
name: 'Phone Standard',
price: 299,
description: ''

View File

@ -1,15 +1,12 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-shipping',
templateUrl: './shipping.component.html',
styleUrls: ['./shipping.component.css']
})
export class ShippingComponent implements OnInit {
export class ShippingComponent {
constructor() { }
ngOnInit() {
}
}

View File

@ -1,6 +1,6 @@
// #docplaster
// #docregion imports
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { CartService } from '../cart.service';
// #enddocregion
@ -10,21 +10,18 @@ import { CartService } from '../cart.service';
templateUrl: './shipping.component.html',
styleUrls: ['./shipping.component.css']
})
// #docregion props, ctor
export class ShippingComponent implements OnInit {
shippingCosts;
// #docregion props
export class ShippingComponent {
shippingCosts = this.cartService.getShippingPrices();
// #enddocregion props
// #docregion inject-cart-service
constructor(
private cartService: CartService
) {
constructor(private cartService: CartService) {
}
// #enddocregion inject-cart-service
ngOnInit() {
this.shippingCosts = this.cartService.getShippingPrices();
}
// #docregion props
}

View File

@ -29,6 +29,26 @@ const checkLogForMessage = async (message: string) => {
};
describe('Http Tests', () => {
// It seems that currently Chrome/ChromeDriver fail to click a button that is just outside the
// viewport (or maybe only partially inside the viewport) - at least in headless mode.
// Possible solutions:
// 1. Click the element via JavaScript (with something like
// `browser.executeScript('arguments[0].click', elem)`).
// 2. Manually scroll the element into view before clicking:
// https://stackoverflow.com/questions/47776774/element-is-not-clickable-at-point-in-headless-mode-but-when-we-remove-headless
// 3. Explicitly set the window size to a bigger size:
// https://stackoverflow.com/questions/62003082/elementnotinteractableexception-element-not-interactable-element-has-zero-size
//
// Since the default 800x600 window size in headless mode (as used on CI) causes the
// `<app-config>` buttons to be in a position that trigger the above issue, we explicitly set the
// window size to 1920x1080 when in headless mode.
beforeAll(async () => {
const config = await browser.getProcessedConfig();
if (config.capabilities?.chromeOptions?.args?.includes('--headless')) {
browser.driver.manage().window().setSize(1920, 1080);
}
});
beforeEach(() => browser.get(''));
describe('Heroes', () => {
@ -66,6 +86,7 @@ describe('Http Tests', () => {
await checkLogForMessage('GET "assets/config.json"');
expect(await page.configSpan.getText()).toContain('Heroes API URL is "api/heroes"');
expect(await page.configSpan.getText()).toContain('Textfile URL is "assets/textfile.txt"');
expect(await page.configSpan.getText()).toContain('Date is "Wed Jan 29 2020" (date)');
});
it('can fetch the configuration JSON file with headers', async () => {

View File

@ -7,6 +7,7 @@
<span *ngIf="config">
<p>Heroes API URL is "{{config.heroesUrl}}"</p>
<p>Textfile URL is "{{config.textfile}}"</p>
<p>Date is "{{config.date.toDateString()}}" ({{getType(config.date)}})</p>
<div *ngIf="headers">
Response headers:
<ul>

View File

@ -2,7 +2,6 @@
// #docregion
import { Component } from '@angular/core';
import { Config, ConfigService } from './config.service';
import { MessageService } from '../message.service';
@Component({
selector: 'app-config',
@ -25,7 +24,7 @@ export class ConfigComponent {
this.headers = undefined;
}
// #docregion v1, v2, v3
// #docregion v1, v2
showConfig() {
this.configService.getConfig()
// #enddocregion v1, v2
@ -34,25 +33,23 @@ export class ConfigComponent {
error => this.error = error // error path
);
}
// #enddocregion v3
showConfig_v1() {
this.configService.getConfig_1()
// #docregion v1, v1_callback
// #docregion v1
.subscribe((data: Config) => this.config = {
heroesUrl: data.heroesUrl,
textfile: data.textfile
textfile: data.textfile,
date: data.date,
});
// #enddocregion v1_callback
}
// #enddocregion v1
showConfig_v2() {
this.configService.getConfig()
// #docregion v2, v2_callback
// #docregion v2
// clone the data object, using its known Config shape
.subscribe((data: Config) => this.config = { ...data });
// #enddocregion v2_callback
}
// #enddocregion v2
@ -74,5 +71,9 @@ export class ConfigComponent {
makeError() {
this.configService.makeIntentionalError().subscribe(null, error => this.error = error );
}
getType(val: any): string {
return val instanceof Date ? 'date' : Array.isArray(val) ? 'array' : typeof val;
}
}
// #enddocregion

View File

@ -14,6 +14,7 @@ import { catchError, retry } from 'rxjs/operators';
export interface Config {
heroesUrl: string;
textfile: string;
date: any;
}
// #enddocregion config-interface
// #docregion proto

View File

@ -57,8 +57,7 @@
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
font-family: Arial;
font-family: Arial, sans-serif;
}
button:hover {

View File

@ -1,5 +1,4 @@
<h3>Heroes</h3>
<!-- #docregion add -->
<div>
<label>Hero name:
<input #heroName />
@ -12,9 +11,7 @@
search
</button>
</div>
<!-- #enddocregion add -->
<!-- #docregion list -->
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a (click)="edit(hero)">
@ -23,10 +20,7 @@
<input *ngIf="hero===editHero" [(ngModel)]="hero.name"
(blur)="update()" (keyup.enter)="update()">
</a>
<!-- #docregion delete -->
<button class="delete" title="delete hero"
(click)="delete(hero)">x</button>
<!-- #enddocregion delete -->
</li>
</ul>
<!-- #enddocregion list -->

View File

@ -6,6 +6,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
// #enddocregion interceptor-providers
import { AuthInterceptor } from './auth-interceptor';
import { CachingInterceptor } from './caching-interceptor';
import { CustomJsonInterceptor , CustomJsonParser, JsonParser} from './custom-json-interceptor';
import { EnsureHttpsInterceptor } from './ensure-https-interceptor';
import { LoggingInterceptor } from './logging-interceptor';
// #docregion interceptor-providers
@ -21,6 +22,10 @@ export const httpInterceptorProviders = [
// #docregion noop-provider
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
// #enddocregion noop-provider, interceptor-providers
// #docregion custom-json-interceptor
{ provide: HTTP_INTERCEPTORS, useClass: CustomJsonInterceptor, multi: true },
{ provide: JsonParser, useClass: CustomJsonParser },
// #enddocregion custom-json-interceptor
{ provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },

Some files were not shown because too many files have changed in this diff Show More