Angularin komponenttien välinen kommunikaatio on yksi kehityksen keskeisistä haasteista, ja sen toteuttaminen voi vaikuttaa sovelluksen ylläpidettävyyteen, erityisesti kun pyritään estämään komponenttien liian tiukka kytkentä toisiinsa. Tässä käsitellään kahta pääasiallista tapaa, joilla Angular-komponentit voivat kommunikoida keskenään: vanhemman ja lapsen välinen tiedonsiirto, sekä sisarusten välinen vuorovaikutus käyttäen Subject-mekanismia.

Kun rakennetaan komponentteja, jotka tarvitsevat toistensa dataa, Angularissa voidaan käyttää yksinkertaisia tiedonsiirtotapoja, kuten @Input() ja @Output(). Nämä tarjoavat tavan välittää tietoja komponentilta toiselle ilman, että tarvitsee suoraan linkittää niitä toisiinsa, mutta tällainen lähestymistapa tuo mukanaan tiukkaa riippuvuutta. Esimerkiksi, kun CitySearchComponent käyttää @Output-ominaisuutta tiedon lähettämiseen vanhemmalle komponentille, se tarkoittaa, että kaikki muutokset ja tapahtumat tulevat tiedoksi vain yhdelle komponentille, mikä rajoittaa sovelluksen joustavuutta ja uudelleenkäytettävyyttä.

Kuvitellaanpa, että meillä on CitySearchComponent, joka lähettää käyttäjän hakutiedot AppComponent-komponentille. AppComponent puolestaan käsittelee näitä tietoja ja kutsuu säilytystietoja hakevaa WeatherService-palvelua, joka palauttaa säilytystiedot, kuten kaupungin sään ja muun siihen liittyvän datan. Vaikka tämä lähestymistapa toimii hyvin yksinkertaisissa sovelluksissa, se ei ole paras ratkaisu laajempaan ja monimutkaisempaan sovellusarkkitehtuuriin.

Vanhemman ja lapsen välinen kommunikaatio

Tässä esitetään esimerkki siitä, miten @Output() ja @Input() voivat toimia Angularissa:

  1. CitySearchComponent:
    CitySearchComponent käyttää EventEmitter-komponenttia ja lähettää tapahtuman vanhemmalle komponentille AppComponent:

    typescript
    import { Component, OnInit, Output, EventEmitter } from '@angular/core';
    export class CitySearchComponent implements OnInit {
    @Output() searchEvent = new EventEmitter<string>(); ngOnInit() {
    this.search.valueChanges.pipe(debounceTime(1000)).subscribe((searchValue: string) => {
    if (!this.search.invalid) { this.searchEvent.emit(searchValue); } }); } }

    Tällöin AppComponent saa tämän tiedon ja hakee säilytystiedot:

    typescript
    import { WeatherService } from './weather/weather.service';
    import { ICurrentWeather } from './interfaces'; export class AppComponent { currentWeather: ICurrentWeather; constructor(private weatherService: WeatherService) {} doSearch(searchValue: string) { const userInput = searchValue.split(',').map(s => s.trim()); this.weatherService .getCurrentWeather(userInput[0], userInput.length > 1 ? userInput[1] : undefined)
    .subscribe(data => this.currentWeather = data);
    } }

    Tässä vaiheessa voimme liittää AppComponentin näkymään currentWeather-datan ja käyttää sitä haluamallamme tavalla.

Tällöin olemme luoneet yksinkertaisen, mutta tiukasti kytketyn rakenteen. Vanhempi komponentti hallitsee lapsen komponenttia ja käsittelee kaikki sen tiedot, mutta tätä lähestymistapaa voidaan pitää rajoittavana, erityisesti laajemmissa projekteissa.

Sisarusten välinen kommunikaatio käyttäen Subject-mekanismia

