Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 4253378

Browse files
authored
docs(hierarchical-di): Correct avoidance example (#3086) and more (#3091)
* closes #3086 * samples reworked to conform to our sample style, make more sense, and cover the points in prose. * copy edits to bring closer to Google docs standards.
1 parent 09f79e3 commit 4253378

27 files changed

+703
-311
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,103 @@
1-
import { browser, element, by } from 'protractor';
1+
'use strict'; // necessary for es6 output in node
22

3-
describe('Hierarchical dependency injection', function () {
3+
import { browser, by, element } from 'protractor';
44

5-
beforeEach(function () {
5+
describe('Hierarchical dependency injection', () => {
6+
7+
beforeAll(() => {
68
browser.get('');
79
});
810

9-
it('should open with a card view', function () {
10-
expect(element.all(by.cssContainingText('button', 'edit')).get(0).isDisplayed()).toBe(true,
11-
'edit button should be displayed');
12-
});
11+
describe('Heroes Scenario', () => {
12+
let page = {
13+
heroName: '',
14+
income: '',
1315

14-
it('should have multiple heroes listed', function () {
15-
expect(element.all(by.css('heroes-list li')).count()).toBeGreaterThan(1);
16-
});
16+
// queries
17+
heroEl: element.all(by.css('heroes-list li')).get(0), // first hero
18+
heroCardEl: element(by.css('heroes-list hero-tax-return')), // first hero tax-return
19+
taxReturnNameEl: element.all(by.css('heroes-list hero-tax-return #name')).get(0),
20+
incomeInputEl: element.all(by.css('heroes-list hero-tax-return input')).get(0),
21+
cancelButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Cancel')),
22+
closeButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Close')),
23+
saveButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Save'))
24+
};
1725

18-
it('should change to editor view after selection', function () {
19-
let editButtonEle = element.all(by.cssContainingText('button', 'edit')).get(0);
20-
editButtonEle.click().then(function() {
21-
expect(editButtonEle.isDisplayed()).toBe(false, 'edit button should be hidden after selection');
26+
it('should list multiple heroes', () => {
27+
expect(element.all(by.css('heroes-list li')).count()).toBeGreaterThan(1);
28+
});
29+
30+
it('should show no hero tax-returns at the start', () => {
31+
expect(element.all(by.css('heroes-list li hero-tax-return')).count()).toBe(0);
32+
});
33+
34+
it('should open first hero in hero-tax-return view after click', () => {
35+
page.heroEl.getText()
36+
.then(val => {
37+
page.heroName = val;
38+
})
39+
.then(() => page.heroEl.click())
40+
.then(() => {
41+
expect(page.heroCardEl.isDisplayed()).toBe(true);
42+
});
43+
});
44+
45+
it('hero tax-return should have first hero\'s name', () => {
46+
// Not `page.tax-returnNameInputEl.getAttribute('value')` although later that is essential
47+
expect(page.taxReturnNameEl.getText()).toEqual(page.heroName);
48+
});
49+
50+
it('should be able to cancel change', () => {
51+
page.incomeInputEl.clear()
52+
.then(() => page.incomeInputEl.sendKeys('777'))
53+
.then(() => {
54+
expect(page.incomeInputEl.getAttribute('value')).toBe('777', 'income should be 777');
55+
return page.cancelButtonEl.click();
56+
})
57+
.then(() => {
58+
expect(page.incomeInputEl.getAttribute('value')).not.toBe('777', 'income should not be 777');
59+
});
60+
});
61+
62+
it('should be able to save change', () => {
63+
page.incomeInputEl.clear()
64+
.then(() => page.incomeInputEl.sendKeys('999'))
65+
.then(() => {
66+
expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should be 999');
67+
return page.saveButtonEl.click();
68+
})
69+
.then(() => {
70+
expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should still be 999');
71+
});
72+
});
73+
74+
it('should be able to close tax-return', () => {
75+
page.saveButtonEl.click()
76+
.then(() => {
77+
expect(element.all(by.css('heroes-list li hero-tax-return')).count()).toBe(0);
78+
});
2279
});
23-
});
2480

25-
it('should be able to save editor change', function () {
26-
testEdit(true);
2781
});
2882

29-
it('should be able to cancel editor change', function () {
30-
testEdit(false);
83+
describe('Villains Scenario', () => {
84+
it('should list multiple villains', () => {
85+
expect(element.all(by.css('villains-list li')).count()).toBeGreaterThan(1);
86+
});
3187
});
3288

33-
function testEdit(shouldSave: boolean) {
34-
// select 2nd ele
35-
let heroEle = element.all(by.css('heroes-list li')).get(1);
36-
// get the 2nd span which is the name of the hero
37-
let heroNameEle = heroEle.all(by.css('hero-card span')).get(1);
38-
let editButtonEle = heroEle.element(by.cssContainingText('button', 'edit'));
39-
editButtonEle.click().then(function() {
40-
let inputEle = heroEle.element(by.css('hero-editor input'));
41-
return inputEle.sendKeys('foo');
42-
}).then(function() {
43-
let buttonName = shouldSave ? 'save' : 'cancel';
44-
let buttonEle = heroEle.element(by.cssContainingText('button', buttonName));
45-
return buttonEle.click();
46-
}).then(function() {
47-
if (shouldSave) {
48-
expect(heroNameEle.getText()).toContain('foo');
49-
} else {
50-
expect(heroNameEle.getText()).not.toContain('foo');
51-
}
52-
});
53-
}
89+
describe('Cars Scenario', () => {
5490

91+
it('A-component should use expected services', () => {
92+
expect(element(by.css('a-car')).getText()).toContain('C1-E1-T1');
93+
});
94+
95+
it('B-component should use expected services', () => {
96+
expect(element(by.css('b-car')).getText()).toContain('C2-E2-T1');
97+
});
5598

99+
it('C-component should use expected services', () => {
100+
expect(element(by.css('c-car')).getText()).toContain('C3-E2-T1');
101+
});
102+
});
56103
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'my-app',
5+
template: `
6+
<label><input type="checkbox" [checked]="showHeroes" (change)="showHeroes=!showHeroes">Heroes</label>
7+
<label><input type="checkbox" [checked]="showVillains" (change)="showVillains=!showVillains">Villains</label>
8+
<label><input type="checkbox" [checked]="showCars" (change)="showCars=!showCars">Cars</label>
9+
10+
<h1>Hierarchical Dependency Injection</h1>
11+
12+
<heroes-list *ngIf="showHeroes"></heroes-list>
13+
<villains-list *ngIf="showVillains"></villains-list>
14+
<my-cars *ngIf="showCars"></my-cars>
15+
`
16+
})
17+
export class AppComponent {
18+
showCars = true;
19+
showHeroes = true;
20+
showVillains = true;
21+
}
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,33 @@
11
// #docregion
2-
import { NgModule } from '@angular/core';
2+
import { NgModule } from '@angular/core';
33
import { BrowserModule } from '@angular/platform-browser';
4-
import { FormsModule } from '@angular/forms';
4+
import { FormsModule } from '@angular/forms';
55

6-
import { HeroesListComponent } from './heroes-list.component';
7-
import { HeroEditorComponent } from './hero-editor.component';
8-
import { HeroCardComponent } from './hero-card.component';
9-
import { HeroesService } from './heroes.service';
6+
import { AppComponent } from './app.component';
7+
import { HeroTaxReturnComponent } from './hero-tax-return.component';
8+
import { HeroesListComponent } from './heroes-list.component';
9+
import { HeroesService } from './heroes.service';
10+
import { VillainsListComponent } from './villains-list.component';
11+
12+
import { carComponents, carServices } from './car.components';
1013

1114
@NgModule({
1215
imports: [
1316
BrowserModule,
1417
FormsModule
1518
],
16-
providers: [ HeroesService ],
19+
providers: [
20+
carServices,
21+
HeroesService
22+
],
1723
declarations: [
24+
AppComponent,
25+
carComponents,
1826
HeroesListComponent,
19-
HeroCardComponent,
20-
HeroEditorComponent
27+
HeroTaxReturnComponent,
28+
VillainsListComponent
2129
],
22-
bootstrap: [ HeroesListComponent ]
30+
bootstrap: [ AppComponent ]
2331
})
2432
export class AppModule { }
2533

26-
/* Documentation artifact below
27-
// #docregion bad-alternative
28-
// Don't do this!
29-
@NgModule({
30-
imports: [
31-
BrowserModule,
32-
FormsModule
33-
],
34-
providers: [ HeroesService, RestoreService ],
35-
declarations: [ HeroesListComponent ],
36-
bootstrap: [
37-
HeroesListComponent,
38-
HeroCardComponent,
39-
HeroEditorComponent
40-
]
41-
})
42-
// #enddocregion bad-alternative
43-
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Component } from '@angular/core';
2+
3+
import {
4+
CarService, CarService2, CarService3,
5+
EngineService, EngineService2, TiresService
6+
} from './car.services';
7+
8+
////////// CCarComponent ////////////
9+
@Component({
10+
selector: 'c-car',
11+
template: `<div>C: {{description}}</div>`,
12+
providers: [
13+
{ provide: CarService, useClass: CarService3 }
14+
]
15+
})
16+
export class CCarComponent {
17+
description: string;
18+
constructor(carService: CarService) {
19+
this.description = `${carService.getCar().description} (${carService.name})`;
20+
}
21+
}
22+
23+
////////// BCarComponent ////////////
24+
@Component({
25+
selector: 'b-car',
26+
template: `
27+
<div>B: {{description}}</div>
28+
<c-car></c-car>
29+
`,
30+
providers: [
31+
{ provide: CarService, useClass: CarService2 },
32+
{ provide: EngineService, useClass: EngineService2 }
33+
]
34+
})
35+
export class BCarComponent {
36+
description: string;
37+
constructor(carService: CarService) {
38+
this.description = `${carService.getCar().description} (${carService.name})`;
39+
}
40+
}
41+
42+
////////// ACarComponent ////////////
43+
@Component({
44+
selector: 'a-car',
45+
template: `
46+
<div>A: {{description}}</div>
47+
<b-car></b-car>`
48+
})
49+
export class ACarComponent {
50+
description: string;
51+
constructor(carService: CarService) {
52+
this.description = `${carService.getCar().description} (${carService.name})`;
53+
}
54+
}
55+
////////// CarsComponent ////////////
56+
@Component({
57+
selector: 'my-cars',
58+
template: `
59+
<h3>Cars</h3>
60+
<a-car></a-car>`
61+
})
62+
export class CarsComponent { }
63+
64+
////////////////
65+
66+
export const carComponents = [
67+
CarsComponent,
68+
ACarComponent, BCarComponent, CCarComponent
69+
];
70+
71+
// generic car-related services
72+
export const carServices = [
73+
CarService, EngineService, TiresService
74+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Injectable } from '@angular/core';
2+
3+
/// Model ///
4+
export class Car {
5+
name = 'Avocado Motors';
6+
constructor(public engine: Engine, public tires: Tires) { }
7+
8+
get description() {
9+
return `${this.name} car with ` +
10+
`${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
11+
}
12+
}
13+
14+
export class Engine {
15+
cylinders = 4;
16+
}
17+
18+
export class Tires {
19+
make = 'Flintstone';
20+
model = 'Square';
21+
}
22+
23+
//// Engine services ///
24+
@Injectable()
25+
export class EngineService {
26+
id = 'E1';
27+
getEngine() { return new Engine(); }
28+
}
29+
30+
@Injectable()
31+
export class EngineService2 {
32+
id = 'E2';
33+
getEngine() {
34+
const eng = new Engine();
35+
eng.cylinders = 8;
36+
return eng;
37+
}
38+
}
39+
40+
//// Tire services ///
41+
@Injectable()
42+
export class TiresService {
43+
id = 'T1';
44+
getTires() { return new Tires(); }
45+
}
46+
47+
/// Car Services ///
48+
@Injectable()
49+
export class CarService {
50+
id = 'C1';
51+
constructor(
52+
protected engineService: EngineService,
53+
protected tiresService: TiresService) { }
54+
55+
getCar() {
56+
return new Car(
57+
this.engineService.getEngine(),
58+
this.tiresService.getTires());
59+
}
60+
61+
get name() {
62+
return `${this.id}-${this.engineService.id}-${this.tiresService.id}`;
63+
}
64+
}
65+
66+
@Injectable()
67+
export class CarService2 extends CarService {
68+
id = 'C2';
69+
constructor(
70+
protected engineService: EngineService,
71+
protected tiresService: TiresService) {
72+
super(engineService, tiresService);
73+
}
74+
getCar() {
75+
const car = super.getCar();
76+
car.name = 'BamBam Motors, BroVan 2000';
77+
return car;
78+
}
79+
}
80+
81+
@Injectable()
82+
export class CarService3 extends CarService2 {
83+
id = 'C3';
84+
constructor(
85+
protected engineService: EngineService,
86+
protected tiresService: TiresService) {
87+
super(engineService, tiresService);
88+
}
89+
getCar() {
90+
const car = super.getCar();
91+
car.name = 'Chizzamm Motors, Calico UltraMax Supreme';
92+
return car;
93+
}
94+
}
95+

0 commit comments

Comments
 (0)