You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Within the template definition of `InvitedHero`, we can use the `hero` property to display data, ie. the name of the hero:
182
187
@@ -303,15 +308,133 @@ figure.image-display
303
308
304
309
:marked
305
310
## #4: A hero takes the job — child to parent communication — antipattern
306
-
_Content_
311
+
312
+
Let's make a step further. In this scenario, we implement the functionality of the **I'll take it!** button &mdash show how a child can communicate
313
+
with its parent. What if the parent were expose a function — let's call it `heroTakesJob()` — and an `InvitedHero` instance called it? It sounds viable,
314
+
but the child component needs to have a reference to its parent. Well, with data binding, the parent can hand a reference on itself to its children, can't it?
315
+
The temptation of doing child-to-parent communication this way is great. In this scenario, we demonstrate how easy it is.
316
+
317
+
.alert.is-critical
318
+
:marked
319
+
** What we are going to do, is a bad practice to avioid — it is an antipattern.** We still demonstrate it, and then explain why it is bad.
320
+
:marked
321
+
Let's append two methods to `HeroJobBoard`, `heroTakesJob` and `getJobBoard`, respectively. As the names suggest, the first carries out
322
+
the administration of taking a heros response, the second retrieves the `HeroJobBoard` instance:
With a slight modification in the `HeroJobBoard` template, the parent can hand itself to its children, provided `InvitedHero` has an input property,
328
+
`jobBoard`, which stores this reference:
329
+
330
+
+makeExample('component-communication/ts/take-job-antipattern/fragments/invited-hero.html', null, 'hero-job-board.ts (extract from template)', {blk:/(job-board)/g})
331
+
332
+
:marked
333
+
The `InvitedHero` component can obtain this reference as soon as we specify its `jobBoard` property. We add the `undertakeJob()` method, too, and implement
334
+
it so that it invokes the parent's' `heroTakesJob()` method through `this.jobBoard`.
Distributing the application's set of responsibilities among components that have clean boundaries is a great way to ensure that our app
353
+
is easier to implement, maintain, test, and fix. In this chapter, instead of having a monolithic application component, we created three components, `HeroJobApp`, `HeroJobBoard`,
354
+
and `InvitedHero`. Each component has well-defined responsibilities. `InvitedHero` does two things: first, it can receive and display job request notifications,
355
+
second, it can notify int context — its parent — about the fact that a hero undertakes the job.
356
+
357
+
In this scenario, we used tightly-coupled binding between an `InvitedHero` instance and its parent, `HeroJobBoard`. This design hurts the autonomy
358
+
of both component types, and does not help a real separation of responsibilities. Instead of simply notifying `HeroJobBoard` and trust in that it can administer
359
+
the list of responding heroes, `InvitedHero` takes over this task, and invokes the `heroTakesJob()` method directly.
360
+
361
+
Eventually, this pattern makes our application fragile — so we take it into account as an _antipattern_.
362
+
363
+
By passing a reference to itself, `HeroJobBoard` offers its entire functionality to an `InvitedHero` instance. This way nothing can prevent `InvitedHero` to use
364
+
other `HeroJobBoard` operations, for example, it could invoke even `announceJob()`, and that hurts our workflow design as badly as the autonomity of components. Giving up
365
+
the fundamental desing principles based on properly used and clean component decomposition prevents us from creating easily maintainable and testable software,
366
+
and creates technical debt.
367
+
368
+
.alert.is-important
369
+
:marked
370
+
The antipattern we used in this scenario couples only two component types. In real applications, there are often deeper component trees with longer chains of
371
+
parents, children, grandchildren, and so on. This issues coming from this kind of tight coupling among components definitely worsen the situation, and causes more
372
+
maintainance and testing headaches — while increasing the technical debt, often exponentially.
373
+
:marked
308
374
## #5: A hero takes the job — child to parent communication with events
309
375
310
-
_Content _
376
+
Knowing that the the tight coupling we used in the previous scenario is an antipattern, it's time to learn how we can avoid such a situation with Angular.
377
+
378
+
The task we want to solve correctly is that `InvitedHero` needs to notify its parent `HeroJobBoard` that a hero undertaks the requested job so that the hero can be
379
+
added to the list of responding ones. We will use _event binding_ to communicate from a children to its parent. We create an _output property_ on `InvitedHero`,
380
+
and in the template of `HeroJobBoard` — where `InvitedHero` is specified with the `<invited-hero>` element — we bind the output property to a
381
+
`HeroJobBoard` event handler method.
382
+
383
+
We can easily add an output property to `InvitedHero`. We name this new property `onHeroResponse`, and use it within the `undertakeJob()` method:
Being an output parameter means that an external component can bind an event handler method in its template that responds the event raised with the object
400
+
behind the output parameter. As the body of `undertakJob()` shows, we can notify the event that a hero undertook the job through calling
401
+
`this.onHeroResponse.next(this.hero)`. The `onHeroResponse` property's type is `EventEmitter<Hero>`. As its name suggest, an instance of this type can emit events
402
+
that are represented with arguments of type `Hero`. In our very case, with the event we pass the `Hero` instance that undertook the job.
403
+
404
+
.alert.is-helpful
405
+
:marked
406
+
The `EventEmitter` generic type is built on the features of the `Observable` type of ECMAScript standard library. `Observable` is a type that
407
+
can be used to model push-based data sources such as DOM events, timer intervals, and sockets. Understanding this type requires a detailed discussion,
408
+
and in this chapter we are not going to leverage the features of observables.
409
+
410
+
`EventEmitter` can be used not only for output parameters, but in other publish-subscribe scenarios, as we will learn it soon.
411
+
:marked
412
+
`HeroJobBoard` can bind itself to the `onHeroResponse` event:
413
+
414
+
+makeExample('component-communication/ts/take-job-event/fragments/invited-hero.html', null, 'hero-job-board.ts (extract from template)', {blk:/(\(on-hero-response\))/g})
415
+
416
+
:marked
417
+
The event is bound to the `heroTakesJob()` method, and `$event` represents the `Hero` instance passed by `InvitedHero` as the event argument, which is the hero who
418
+
undertook the job. The `heroTakesJob()` method is exactly the same as we used in the previous scenario:
Now, with these simple steps, our app works just like at the end of the previous scenario — but this time it uses a supported child-to-parent communication pattern.
424
+
425
+
### Why this practice is preferred
426
+
427
+
In this scenario, out components kept their autonomy, in constrast to the antipattern treated in the previous scenario. `InvitedHero` gets the job request through an
428
+
input property, but actually does not know from which component the job request is bound to its `request` property. Similarly, when a hero undertakes the job,
429
+
`InvitedHero` emits an event through its `onHeroResponde` output property without knowing which component (or components) listen to this event.
430
+
431
+
This architecture allows easy maintenance and testing, as our components encapsulate everything they are responsible for. They do not have strong dependencies on
432
+
other components. When we face with child-to-parent communication, using events as we did in this scenario is a _viable way to avoid the traps and fragility_ of the
433
+
antipattern demonstrated in the previous section.
311
434
312
435
## #6: Refactoring communication to an intermediary service
313
436
314
-
_Content_
437
+
There is still a tiny flaw in our inter-component communication design. The `HeroJobBoard` and `InvitedHero` components >>> _to be continued_
315
438
316
439
## #7: Assigning the job to a hero — extending the intermediary service
0 commit comments