Kun haluamme vähentää komponenttien välistä tiukkaa kytkentää, voimme siirtyä käyttämään Subject-mekanismia, joka on osa RxJS-kirjastoa. Subject yhdistää sekä Observable että Observer-toiminnot, eli komponentti voi sekä vastaanottaa että lähettää tietoa. Tämä on tehokas tapa hallita komponenttien välistä vuorovaikutusta ilman, että niitä täytyy suoraan liittää toisiinsa.

Esimerkiksi, jos meillä on WeatherService, voimme käyttää BehaviorSubject-objektia, joka on eräänlainen Subject, joka muistuttaa aina viimeisimmän lähetetyn tiedon:

typescript
import { BehaviorSubject, Observable } from 'rxjs'; export class WeatherService {
readonly currentWeather$ = new BehaviorSubject<ICurrentWeather>({
city: '--', country: '--', date: Date.now(), image: '', temperature: 0, description: '', }); updateCurrentWeather(search: string | number, country?: string): void { this.getCurrentWeather(search, country) .subscribe(weather => this.currentWeather$.next(weather)); } }

Tässä BehaviorSubject pitää yllä viimeisintä säilytystietoa ja lähettää sen aina tilaajille, kun tiedot muuttuvat. Tämä mahdollistaa komponenttien vastaanottaa ja lähettää tietoa ilman, että niiden täytyy olla suorassa yhteydessä toisiinsa. WeatherService tarjoaa palvelun, johon kaikki komponentit voivat liittyä ja kuunnella sen tilan muutoksia.

Joustavuus ja laajennettavuus

Vaikka @Input ja @Output ovat käteviä ja toimivat hyvin yksinkertaisissa sovelluksissa, ne voivat nopeasti tulla rajoittaviksi, kun halutaan luoda dynaamisempia ja laajempia sovelluksia. Subject-mekanismin käyttö antaa meille enemmän joustavuutta ja erottelua komponenttien välillä, mutta se vaatii myös huolellista suunnittelua ja resursseja, koska se voi johtaa monimutkaisempaan tilanhallintaan ja suorituskyvyn haasteisiin, kuten muistivuotoihin, erityisesti jos käytetään ReplaySubject-tyyppisiä objekteja.

Yhteenveto

On tärkeää ymmärtää, että Angularissa komponenttien välinen vuorovaikutus ja tiedonsiirto voi olla monivaiheinen prosessi, joka vaatii tarkkaa suunnittelua. Yksinkertaiset @Input ja @Output mekanismit voivat toimia hyvin pienissä projekteissa, mutta suuremmissa sovelluksissa on järkevää käyttää tehokkaampia RxJS:n Subject-ratkaisuja. Tämä lähestymistapa lisää joustavuutta ja vähentää komponenttien tiukkaa kytkentää, mutta tuo myös mukanaan lisää monimutkaisuutta ja haasteita.

Miten hallita postinumeroiden kansainvälisiä haasteita Angular-sovelluksessa?

Kun rakennamme Angular-sovelluksia, joissa käyttäjä syöttää hakukenttään esimerkiksi postinumeron tai kaupungin nimen, törmäämme helposti haasteisiin syötteen monimuotoisuuden kanssa. Perinteisesti monet sovellukset tukevat vain yksinkertaisia, esimerkiksi Yhdysvaltain viisinumeroisia postinumeroita. Tämä kuitenkin rajoittaa sovelluksen käyttöä kansainvälisesti, sillä postinumerot voivat vaihdella suuresti eri maissa. Iso-Britannian monimutkaiset postinumerot kuten "EC2R 6AB" havainnollistavat hyvin tätä monimuotoisuutta. Pelkkä syötteen muodon tarkistus ei riitä estämään virheitä, sillä käyttäjä voi kirjoittaa postinumeron väärin, mutta kuitenkin niin, että se näyttää muodollisesti oikealta.

