Romaji to Kana Converter: Angular & Express — Chapter 4, Exercise 1 ===================================================================== TASK Extend ConvertService exactly as this chapter describes (the private writable signals, the public asReadonly() views, the computed hasResult, and convertText()). Without building either component yet, call service.convertText('konnichiwa') directly and confirm service.hiragana() and service.katakana() return the correct real strings afterward, and that service.hasResult() is false before the call and true after it. SOLUTION // convert.service.ts (the real Chapter 4 version) import { Injectable, inject, signal, computed } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { convert } from './conversion-engine'; @Injectable({ providedIn: 'root' }) export class ConvertService { private http = inject(HttpClient); private readonly hiraganaResult = signal(''); private readonly katakanaResult = signal(''); readonly hiragana = this.hiraganaResult.asReadonly(); readonly katakana = this.katakanaResult.asReadonly(); readonly hasResult = computed(() => this.hiraganaResult().length > 0); ping() { return this.http.get('http://localhost:4000/api/ping'); } convertText(romaji: string): void { const result = convert(romaji); this.hiraganaResult.set(result.hiragana); this.katakanaResult.set(result.katakana); } } Exercising it directly, bypassing HttpClient by constructing the class by hand in a plain script (no Angular test harness needed, since nothing here actually calls http yet): const service = new ConvertService(); console.log('before:', service.hasResult()); service.convertText('konnichiwa'); console.log('hiragana:', service.hiragana()); console.log('katakana:', service.katakana()); console.log('after:', service.hasResult()); Output (verified by hand-running the real service against the finished Chapter 3 engine): before: false hiragana: こんにちは katakana: コンニチハ after: true WHY THIS WORKS AS AN ANSWER ---------------------------- It confirms the service's own state genuinely starts empty (hasResult() is false before anything has been converted), that a single call to convertText() correctly populates both hiraganaResult and katakanaResult signals with the real, already-verified output from Chapter 3's own convert() (including こんにちは coming out correctly rather than the こんにちわ Chapter 2 originally produced), and that hasResult() correctly flips to true once real content exists — proving the computed signal genuinely derives from the two underlying writable signals rather than being set independently.