Romaji to Kana Converter: Angular & Express — Chapter 4, Exercise 2 ===================================================================== TASK Build RomajiInputComponent and KanaOutputComponent exactly as this chapter describes, wire them into app.ts and app.html in place of Chapter 1's ping check, and confirm typing "sushi" into the input field correctly displays すし and スシ in the output panel -- including confirming the @else placeholder text is what shows before anything has been typed. SOLUTION // romaji-input/romaji-input.ts import { Component, inject } from '@angular/core'; import { ConvertService } from '../convert.service'; @Component({ selector: 'app-romaji-input', templateUrl: './romaji-input.html', styleUrl: './romaji-input.css', }) export class RomajiInputComponent { private convertService = inject(ConvertService); onInput(value: string) { this.convertService.convertText(value); } } // romaji-input/romaji-input.html // kana-output/kana-output.ts import { Component, inject } from '@angular/core'; import { ConvertService } from '../convert.service'; @Component({ selector: 'app-kana-output', templateUrl: './kana-output.html', styleUrl: './kana-output.css', }) export class KanaOutputComponent { private convertService = inject(ConvertService); hiragana = this.convertService.hiragana; katakana = this.convertService.katakana; hasResult = this.convertService.hasResult; } // kana-output/kana-output.html @if (hasResult()) {

Hiragana: {{ hiragana() }}

Katakana: {{ katakana() }}

} @else {

Type something above to see the conversion.

} // app.ts import { Component } from '@angular/core'; import { RomajiInputComponent } from './romaji-input/romaji-input'; import { KanaOutputComponent } from './kana-output/kana-output'; @Component({ selector: 'app-root', templateUrl: './app.html', styleUrl: './app.css', imports: [RomajiInputComponent, KanaOutputComponent], }) export class AppComponent {} // app.html

Romaji to Kana Converter

Run with: ng serve, then open http://localhost:4200 Confirmed behavior: On first load (nothing typed yet): Type something above to see the conversion. After typing "sushi" into the input field: Hiragana: すし Katakana: スシ WHY THIS WORKS AS AN ANSWER ---------------------------- The @else branch correctly renders on first load, since hasResult() reads a computed signal whose two underlying writable signals both start as empty strings -- length 0, so hasResult() is false and the placeholder shows instead of the
block. Typing "sushi" fires the (input) event on every keystroke, calling onInput(), which calls convertText('sushi') on the shared service; that call runs Chapter 3's own finished convert() function and sets both signals to real values, which flips hasResult() to true and swaps which branch of the @if/@else block renders -- all without RomajiInputComponent and KanaOutputComponent ever referencing each other directly, since both are only ever talking to the one shared ConvertService instance.