Tämän vuoksi on järkevää luottaa kolmannen osapuolen palveluihin, jotka voivat tarkistaa postinumeron oikeellisuuden ja palauttaa siihen liittyvät maantieteelliset tiedot. Esimerkiksi geonames.org tarjoaa ilmaisen rajapinnan, jonka avulla voidaan ratkaista, onko annettu merkkijono kelvollinen postinumero, ja tarvittaessa palauttaa koordinaatit, joiden perusteella voidaan hakea säädataa tai muuta paikannustietoa.

Palvelu toteutetaan Angularissa erillisenä postinumeroihin keskittyvänä palveluna (PostalCodeService), joka noudattaa määriteltyä rajapintaa (IPostalCodeService). Tämä rajapinta mahdollistaa yksikkötestauksen ja palvelun helposti korvattavan version kehittämisen myöhemmässä vaiheessa. Lisäksi rajapintojen käyttö tukee Test-Driven Development -menetelmiä ja helpottaa tiimin sisäistä kommunikointia, sillä kehittäjät tietävät tarkalleen, mitä odottaa kutsuista.

Postinumeroiden tarkistaminen toteutetaan asynkronisesti RxJS-observablien avulla. Huolellinen ketjutus (chaining) mahdollistaa monivaiheisen tiedonhakuputken, jossa ensin kutsutaan postinumeroiden palvelua ja sen jälkeen, saadun paikkatiedon perusteella, haetaan säätiedot toisesta API:sta. SwitchMap-operaattorin avulla voidaan siirtyä sujuvasti observablen sisäisestä datasta seuraavaan asynkroniseen kutsuun ilman, että tilanhallinta monimutkaistuu.

Tämä lähestymistapa myös vähentää koodin boilerplate-määrää ja tekee sovelluksesta ylläpidettävämmän. Samalla koodi irrotetaan sovelluksen elinkaaren hallinnasta – esimerkiksi ngOnInit-metodin rooli vähenee, koska asynkroninen datavirta ei ole sidottu komponentin elinkaareen. Tämä helpottaa yksikkötestausta ja parantaa komponenttien uudelleenkäytettävyyttä.

On tärkeää ymmärtää, että vaikka ulkoisen palvelun käyttö tuo mukanaan luotettavuutta ja kattavuutta, se myös edellyttää ympäristömuuttujien huolellista hallintaa, kuten käyttäjätunnusten tallentamista ympäristöasetuksiin ja HTTPS:n varmistamista tuotantoympäristössä, jotta ei synny turvallisuusvaroituksia tai toimintavirheitä.

Lisäksi on hyvä huomata, että postinumeroiden ja kaupunkien välinen ero ei ole pelkästään tekninen haaste vaan myös käyttökokemuksellinen. Sovelluksen tulee olla käyttäjäystävällinen ja sietää virheellisiä syötteitä siten, että se ohjaa käyttäjää korjaamaan virheen tai ehdottaa vaihtoehtoja ilman turhautumista.

Tämän lähestymistavan ymmärtäminen auttaa kehittäjää rakentamaan kansainvälisesti skaalautuvia, testattavia ja ylläpidettäviä Angular-sovelluksia, jotka ovat valmiita kohtaamaan todellisen maailman syötteiden moninaisuuden. Koodin asynkronisuus, observablien käyttö ja ulkoisten API-kutsujen ketjutus muodostavat yhdessä perustan joustavalle ja laadukkaalle sovellusarkkitehtuurille.

Miksi Monorepo voi olla tehokas valinta, mutta ei aina paras ratkaisu?

Monorepo, eli monoliittinen arkistointistrategia, on ohjelmistokehityksessä käytetty lähestymistapa, jossa useiden projektien koodi säilytetään yhdessä arkistossa. Tämä mahdollistaa yhtenäisen versionhallinnan, yksinkertaistetun riippuvuuksien hallinnan ja helpomman koodin jakamisen eri projektien välillä. Erityisesti monorepojen etuja ovat niiden kyky yhdistää useita projekteja saman kehitysympäristön alle, mikä mahdollistaa helpomman koodin tarkastelun ja jakamisen eri projekteissa, kuten TypeScript-liitäntöjen jakamisen frontendin ja backendin välillä, mikä varmistaa, että tiedonobjektit pysyvät synkronoituna.

