Skip to content

Commit d9344d2

Browse files
committed
Scenario #7 completed.
1 parent 01ae6c7 commit d9344d2

File tree

5 files changed

+194
-5
lines changed

5 files changed

+194
-5
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// #docregion
2+
@Component({
3+
selector: 'hero-job-board',
4+
template:
5+
// --- Intact
6+
` <div class='request-panel'>
7+
<h2>Hero Job Board</h2>
8+
<div>
9+
<button (click)='inviteAllHeroes()'>Invite heroes</button>
10+
</div>
11+
<h3>Job Request</h3>
12+
<label>Request:</label>
13+
<input #box (keyup)="0" style='width: 400px;' [value]='jobRequest' />
14+
<button (click)='announceJob(box.value)'
15+
[disabled]='!box?.value.trim().length > 0'>
16+
Ask
17+
</button>
18+
<h3>{{getJobStatus()}}</h3>` +
19+
// --- We altered this template fragment
20+
` <div *ng-if='!winner'>
21+
<div class='responding-hero'
22+
*ng-for='#hero of respondingHeroes'>
23+
<span class='hero-name'>{{hero.name}}</span>
24+
<button (click)='assignJob(hero)'>Assign</button>
25+
</div>
26+
</div>
27+
<div *ng-if='winner'>
28+
The winner is
29+
<span class='winner'>{{winner.name}}
30+
</div>
31+
` +
32+
// --- Intact
33+
` </div>
34+
<div class='invited-hero-list'>
35+
<invited-hero *ng-for='#hero of invitedHeroes'
36+
[hero]='hero'>
37+
</invited-hero>
38+
</div>
39+
`,
40+
styles: [
41+
// --- We appended this new style rule to the existing styles
42+
` .winner {
43+
display: inline-block;
44+
color: white;
45+
font-weight: bold;
46+
background-color: green;
47+
padding: 4px 8px;
48+
}
49+
`],
50+
directives: [
51+
InvitedHero,
52+
CORE_DIRECTIVES],
53+
providers: [JobService]
54+
})
55+
export class HeroJobBoard{
56+
// --- Existing and untouched members are omitted for the sake of brevity
57+
get winner() {
58+
return this.jobService.assignedTo;
59+
}
60+
61+
getJobStatus() {
62+
if (!this.jobRequest) {
63+
return "No job request announced"
64+
}
65+
else if (this.winner) {
66+
return "Job assigned";
67+
} else
68+
{
69+
return this.jobService.respondingHeroes.length > 0
70+
? "Responding heroes"
71+
: "No responding heroes";
72+
}
73+
}
74+
75+
assignJob(hero: Hero) {
76+
this.jobService.assign(hero);
77+
}
78+
}
79+
// #enddocregion
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// #docregion
2+
@Component({
3+
selector: 'invited-hero',
4+
template:
5+
// --- Intact
6+
` <div class='invited-hero'>
7+
<h3 class='hero-name'>Job Request for {{hero.name}}</h4>
8+
<h4 class=job-request
9+
[class.announced]='request'
10+
[class.undertaken]='undertaken'>
11+
{{getRequest()}}
12+
</h4>` +
13+
// --- Altered section
14+
` <h2 *ng-if='winner'
15+
[class.won]='undertaken && winner==hero'
16+
[class.lost]='undertaken && winner!=hero'
17+
[class.else]='!undertaken'>
18+
{{finalState}}
19+
</h2>
20+
<button [disabled]="!request || undertaken"
21+
[hidden]='winner'
22+
(click)='undertakeJob()'>
23+
I'll take it!
24+
</button>
25+
</div>
26+
`,
27+
styles: [
28+
// --- We appended these new style rules
29+
` .won {
30+
color: white;
31+
border: 2px solid green;
32+
background-color: green;
33+
padding: 8px;
34+
}
35+
.lost {
36+
color: white;
37+
border: 2px solid gray;
38+
background-color: gray;
39+
padding: 8px;
40+
}
41+
.else {
42+
color: gray;
43+
border: 2px solid gray;
44+
background-color: #dddddd;
45+
padding: 8px;
46+
}
47+
`],
48+
directives: [CORE_DIRECTIVES]
49+
})
50+
export class InvitedHero {
51+
// --- Existing and untouched members are omitted for the sake of brevity
52+
winner: Hero;
53+
54+
constructor(private jobService: JobService) {
55+
// --- Listen to jobAssignedEvent
56+
jobService.jobAssignedEvent.subscribe(
57+
winner => {
58+
this.winner = winner;
59+
});
60+
}
61+
62+
get finalState() {
63+
if (!this.winner) return "No winner announced yet."
64+
if (!this.undertaken) return "Job taken."
65+
return this.winner == this.hero
66+
? "I won the job !!!"
67+
: "I lost the job :-(";
68+
}
69+
}
70+
// #enddocregion

