Skip to content

Commit 868073e

Browse files
Dotneteerwardbell
authored andcommitted
Scenario #3 completed
1 parent d83b401 commit 868073e

File tree

7 files changed

+131
-12
lines changed

7 files changed

+131
-12
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* #docregion */
2+
.announced {
3+
color: red;
4+
border: 2px solid red;
5+
background-color: #ffdddd;
6+
}
7+
/* #enddocregion */
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!-- #docregion -->
2+
<input #box (keyup)='0' style='width: 400px;' [value]='jobRequest' />
3+
<button (click)='announceJob(box.value)'
4+
[disabled]='!box?.value.trim().length > 0'>
5+
Ask
6+
</button>
7+
<!-- #enddocregion -->
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- #docregion -->
2+
<invited-hero *ng-for='#hero of invitedHeroes'
3+
[hero]='hero' [request]='jobRequest'>
4+
</invited-hero>
5+
<!-- #enddocregion -->

public/docs/_examples/component-communication/ts/send-job-request/src/app/hero-job-board.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,8 @@ export class HeroJobBoard{
7676
];
7777
respondingHeroes: Hero[] = [];
7878
invitedHeroes: Hero[] = [];
79-
jobRequest: string = null;
80-
81-
inviteAllHeroes() {
82-
this.invitedHeroes = this.heroes;
83-
this.respondingHeroes = [];
84-
this.jobRequest = null;
85-
}
8679

80+
// #docregion get-job-status
8781
getJobStatus() {
8882
if (!this.jobRequest) {
8983
return "No job request announced"
@@ -94,8 +88,18 @@ export class HeroJobBoard{
9488
: "No responding heroes";
9589
}
9690
}
91+
// #enddocregion get-job-status
92+
// #docregion announce
93+
jobRequest: string = null;
94+
95+
inviteAllHeroes() {
96+
this.invitedHeroes = this.heroes;
97+
this.respondingHeroes = [];
98+
this.jobRequest = null;
99+
}
97100

98101
announceJob(request) {
99102
this.jobRequest = request.trim();
100103
}
104+
// #enddocregion announce
101105
}

public/docs/_examples/component-communication/ts/send-job-request/src/app/invited-hero.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Hero} from './hero';
33

44
@Component({
55
selector: 'invited-hero',
6+
// #docregion template
67
template: `
78
<div class='invited-hero'>
89
<h3 class='hero-name'>Job Request for {{hero.name}}</h4>
@@ -15,6 +16,8 @@ import {Hero} from './hero';
1516
</button>
1617
</div>
1718
`,
19+
// #enddocregion template
20+
// #docregion styles
1821
styles: [`
1922
.invited-hero {
2023
margin: 8px;
@@ -38,6 +41,8 @@ import {Hero} from './hero';
3841
}
3942
`]
4043
})
44+
// #enddocregion styles
45+
// #docregion component
4146
export class InvitedHero {
4247
@Input() hero : Hero;
4348
@Input() request: string;
@@ -46,4 +51,5 @@ export class InvitedHero {
4651
return this.request
4752
? this.request : "No job announced";
4853
}
49-
}
54+
}
55+
// #enddocregion

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

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ figure.image-display
195195
+makeExample('component-communication/ts/invite-heroes/src/app/hero-job-board.ts', 'invite-heroes', 'hero-job-board.ts', {blk: /(invitedHeroes|inviteAllHeroes\(\))/g})
196196

197197
:marked
198-
With these changes, we completed the goal of this scenario. When users start the app, only the Hero Job Board panel is visible, but after clicking the
198+
With these changes, we completed this scenario. When users start the app, only the Hero Job Board panel is visible, but after clicking the
199199
invitation button, heroes are ready to listen to job requests &mdash; as their names in the panels indicate:
200200

201201
figure.image-display
@@ -206,15 +206,105 @@ figure.image-display
206206
**Always include the parenthesis!** Always use `@Input()`. It's easy to forget the parentheses. Our application will fail mysteriously if we do.
207207
:marked
208208
.l-main.section
209+
:marked
210+
### An alternative method of defining an input property
211+
212+
Instead of using the `@Input()` annotation, we can use the `inputs` annotation property of `@Component()` to specify input properties.
213+
214+
Originally we defined `InvitedHero` this way:
215+
216+
code-example(format='linenums', language='typescript').
217+
@Component({
218+
...
219+
})
220+
export class InvitedHero {
221+
@Input() hero: Hero;
222+
}
223+
224+
:marked
225+
Using the alternative method, we could have defined the `hero` input property this way:
226+
227+
code-example(format='linenums', language='typescript').
228+
@Component({
229+
inputs: ['hero']
230+
})
231+
export class InvitedHero {
232+
hero: Hero;
233+
}
234+
235+
:marked
236+
.alert.is-helpful
237+
:marked
238+
In this chapter, we are age going to use the `@Input()` annotation.
239+
:marked
240+
.l-main.section
209241
:marked
210242
## #3: Announcing a job &mdash; parent to child communication with multiple data binding
211243