Monorepoiden käyttö perustuu siihen, että kehittäjät voivat työskennellä useissa projekteissa samassa IDE-ikkunassa. Tämä voidaan saavuttaa käyttämällä VS Code -editorin monilähteisiä työtiloja, joissa useita projekteja voi lisätä samaan tarkastelupaneeliin. Monorepo yhdistää kuitenkin projektit versionhallinnan tasolle, jolloin niitä voidaan rakentaa yhdessä CI-palvelimella. Tämä tuottaa etuja, kuten yhtenäiset koodin tarkastukset ja helpomman kehityksen koordinoinnin.

Monorepo mahdollistaa myös atomisten muutosten tekemisen, jolloin eri projekteissa tapahtuvat muutokset voidaan yhdistää yhteen commit-merkintään. Tämä tuo mukanaan merkittäviä etuja, kuten sen, että kehittäjät voivat työskennellä samanaikaisesti eri projekteissa ilman, että heidän tarvitsee huolehtia koordinoimisesta useiden eri repositorioiden, käyttöönottojen ja järjestelmien välillä. Kaikki prosessit, jotka liittyvät koodin laadun ylläpitoon ja standardeihin, yksinkertaistuvat, koska kaikki tapahtuu yhdessä paikassa: yksi pull request tarkistettavaksi, yksi käyttöönotto varmistettavaksi ja yksi joukko tarkistuksia, joita täytyy noudattaa.

Monorepon käyttö ei kuitenkaan ole ongelmatonta. Suurissa sovelluksissa, joissa koodi on jaettu lukuisiin tiedostoihin, saattaa syntyä merkittäviä haasteita. Tässä vaiheessa kaikki kehittäjät tarvitsevat huippuluokan laitteistot ja CI/CD-palvelimet, jotka vaativat kalliita ja tehokkaita resursseja. Lisäksi monorepon käyttöönotto voi tehdä automaattisesta käyttöönotosta huomattavasti monimutkaisempaa. Uuden tiimin jäsenen on myös opittava monen eri teknologian hallinta, mikä voi olla haastavaa ja aikaa vievää.

Vaikka monorepot olivat käytännössä hankalia suurimmalle osalle yrityksistä 2000-luvun alussa, tilanne muuttui merkittävästi vuonna 2019, kun Google julkaisi avoimen lähdekoodin Bazel-rakennustyökalun. Tämä avasi mahdollisuuden pienemmän mittakaavan projekteille hyödyntää monorepo-konseptia. Nykyään JavaScriptin ja TypeScriptin maailmassa Nx on noussut suosituksi työkaluksi monorepojen hallintaan. Nx tarjoaa seuraavan sukupolven rakennusjärjestelmän, joka tukee monorepoja ja tarjoaa tehokkaita integraatioita.

Nx:n käyttöönotto ja siihen liittyvä arkkitehtuuri voivat olla erittäin hyödyllisiä erityisesti suurille tiimeille ja yrityksille, sillä se tarjoaa miellyttävän rakenteen ja mahdollistaa dynaamisen yhteistyön. Nx:n käyttöliittymä, joka on yhteensopiva Angularin kanssa, tuo erinomaisen kehityskokemuksen myös suuriin sovelluksiin, ja se mahdollistaa rinnakkaistoteutuksen ja hajautetun välimuistin hyödyntämisen rakennusprosessissa ilman, että tiimi tarvitsee investoida kalliisiin infrastruktuureihin. Sen avulla voidaan hallita monimutkaisempia ja laajempia projekteja ilman, että jokaisen työkalun konfigurointi ja ylläpito vie liikaa aikaa ja resursseja.