public/docs/_examples/component-communication/ts/assign-job/src/app/job-service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// #docregion
12
import {Injectable, EventEmitter} from 'angular2/angular2';
23
import {Hero} from './hero';
34

@@ -36,4 +37,5 @@ export class JobService {
3637
this._assignedTo = hero;
3738
this.jobAssignedEvent.next(hero);
3839
}
39-
}
40+
}
41+
// #enddocregion

public/docs/ts/latest/guide/component-communication.jade

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,13 @@ figure.image-display
161161

162162
figure.image-display
163163
img(src="/resources/images/devguide/component-communication/prototype-ui.png" alt="Parent and its children")
164-
164+
165+
.alert.is-helpful
166+
:marked
167+
In the subsequent sections of this chapter, we are going to extend and, sometimes, refactor this application. The app we create implements the basic
168+
workflow, but does not add every nitty-gritty details that we would add to a real application, as those subtleties would distract the focus from the
169+
most essential things. For example, we do not check inputs thoroughly, and do not spend too much time to create fancy UI.
170+
165171
:marked
166172
## #2: Inviting heroes &mdash; parent to child communication with data binding
167173

@@ -489,7 +495,7 @@ figure.image-display
489495
Our application will work properly only if we use a singleton instance of `JobService`: the single `HeroJobBoard` instance and all `InvitedHero` instances
490496
_must use_ the very same `JobService` instance. We'll ensure this behavior by configuring `HeroJobBoard`'s `providers` annotation property this way:
491497

492-
+makeExample('component-communication/ts/job-service/fragments/provider-annotation.ts', null, 'hero-job-board.ts (component annotation)' ,{blk: /(providers)/g})
498+
+makeExample('component-communication/ts/job-service/fragments/provider-annotation.ts', null, 'hero-job-board.ts (component annotation)', {blk: /(providers)/g})
493499

494500
:marked
495501
Thanks to the great design of Angular's hierarchical dependency injection system &mdash;
@@ -501,10 +507,42 @@ figure.image-display
501507

502508
## #7: Assigning the job to a hero &mdash; extending the intermediary service
503509

510+
Now, we have the `JobService` component, which &mdash; according to our intention &mdash; encapsulates the business logic of the app. Naturally, we want this
511+
very component to implement the method of assigning the job to a hero. We need to provide a method for `HeroJobBoard` to assign the job to one of the responding
512+
heroes, and add an event `InviteddHero` instances listen to in order to get notified about job assignment.
504513

505-
_Content_
514+
This extension is strightforward, as the new, highlighted members of `JobService` show:
515+
516+
+makeExample('component-communication/ts/assign-job/src/app/job-service.ts', null, 'job-service.ts', {blk: /(_assignedTo|assignedTo\(\)|jobAssignedEvent|assign\(hero: Hero\))/g})
517+
518+
:marked
519+
To integrate the new `assign()` method with `HeroJobBoard`, we need to change the component template and add a couple of methods to the class body, as shown in
520+
this code extract:
521+
522+
+makeExample('component-communication/ts/assign-job/fragments/hero-job-board.ts', null, 'job-service.ts', {blk: /(winner\(\)|assignJob\(hero: Hero\))/g})
523+
524+
:marked
525+
Now, the component template removes the list of responding heroes as soon as the job as assigned to one of them, and the winner is displayed in the Hero Job Board.
526+
We need to change `InvitedHero`, too, so that it could listen to assignment announcements, and change its UI accordingly:
527+
528+
+makeExample('component-communication/ts/assign-job/fragments/invited-hero.ts', null, 'invited-hero.ts', {blk: /(subscribe|finalState\(\))/g})
529+
530+
:marked
531+
Here, `InviteHero` subscribes to the `jobAssignedEvent`, and sets the winner hero according to the event parameter (`JobService` defines `jobAssigned` as a
532+
type of `EventEmitter<Hero>`). The `finalState()` helper method is used to set the right label in the UI of the component that displays
533+
whether the hero won or lost &mdash; provided it undertake the job at all.
534+
535+
When the person in need assigns a job to a hero, the UI is immediately updated. This figure shows that Mr. Nice, Bombasto and RubberMan undertook the job,
536+
and finally it was assigned to Bombasto:
537+
538+
figure.image-display
539+
img(src="/resources/images/devguide/component-communication/assign-job-ui.png" alt="Assign a job to a hero")
540+
541+
:marked
542+
Now, we implemented the basic workflow. We could have been satisfied with the component communication scenarios we've implemented by now, but there's still a
543+
few things that we should discuss.
506544

507-
## #8: A mailcious hero steals the job &mdash; issues with multiple facades
545+
## #8: A malicious hero steals the job &mdash; issues with multiple facades
508546

509547
_Content_
510548

44.5 KB
Loading

0 commit comments

Comments
 (0)