212-
_Content_
244+
The aim of the Hero Job Board application is to use our heroes superpower. So, let's create job requests for them! In this scenario, we enable the person in need to
245+
create a job request and send them to heroes by clicking the **Ask** button.
213246

214-
## #4: A hero takes the job &mdash; child holds a direct reference to parent &mdash; antipattern
247+
We are going to use data binding from `HeroJobBoard` (parent) to `InvitedHero` (child), similarly as we did in the previous scenario. However, this time we bind another
248+
property, and demonstrate that in the child template we can use the input property indirectly, and Angular still detects the changes, and updates the UI accordingly.
215249

216-
_Content_
250+
We add the `request` property to `InvitedHero` so that `HeroJobBoard` could pass it a new job request. The `getRequest()` method leverages this property to fall back
251+
to a default lable when no job request is available:
252+
253+
+makeExample('component-communication/ts/send-job-request/src/app/invited-hero.ts', 'component', 'invited-hero.ts (component)', {blk: /(request|getRequest\(\))/g})
254+
255+
:marked
256+
Beside the `request` property (that has two occurrences), the modified template of `InvitedHero` uses `getRequest()`, too:
217257

258+
+makeExample('component-communication/ts/send-job-request/src/app/invited-hero.ts', 'template', 'invited-hero.ts (template)', {pnk: /(getRequest\(\))/g})
259+
260+
:marked
261+
`HeroJobBoard` defines an input box and and a button within its template to send the new job request to heroes. We modify this part of the template so that a job request
262+
should contain at least one non-whitespace character to send:
263+
264+
+makeExample('component-communication/ts/send-job-request/fragments/input.html', null, 'hero-job-board.ts (extract from template)')
265+
266+
:marked
267+
The value of the input control is refreshed when the `jobRequest` properties value is changed &mdash; it is done within the code of `HeroJobBoard` &mdash; or users type
268+
into the text field. To store the value of the text field in the `box` local variable, we're using the technique demonstrated in the [User Input](./user-input.html) chapter.
269+
The **Ask** button uses this `box` variable to define an expression that disables the button unless at least one non-whitespace charecter is typed in.
270+
271+
.alert.is-helpful
272+
:marked
273+
The expression uses the Angular "Elvis" operator (`?.`). This is a fluent and convenient way to guard against null and undefined values in property paths.
274+
It protects against a view render failure if `box` is null.
275+
:marked
276+
When users click the **Ask** button, the `announceJob()` method is invoked with the content of the input text field.
277+
278+
+makeExample('component-communication/ts/send-job-request/src/app/hero-job-board.ts', 'announce', 'hero-job-board.ts', {blk: /(inviteAllHeroes\(\)|announceJob\(request\))/g})
279+
280+
:marked
281+
When announcing a new job request, the `jobRequest` property is set. The value of this property is deleted (set to null) within `inviteAllHeroes()`,
282+
so users can click the **Invite heroes** to reset the application, as this action will delete the job request, too.
283+
284+
To push the `jobRequest` value to all `InvitedHero` instances, we need to modify the `HeroJobBoard` template:
285+
+makeExample('component-communication/ts/send-job-request/fragments/request.html', null, 'hero-job-board.ts (extract from template)', {blk: /(request)/g})
286+
287+
:marked
288+
We're almost done. Now, data binding works as expected, but to make the app reflect its current workflow state, we add a few cosmetics. In `HeroJobBoard` we define
289+
the `getJobStatus()` function that retrieves an appropriate label to show instead of "Responding reroes":
290+
291+
+makeExample('component-communication/ts/send-job-request/src/app/hero-job-board.ts', 'get-job-status', 'hero-job-board.ts')
292+
293+
:marked
294+
To visually mark that there's a new job request for a hero, we append a new style (to the `styles` annotation property) to `InviteHero`:
295+
296+
+makeExample('component-communication/ts/send-job-request/fragments/announced.css', null, 'hero-job-board.ts (extract from styles)')
297+
298+
:marked
299+
Running the application, the person in need can broadcast a job request to all invited heroes:
300+
301+
figure.image-display
302+
img(src="/resources/images/devguide/component-communication/send-job-request-ui.png" alt="Job request sent")
303+
304+
:marked
305+
## #4: A hero takes the job &mdash; child to parent communication &mdash; antipattern
306+
_Content_
307+
218308
## #5: A hero takes the job &mdash; child to parent communication with events
219309

220310
_Content _
Loading

0 commit comments

Comments
 (0)