On kuitenkin tärkeää huomata, että monorepojen käyttöönotossa on omat kustannuksensa. Etenkin monimutkaisissa projekteissa, joissa käytetään useita teknologioita, kuten JavaScript, TypeScript, Git, Nx ja Angular, syntyy suuri kognitiivinen kuorma. Tällöin jokainen työkalu vaatii asiantuntemusta ja huolellista konfigurointia. Nykyiset ohjelmistot ja laitteistot pystyvät käsittelemään Angular-sovelluksia, joissa on satoja komponentteja, varsin nopeasti. Esbuildin ja Viten käyttöönotto tuo parannuksia myös näihin rakennusprosessien nopeuksiin.

Kun valitset, onko monorepo oikea ratkaisu, on tärkeää arvioida tarkasti projektin tarpeet. Monorepon käyttöönotto ei ole aina ratkaisu kaikkiin ongelmiin, ja se voi tuoda tullessaan monimutkaisuutta, jota ei aina tarvita. Siksi on suositeltavaa miettiä, voiko tiimi hyötyä yksinkertaisemmista ratkaisusta, ennen kuin ottaa käyttöön raskaampia työkaluja. Loppujen lopuksi tärkeintä on valita teknologiat, jotka palvelevat parhaiten projektin erityistarpeita ilman, että projekti ylikuormittuu tarpeettomilla työkaluilla ja monimutkaisuuksilla.

Kuinka rakentaa käyttäjähallintapalvelu ja monivaiheiset lomakkeet Angular-sovellukseen

Kun rakennat käyttäjähallintajärjestelmää Angular-sovellukseen, on tärkeää ottaa huomioon useita keskeisiä tekijöitä. Erityisesti, jos sovelluksessa on monivaiheisia lomakkeita ja käyttäjän profiilin päivittämistä, kannattaa kiinnittää huomiota sekä toiminnallisuuteen että sovelluksen suorituskykyyn. Tässä osassa käsittelemme käyttäjäpalvelun luomista, käyttäjätietojen hakemista ja päivittämistä, sekä lomakkeiden käsittelyä Angular-sovelluksessa.

Ennen kuin luot palvelun, varmista, että taustapalvelin on käynnistetty ja että sovelluksen AuthMode on asetettu CustomServer-tilaan. Voit käyttää komentoa npm run start:backend käynnistääksesi palvelimen ja tietokannan. Tämä valmistaa taustan, jonka avulla voit käsitellä käyttäjätietoja turvallisesti.

Käyttäjäpalvelun luominen

Käyttäjäpalvelu on keskeinen osa käyttäjähallintaa. Se hoitaa tietojen hakemisen ja päivittämisen serveriltä. Ensimmäiseksi luodaan UserService palvelu, joka sisältää seuraavat toiminnot: getUser ja updateUser. Näiden avulla voidaan noutaa käyttäjätietoja ja päivittää niitä.

Palvelun luomiseksi tarvitset ensin IUserService -rajapinnan, joka määrittelee tarvittavat metodit, kuten getUser ja updateUser. Rajapinnan avulla eristämme käyttöliittymän (UI) ja toiminnallisuuden, jolloin voimme helposti vaihtaa taustapalvelimia ilman, että meidän tarvitsee muuttaa sovelluksen muuta logiikkaa. Tämä noudattaa SOLID-periaatteiden D-osa (Dependency Inversion Principle), joka suosittelee abstraktioiden käyttöä konkreettisten toteutusten sijaan.

typescript
export interface IUserService {
getUser(id: string): Observable<IUser>;
updateUser(id: string, user: IUser): Observable<IUser>; }

Käyttäjätietojen hakeminen

Käyttäjän tietojen hakeminen suoritetaan getUser-metodilla. Tässä käytämme HTTP-pyyntöä, joka vie pyynnön palvelimelle ja palauttaa käyttäjän tiedot. Jos käyttäjän tunnus (id) ei ole määritetty, metodissa heitetään virhe.

typescript
getUser(id: string | null): Observable<IUser> { if (id === null) { return throwError('User id is not set'); } return this.httpClient.get<IUser>(`${environment.baseUrl}/v2/user/${id}`); }

Tämä funktio palauttaa käyttäjän tiedot JSON-muodossa, ja se voidaan myöhemmin muuntaa User-luokan olioon, jota voidaan käyttää sovelluksessa.

Käyttäjätietojen päivittäminen

Käyttäjätietojen päivittämiseen käytämme updateUser-metodia. Tämä metodi ottaa vastaan käyttäjän tiedot ja päivittää ne palvelimelle PUT-pyynnön avulla. Jos pyyntö epäonnistuu, tallennamme käyttäjän tiedot välimuistiin ja voimme palauttaa ne myöhemmin, jos tarvitaan. Välimuistin käyttö parantaa käyttäjäkokemusta ja estää tietojen menetyksen, jos palvelin vastaa virheellä.

typescript
updateUser(id: string, user: IUser): Observable<IUser> { if (id === '') { return throwError('User id is not set'); } // Tallenna käyttäjätiedot välimuistiin mahdollisia virheitä varten this.cache.setItem('draft-user', Object.assign(user, { _id: id })); const updateResponse$ = this.httpClient
.put<IUser>(`${environment.baseUrl}/v2/user/${id}`, user)
.
pipe(map(User.Build), catchError(transformError)); updateResponse$.subscribe( (res) => {
this.authService.currentUser$.next(res);
this.cache.removeItem('draft-user'); }, (err) => throwError(err) ); return updateResponse$; }

Tässä esimerkissä välimuistiin tallennetaan käyttäjän syöttämät tiedot, jotta ne eivät katoa palvelimen virheen vuoksi. Kun päivitys onnistuu, välimuisti poistetaan ja käyttäjän tiedot päivitetään.

Monivaiheiset ja responsiiviset lomakkeet

Lomakkeet, erityisesti monivaiheiset lomakkeet, ovat yksi tärkeimmistä osista käyttäjän vuorovaikutusta sovelluksessa. Monivaiheiset lomakkeet antavat käyttäjille mahdollisuuden täyttää tietoja vaiheittain, mikä parantaa käyttökokemusta ja vähentää virheitä.

On tärkeää, ettei lomakkeita yli-insinööröidä, eli on parempi luoda yksi suuri komponentti kuin hajottaa lomake useisiin osiin, mikä saattaa tehdä koodista liian monimutkaista. Toisin sanoen, lomakkeet tulisi jakaa useampiin komponentteihin vain silloin, kun se todella parantaa ylläpidettävyyttä ja laajennettavuutta.

Esimerkiksi monivaiheisessa lomakkeessa, jossa käyttäjä syöttää profiilitietojaan, voit käyttää Angularin FormBuilder-palvelua lomakkeen hallintaan. Tässä on esimerkki lomakkeen määrittelystä:

typescript
@Component({ selector: 'app-profile', templateUrl: './profile.component.html', styleUrls: ['./profile.component.css'], })
export class ProfileComponent implements OnInit {
formGroup: FormGroup; currentUserId: string; constructor( private formBuilder: FormBuilder, private userService: UserService, private authService: AuthService ) {} ngOnInit() { this.buildForm(); this.authService.currentUser$.pipe( filter((user) => user !== null), tap((user) => { this.currentUserId = user._id; this.buildForm(user); }) ); } private buildForm(user?: IUser) {
this.formGroup = this.formBuilder.group({
firstName: [user?.firstName || '', Validators.required],
lastName: [user?.lastName || '', Validators.required],
email: [user?.email || '', [Validators.required, Validators.email]], }); } }

Tässä esimerkissä buildForm-metodi luo lomakkeen ja täyttää sen käyttäjän tiedoilla, jos ne ovat saatavilla.

Monivaiheiset lomakkeet voivat olla myös responsiivisia, ja voit käyttää CSS:ää, kuten media queries, varmistaaksesi, että lomake toimii hyvin eri laitteilla. Tämä parantaa käyttäjäkokemusta erityisesti mobiililaitteilla.

Tärkeät huomiot ja parannukset

Lomakkeiden, kuten myös käyttäjäpalvelun toteuttamisen, tulee olla skaalautuvaa ja helposti ylläpidettävää. Tämän vuoksi on tärkeää käyttää oikeanlaisia abstraktioita ja rajapintoja, jotta koodi on joustavaa ja mukautettavissa tulevaisuudessa. Välimuistin käyttö on erityisen hyödyllistä, sillä se voi estää käyttäjän tietojen menetyksen, mikäli palvelin vastaa virheellä. Lisäksi, vaikka lomakkeet saattavat aluksi vaikuttaa yksinkertaisilta, monivaiheisten ja responsiivisten lomakkeiden toteuttaminen vaatii huolellista suunnittelua ja testauksia.

Kuinka optimoida Angularin suorituskykyä ja parantaa lomakkeiden hallintaa

Angularin tehokkuus ja suorituskyky voivat olla haasteita, erityisesti suurilla sovelluksilla ja monimutkaisilla lomakkeilla, joissa on useita reaktiivisia kenttiä ja laskettuja ominaisuuksia. Tässä käsitellään, miten voidaan optimoida laskettujen ominaisuuksien käyttö ja parantaa lomakkeiden hallintaa Angular-sovelluksissa, erityisesti käyttäen signaaleja ja mukautettuja putkia.

Kun käytämme laskettua ominaisuutta, kuten ikää käyttäjän syntymäpäivän perusteella, voimme kohdata suorituskykyongelmia. Esimerkiksi, jos käytämme getFullYear()-funktiota suoraan age-ominaisuuden laskemiseen, Angularin muutostarkistusalgoritmi kutsuu tätä ominaisuutta jopa 60 kertaa sekunnissa, mikä voi kuormittaa sovellusta erityisesti, jos se on osa dynaamisesti päivitettävää käyttöliittymää. Tämä voi johtaa merkittäviin suorituskykyongelmiin.

Tällöin voimme optimoida toteutuksen käyttämällä "puhtaita" mukautettuja putkia (pure pipes). Näin Angular ymmärtää, että age-ominaisuus tulee tarkistaa vain silloin, kun sen riippuvat arvot muuttuvat. Tämä takaa sen, että laskettu arvo ei päivity tarpeettomasti, ja suorituskyky pysyy hyvänä. Esimerkiksi:

typescript
now = new Date(); dateOfBirth = signal(this.formGroup.get('dateOfBirth')?.value || this.now); age = computed(() => this.now.getFullYear() - this.dateOfBirth().getFullYear());

Tässä käytämme signal-objektia dateOfBirth:n ja age:n hallintaan. Tällöin age päivittyy vain, jos dateOfBirth muuttuu. Tämä toteutus on yksinkertainen ja Angularin muutostarkistusalgoritmi hoitaa päivitykset automaattisesti. Tässä vaiheessa on kuitenkin tärkeää huomata, että signaalipohjaiset komponentit eivät ole vielä täysin tuettuja reaktiivisissa lomakkeissa, kuten FormGroup-kohdassa. Tämä haaste auttaa ymmärtämään, kuinka merkittävä muutos signaalien käyttöönotto on Angularissa.

Toinen tärkeä näkökulma liittyy ikä-arvon validointiin. Voimme määrittää syntymäpäivän validoinnin rajoittamalla sen vain viimeisen sadan vuoden sisällä. Tämä onnistuu yksinkertaisella minDate-määritteellä:

typescript
minDate = new Date(this.now.getFullYear() - 100, this.now.getMonth(), this.now.getDate());

Tämä koodi rajoittaa syntymäpäivän syöttämisen siten, että se ei voi olla enempää kuin 100 vuotta vanha. Tämä voidaan ottaa käyttöön HTML-mallissa esimerkiksi seuraavasti:

html
Date of Birth @if (formGroup.get('dateOfBirth')?.value) { {{ age }} year(s) old }

Tällöin lomakkeelle tulee näkyviin käyttäjän ikä sen perusteella, mitä syntymäpäivää käyttäjä on valinnut.

Tyypillinen haaste, joka liittyy käyttäjän osoitetietojen syöttämiseen, on myös tilakentän käsittely. Tämän ratkaisemiseksi voidaan käyttää "typeahead"-tyyppistä suodattavaa pudotusvalikkoa, joka auttaa käyttäjää valitsemaan osavaltion. Tämä toteutetaan seuraavalla koodilla:

typescript
const state = this.formGroup.get('address.state'); if (state != null) {
this.states$ = state.valueChanges.pipe(
startWith(''), map((value) => USStateFilter(value)) ); }

Tässä määritämme state-kenttään kuuntelijan, joka suodattaa osavaltiot, kun käyttäjä kirjoittaa. HTML-mallissa tämä voidaan toteuttaa mat-autocomplete-komponentilla, joka näyttää suodatetut osavaltiot reaaliaikaisesti:

html
State @for (state of states$ | async; track state) { {{ state.name }} }

Tämä luo dynaamisen pudotusvalikon, jossa käyttäjä voi nopeasti valita oikean osavaltion.

Monimutkaisempia lomakkeita varten, kuten useiden puhelinnumeroiden syöttäminen, voidaan käyttää dynaamisia FormArray-komponentteja. Näin saadaan mahdollisuus lisätä useita puhelinnumeroita ja hallita niitä tehokkaasti. Tämä toteutetaan seuraavasti:

typescript
phones: this.formBuilder.array(this.buildPhoneArray(user?.phones || []))

Tässä käytämme FormArray-komponenttia puhelinnumeroiden hallintaan. buildPhoneArray-metodi luo tarvittavat FormGroup-objektit ja lisää ne FormArray-kenttään. Näin käyttäjä voi lisätä uusia puhelinnumeroita lomakkeeseen.

typescript
private buildPhoneArray(phones: IPhone[]) { const groups = []; if (phones?.length === 0) {
groups.push(this.buildPhoneFormControl(1));
}
else { phones.forEach((p) => { groups.push(this.buildPhoneFormControl(p.id, p.type, p.digits)); }); } return groups; }

Tämä metodi auttaa määrittämään, kuinka monta puhelinnumeroa lomakkeessa tulisi olla. Lisäksi käyttäjälle voidaan tarjota mahdollisuus lisätä uusi puhelinnumero "Add Phone" -painikkeella, joka lisää uuden tyhjän kentän lomakkeeseen.

typescript
addPhone() {
this.phonesArray.push(this.buildPhoneFormControl(this.formGroup.get('phones').value.length + 1));
}

phonesArray-ominaisuus helpottaa dynaamisten kenttien käsittelyä ja varmistaa, että lomakkeen tilaa voidaan hallita tehokkaasti.

Tämä kaikki osoittaa, kuinka Angularin dynaamiset lomakkeet ja optimointi voivat parantaa sovelluksen suorituskykyä ja käyttäjäkokemusta, erityisesti silloin, kun käsitellään suuria tietomääriä tai monimutkaisia lomakkeita. Angularin tarjoamat uudet työkalut, kuten signaalit ja mukautetut putket, auttavat luomaan responsiivisia ja tehokkaita sovelluksia, joissa suorituskyky ei kärsi vaikka kenttäarvot päivittyisivät usein.