Riigi juhtimine on veebirakenduse väljatöötamisel väga oluline arhitektuur.
Selles õpetuses käsitleme lihtsat lähenemist oleku haldamiseks Nurga pealekandmine mis kasutab Firebase selle tagana.
Vaatame läbi mõned mõisted, nagu riik, kauplused ja teenused. Loodetavasti aitab see teil neist terminitest paremini aru saada ja paremini mõista ka muid riigihalduse raamatukogusid, näiteks NgRx ja NgXs.
Ehitame töötajate administraatori lehe, et kajastada erinevaid olekuhalduse stsenaariume ja neid käsitlevaid lähenemisviise.
Tüüpilisel Nurgeline rakendus on meil komponendid ja teenused. Tavaliselt toimivad vaate mallina komponendid. Teenused sisaldavad äriloogikat ja / või suhtlevad väliste API-de või muude teenustega toimingute lõpuleviimiseks või andmete hankimiseks.
Komponendid kuvavad tavaliselt andmeid ja võimaldavad kasutajatel toimingute sooritamiseks rakendusega suhelda. Seda tehes võivad andmed muutuda ja rakendus kajastab neid muudatusi, värskendades vaadet.
Angulari muutuste tuvastamise mootor hoolitseb kontrollimise eest, kui vaatega seotud komponendi väärtus on muutunud, ja värskendab vaadet vastavalt.
Rakenduse kasvades hakkab meil olema üha rohkem komponente ja teenuseid. Sageli võib olla keeruline mõista, kuidas andmed muutuvad, ja jälgida, kus see juhtub.
Kui me kasutame Firebase meie tagatipuks pakutakse meile tõeliselt korralikku API-d, mis sisaldab enamikku reaalajas rakenduse loomiseks vajalikest toimingutest ja funktsioonidest.
@angular/fire
on ametlik Angular Firebase'i teek. See on kiht Firebase JavaScripti SDK teegi peal, mis lihtsustab Firebase SDK kasutamist nurgarakenduses. See sobib suurepäraselt nurgeliste heade tavadega, näiteks vaatlusobjektide kasutamine Firebase'ist andmete hankimiseks ja kuvamiseks meie komponentidesse.
Võime mõelda olekust kui rakenduses mis tahes ajahetkel kuvatavatest väärtustest. Pood on lihtsalt selle rakenduse riigi omanik.
Olekut saab modelleerida ühe tavalise objektina või nende reana, peegeldades rakenduse väärtusi.
Ehitame selle üles: kõigepealt loome Angular CLI abil rakenduse põhitellingud ja ühendame need Firebase'i projektiga.
$ npm install -g @angular/cli $ ng new employees-admin` Would you like to add Angular routing? Yes Which stylesheet format would you like to use? SCSS $ cd employees-admin/ $ npm install bootstrap # We'll add Bootstrap for the UI
Ja edasi styles.scss
// ... @import '~bootstrap/scss/bootstrap';
Järgmisena installime @angular/fire
:
npm install firebase @angular/fire
Nüüd loome Firebase'i projekti aadressil Firebase'i konsooli .
Seejärel oleme valmis looma Firestore'i andmebaasi.
Selle õpetuse jaoks alustan testrežiimis. Kui plaanite tootmisse lubada, peaksite rakendama eeskirju, et keelata sobimatu juurdepääs.
Valige Projekti ülevaade → Projekti seaded ja kopeerige Firebase'i veebikonfiguratsioon kohalikku environments/environment.ts
export const environment = { production: false, firebase: { apiKey: '', authDomain: '', databaseURL: '', projectId: '', storageBucket: '', messagingSenderId: '' } };
Sel hetkel on meil oma rakenduse jaoks põhitellingud paigas. Kui me ng serve
, saame:
teisendada SQL-server Oracle'iks
Loome kaks üldist abstraktset klassi, mille seejärel tippime ja laiendame oma teenuste ehitamiseks.
Üldised saate kirjutada käitumist ilma seotud tüübita. See lisab taaskasutatavust ja paindlikkust oma koodile.
TypeScripti üldiste võimaluste kasutamiseks loome @angular/fire
baaside üldise ümbrise firestore
teenus.
Loome app/core/services/firestore.service.ts
.
Siin on kood:
import { Inject } from '@angular/core'; import { AngularFirestore, QueryFn } from '@angular/fire/firestore'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { environment } from 'src/environments/environment'; export abstract class FirestoreService { protected abstract basePath: string; constructor( @Inject(AngularFirestore) protected firestore: AngularFirestore, ) { } doc$(id: string): Observable { return this.firestore.doc(`${this.basePath}/${id}`).valueChanges().pipe( tap(r => { if (!environment.production) { console.groupCollapsed(`Firestore Streaming [${this.basePath}] [doc$] ${id}`) console.log(r) console.groupEnd() } }), ); } collection$(queryFn?: QueryFn): Observable { return this.firestore.collection(`${this.basePath}`, queryFn).valueChanges().pipe( tap(r => { if (!environment.production) { console.groupCollapsed(`Firestore Streaming [${this.basePath}] [collection$]`) console.table(r) console.groupEnd() } }), ); } create(value: T) { const id = this.firestore.createId(); return this.collection.doc(id).set(Object.assign({}, { id }, value)).then(_ => { if (!environment.production) { console.groupCollapsed(`Firestore Service [${this.basePath}] [create]`) console.log('[Id]', id, value) console.groupEnd() } }) } delete(id: string) { return this.collection.doc(id).delete().then(_ => { if (!environment.production) { console.groupCollapsed(`Firestore Service [${this.basePath}] [delete]`) console.log('[Id]', id) console.groupEnd() } }) } private get collection() { return this.firestore.collection(`${this.basePath}`); } }
See abstract class
töötab meie Firestore'i teenuste üldise ümbrisena.
See peaks olema ainus koht, kuhu peaksime süstima AngularFirestore
See minimeerib mõju, kui @angular/fire
raamatukogu värskendatakse. Samuti, kui tahame mingil hetkel teeki muuta, peame ainult seda klassi uuendama.
Lisasin doc$
, collection$
, create
ja delete
. Nad mähkivad @angular/fire
meetodid ja pakuvad logimist, kui Firebase voogesitab andmeid - see on silumiseks väga mugav - ja pärast objekti loomist või kustutamist.
Meie üldine kauplusteenus ehitatakse RxJS-i abil | BehaviorSubject
BehaviorSubject
võimaldab tellijatel saada viimane emiteeritud väärtus kohe, kui nad tellivad. Meie puhul on see kasulik, kuna saame poe tellimisel kõigi pomponentide algväärtusega poodi alustada.
Poel on kaks meetodit: patch
ja set
. (Loome get
meetodid hiljem.)
Loome app/core/services/store.service.ts
:
import { BehaviorSubject, Observable } from 'rxjs'; import { environment } from 'src/environments/environment'; export abstract class StoreService { protected bs: BehaviorSubject; state$: Observable; state: T; previous: T; protected abstract store: string; constructor(initialValue: Partial) { this.bs = new BehaviorSubject(initialValue as T); this.state$ = this.bs.asObservable(); this.state = initialValue as T; this.state$.subscribe(s => { this.state = s }) } patch(newValue: Partial, event: string = 'Not specified') { this.previous = this.state const newState = Object.assign({}, this.state, newValue); if (!environment.production) { console.groupCollapsed(`[${this.store} store] [patch] [event: ${event}]`) console.log('change', newValue) console.log('prev', this.previous) console.log('next', newState) console.groupEnd() } this.bs.next(newState) } set(newValue: Partial, event: string = 'Not specified') { this.previous = this.state const newState = Object.assign({}, newValue) as T; if (!environment.production) { console.groupCollapsed(`[${this.store} store] [set] [event: ${event}]`) console.log('change', newValue) console.log('prev', this.previous) console.log('next', newState) console.groupEnd() } this.bs.next(newState) } }
Üldklassina lükkame sisestamise edasi, kuni seda korralikult pikendatakse.
Konstruktor saab tüübi algväärtuse Partial
. See võimaldab meil väärtusi rakendada ainult oleku mõnele omadusele. Konstruktor tellib ka sisemise BehaviorSubject
heitkoguseid ja hoiab sisemist olekut pärast iga muudatust
patch()
saab newValue
tüüpi Partial
ja ühendab selle praeguse this.state
-ga poe väärtus. Lõpuks me next()
newState
ja edastavad uue oleku kõigile poe tellijatele.
set()
töötab väga sarnaselt, ainult et riikliku väärtuse lappimise asemel määrab selle väärtuseks newValue
see sai.
Logime oleku eelmised ja järgmised väärtused muudatuste ilmnemisel, mis aitab meil siluda ja olekumuutusi hõlpsasti jälgida.
Olgu, vaatame seda kõike toimimas. Mida me teeme, on töötajate lehe loomine, mis sisaldab töötajate loendit ja uute töötajate lisamise vormi.
Uuendame app.component.html
lihtsa navigeerimisriba lisamiseks:
Järgmisena loome põhimooduli:
kuidas eris bot kasutada
ng g m Core
Jaotises core/core.module.ts
lisame rakenduse jaoks vajalikud moodulid:
// ... import { AngularFireModule } from '@angular/fire' import { AngularFirestoreModule } from '@angular/fire/firestore' import { environment } from 'src/environments/environment'; import { ReactiveFormsModule } from '@angular/forms' @NgModule({ // ... imports: [ // ... AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ReactiveFormsModule, ], exports: [ CommonModule, AngularFireModule, AngularFirestoreModule, ReactiveFormsModule ] }) export class CoreModule { }
Nüüd loome töötajate lehe, alustades töötajate moodulist:
ng g m Employees --routing
Jaotises employees-routing.module.ts
lisame employees
tee:
// ... import { EmployeesPageComponent } from './components/employees-page/employees-page.component'; // ... const routes: Routes = [ { path: 'employees', component: EmployeesPageComponent } ]; // ...
Ja sisse employees.module.ts
impordime ReactiveFormsModule
:
// ... import { ReactiveFormsModule } from '@angular/forms'; // ... @NgModule({ // ... imports: [ // ... ReactiveFormsModule ] }) export class EmployeesModule { }
Lisame nüüd need kaks moodulit kausta app.module.ts
fail:
// ... import { EmployeesModule } from './employees/employees.module'; import { CoreModule } from './core/core.module'; imports: [ // ... CoreModule, EmployeesModule ],
Lõpuks loome oma töötajate lehe tegelikud komponendid koos vastava mudeli, teenuse, poe ja olekuga.
ng g c employees/components/EmployeesPage ng g c employees/components/EmployeesList ng g c employees/components/EmployeesForm
Meie mudeli jaoks vajame faili nimega models/employee.ts
:
export interface Employee { id: string; name: string; location: string; hasDriverLicense: boolean; }
Meie teenus asub failis nimega employees/services/employee.firestore.ts
. See teenus laiendab üldist FirestoreService
varem loodud ja määrame lihtsalt basePath
Firestore'i kollektsiooni:
import { Injectable } from '@angular/core'; import { FirestoreService } from 'src/app/core/services/firestore.service'; import { Employee } from '../models/employee'; @Injectable({ providedIn: 'root' }) export class EmployeeFirestore extends FirestoreService { protected basePath: string = 'employees'; }
Seejärel loome faili employees/states/employees-page.ts
. See toimib töötajate lehe olekuna:
import { Employee } from '../models/employee'; export interface EmployeesPage { loading: boolean; employees: Employee[]; formStatus: string; }
Riigil on loading
väärtus, mis määrab, kas lehel kuvatakse laadimissõnum, employees
ise ja a formStatus
muutuja vormi oleku käsitsemiseks (nt Saving
või Saved
.)
Vajame faili aadressil employees/services/employees-page.store.ts
. Siin laiendame StoreService
varem loodud. Määrame poe nime, mida kasutatakse silumisel selle tuvastamiseks.
See teenus lähtestab ja hoiab lehel töötajate olekut. Pange tähele, et konstruktor kutsub super()
lehe algse olekuga. Sel juhul lähtestame oleku loading=true
abil ja tühi töötajate rida.
import { EmployeesPage } from '../states/employees-page'; import { StoreService } from 'src/app/core/services/store.service'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class EmployeesPageStore extends StoreService { protected store: string = 'employees-page'; constructor() { super({ loading: true, employees: [], }) } }
Nüüd loome EmployeesService
integreerima EmployeeFirestore
ja EmployeesPageStore
:
ng g s employees/services/Employees
Pange tähele, et süstime EmployeeFirestore
ja EmployeesPageStore
selles teenuses. See tähendab, et EmployeesService
sisaldab ja koordineerib kõnesid Firestore'i ja poodi oleku värskendamiseks. See aitab meil luua komponentide jaoks ühe API, millele helistada.
import { EmployeesPageStore } from './employees-page.store'; import { EmployeeFirestore } from './employee.firestore'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Employee } from '../models/employee'; import { tap, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class EmployeesService { constructor( private firestore: EmployeeFirestore, private store: EmployeesPageStore ) { this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, }, `employees collection subscription`) }) ).subscribe() } get employees$(): Observable { return this.store.state$.pipe(map(state => state.loading ? [] : state.employees)) } get loading$(): Observable { return this.store.state$.pipe(map(state => state.loading)) } get noResults$(): Observable { return this.store.state$.pipe( map(state => { return !state.loading && state.employees && state.employees.length === 0 }) ) } get formStatus$(): Observable { return this.store.state$.pipe(map(state => state.formStatus)) } create(employee: Employee) { this.store.patch({ loading: true, employees: [], formStatus: 'Saving...' }, 'employee create') return this.firestore.create(employee).then(_ => { this.store.patch({ formStatus: 'Saved!' }, 'employee create SUCCESS') setTimeout(() => this.store.patch({ formStatus: '' }, 'employee create timeout reset formStatus'), 2000) }).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, 'employee create ERROR') }) } delete(id: string): any { this.store.patch({ loading: true, employees: [] }, 'employee delete') return this.firestore.delete(id).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, 'employee delete ERROR') }) } }
Vaatame, kuidas teenus töötab.
Konstruktoris tellime Firestore'i töötajate kollektsiooni. Niipea kui Firestore väljastab kogu andmeid, värskendame poodi, seades loading=false
ja employees
Firestore'i tagastatud kollektsiooniga. Kuna oleme süstinud EmployeeFirestore
, sisestatakse Firestore'ist tagastatud objektid kataloogile Employee
, mis võimaldab rohkem IntelliSense'i funktsioone.
See tellimus on aktiivne, kuni rakendus on aktiivne, kuulab kõiki muudatusi ja värskendab poodi iga kord, kui Firestore voogesitab andmeid.
this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, }, `employees collection subscription`) }) ).subscribe()
employees$()
ja loading$()
funktsioonid valivad olekutüki, mida soovime komponendis hiljem kasutada. employees$()
tagastab oleku laadimisel tühja massiivi. See võimaldab meil kuvada korraliku sõnumside.
get employees$(): Observable { return this.store.state$.pipe(map(state => state.loading ? [] : state.employees)) } get loading$(): Observable { return this.store.state$.pipe(map(state => state.loading)) }
Okei, nii et nüüd on meil kõik teenused valmis ja saame oma vaate komponendid üles ehitada. Kuid enne, kui me seda teeme, võib kasuks tulla kiire värskendus ...
async
ToruVaatlusfunktsioonid võimaldavad tellijatel saada andmeid heitkogustena voogena. See koos async
toru, võib väga võimas.
async
pipe hoolitseb vaadeldava tellimise ja vaate värskendamise eest, kui uued andmed väljastatakse. Veelgi olulisem on see, et komponendi hävitamisel loobutakse automaatselt tellimusest, kaitstes meid mälulekete eest.
Vaatlusobjektide ja üldiselt RxJ-teegi kohta saate lugeda rohkem ametlikud dokumendid .
Sisestame employees/components/employees-page/employees-page.component.html
-i selle koodi:
Employees
Samamoodi employees/components/employees-list/employees-list.component.html
on see, kasutades async
ülalnimetatud torutehnika:
Loading... No results {{employee.location}} {{employee.name}}
{{employee.hasDriverLicense ? 'Can drive': ''}}
Delete
Kuid sel juhul vajame ka komponendi jaoks mõnda TypeScripti koodi. Fail employees/components/employees-list/employees-list.component.ts
vajavad seda:
import { Employee } from '../../models/employee'; import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { EmployeesService } from '../../services/employees.service'; @Component({ selector: 'app-employees-list', templateUrl: './employees-list.component.html', styleUrls: ['./employees-list.component.scss'] }) export class EmployeesListComponent implements OnInit { loading$: Observable; employees$: Observable; noResults$: Observable; constructor( private employees: EmployeesService ) {} ngOnInit() { this.loading$ = this.employees.loading$; this.noResults$ = this.employees.noResults$; this.employees$ = this.employees.employees$; } delete(employee: Employee) { this.employees.delete(employee.id); } }
Brauserisse minnes on meil nüüd järgmine:
Ja konsoolil on järgmine väljund:
Seda vaadates võime öelda, et Firestore voogesitas employees
tühjade väärtustega kogu ja employees-page
pood oli lappitud, seades loading
alates true
kuni false
.
OK, koostame vormi uute töötajate lisamiseks Firestore'i:
Aastal employees/components/employees-form/employees-form.component.html
lisame selle koodi:
Name Please enter a Name. Choose location {{loc}} Please select a Location. Has driver license Add { status$ }
Vastav TypeScripti kood asub employees/components/employees-form/employees-form.component.ts
import { EmployeesService } from './../../services/employees.service'; import { AngularFirestore } from '@angular/fire/firestore'; import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Observable } from 'rxjs'; @Component({ selector: 'app-employees-form', templateUrl: './employees-form.component.html', styleUrls: ['./employees-form.component.scss'] }) export class EmployeesFormComponent implements OnInit { form: FormGroup = new FormGroup({ name: new FormControl('', Validators.required), location: new FormControl('', Validators.required), hasDriverLicense: new FormControl(false) }); locations = [ 'Rosario', 'Buenos Aires', 'Bariloche' ] status$: Observable ; constructor( private employees: EmployeesService ) {} ngOnInit() { this.status$ = this.employees.formStatus$; } isInvalid(name) async submit() { this.form.disable() await this.employees.create({ ...this.form.value }) this.form.reset() this.form.enable() } }
Vorm helistab create()
meetod EmployeesService
. Praegu näeb leht välja selline:
Vaatame, mis juhtub, kui lisame uue töötaja.
Pärast uue töötaja lisamist näeme konsooli väljundit järgmist:
Need on kõik sündmused, mis käivitatakse uue töötaja lisamisel. Vaatame lähemalt.
Kui helistame create()
täidame järgmise koodi, seades loading=true
, formStatus='Saving...'
ja employees
massiiv tühjaks ((1)
ülaltoodud pildil).
this.store.patch({ loading: true, employees: [], formStatus: 'Saving...' }, 'employee create') return this.firestore.create(employee).then(_ => { this.store.patch({ formStatus: 'Saved!' }, 'employee create SUCCESS') setTimeout(() => this.store.patch({ formStatus: '' }, 'employee create timeout reset formStatus'), 2000) }).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, 'employee create ERROR') })
Järgmisena kutsume töötaja loomiseks Firestore'i baasteenuse, mis logib (4)
. Lubaduse tagasihelistamisel määrasime formStatus='Saved!'
ja logi (5)
. Lõpuks määrasime formStatus
seadmise ajalõpu tagasi tühjana, logides (6)
.
Logi sündmused (2)
ja (3)
on sündmused, mille käivitab töötajate kollektsiooni Firestore'i tellimus. Kui EmployeesService
on instantsitud, tellime kogu ja saame kollektsiooni kätte iga toimuva muutuse korral.
See seab poodi uue oleku loading=false
abil seadistades employees
massiiv Firestore'ist tulevatele töötajatele.
Kui laiendame logirühmi, näeme poe kõigi sündmuste ja värskenduste üksikasjalikke andmeid koos eelmise ja järgmise väärtusega, mis on silumiseks kasulik.
Nii näeb leht välja pärast uue töötaja lisamist:
Oletame, et tahame nüüd oma lehel kuvada mõned kokkuvõtlikud andmed. Oletame, et soovime töötajate koguarvu, kui palju on autojuhte ja kui palju on Rosarios.
mida saate teha Adobe'iga xd
Alustame uute olekutribuutide lisamisega lehe olekumudelisse employees/states/employees-page.ts
:
// ... export interface EmployeesPage { loading: boolean; employees: Employee[]; formStatus: string; totalEmployees: number; totalDrivers: number; totalRosarioEmployees: number; }
Ja lähtestame need employees/services/emplyees-page.store.ts
poes:
// ... constructor() { super({ loading: true, employees: [], totalDrivers: 0, totalEmployees: 0, totalRosarioEmployees: 0 }) } // ...
Järgmisena arvutame uute omaduste väärtused ja lisame nende vastavad valijad EmployeesService
// ... this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, totalEmployees: employees.length, totalDrivers: employees.filter(employee => employee.hasDriverLicense).length, totalRosarioEmployees: employees.filter(employee => employee.location === 'Rosario').length, }, `employees collection subscription`) }) ).subscribe() // ... get totalEmployees$(): Observable { return this.store.state$.pipe(map(state => state.totalEmployees)) } get totalDrivers$(): Observable { return this.store.state$.pipe(map(state => state.totalDrivers)) } get totalRosarioEmployees$(): Observable { return this.store.state$.pipe(map(state => state.totalRosarioEmployees)) } // ...
Nüüd loome kokkuvõtva komponendi:
ng g c employees/components/EmployeesSummary
Lisame selle employees/components/employees-summary/employees-summary.html
:
Total: { async}
Drivers: { async}
Rosario: { async}
Ja employees/components/employees-summary/employees-summary.ts
import { Component, OnInit } from '@angular/core'; import { EmployeesService } from '../../services/employees.service'; import { Observable } from 'rxjs'; @Component({ selector: 'app-employees-summary', templateUrl: './employees-summary.component.html', styleUrls: ['./employees-summary.component.scss'] }) export class EmployeesSummaryComponent implements OnInit { total$: Observable ; drivers$: Observable ; rosario$: Observable ; constructor( private employees: EmployeesService ) {} ngOnInit() { this.total$ = this.employees.totalEmployees$; this.drivers$ = this.employees.totalDrivers$; this.rosario$ = this.employees.totalRosarioEmployees$; } }
Seejärel lisame komponendi employees/employees-page/employees-page.component.html
// ... Employees
// ...
Tulemuseks on järgmine:
Konsoolis on meil:
Töötajate teenus arvutab kokku totalEmployees
, totalDrivers
ja totalRosarioEmployees
iga heite kohta ja ajakohastab olekut.
The selle õpetuse täielik kood on saadaval GitHubis ja seal on ka otseülekanne .
Selles õpetuses käsitlesime lihtsat lähenemisviisi nurkrakenduste oleku haldamiseks Firebase'i taguotsa abil.
See lähenemisviis sobib kenasti vaatlusobjektide kasutamise nurgaliste juhistega. See hõlbustab ka silumist, pakkudes rakenduse oleku kõigi värskenduste jälgimist.
Üldist kauplusteenust saab kasutada ka rakenduste oleku haldamiseks, mis ei kasuta Firebase'i funktsioone, kas ainult rakenduse või teistelt API-del pärinevate andmete haldamiseks.
Kuid enne, kui hakkate seda valimatult rakendama, on üks asi, mida kaaluda: EmployeesService
tellib konstruktoril Firestore'i ja jätkab kuulamist, kui rakendus on aktiivne. See võib olla kasulik, kui kasutame töötajate loendit rakenduse mitmel lehel, et vältida andmete saamist Firestore'ist lehtede vahel liikumisel.
Kuid see ei pruugi olla parim võimalus teiste stsenaariumite korral, näiteks kui peate lihtsalt üks kord algväärtused tõmbama ja seejärel käivitama Firebase'i andmete uuesti laadimise käsitsi. Alumine rida on see, et paremate rakendusmeetodite valimiseks on alati oluline mõista oma rakenduse nõudeid.
Nurgeline (algselt AngularJS) on populaarne esiotsa raamistik üheleheliste rakenduste (SPA) loomiseks. See on avatud lähtekoodiga ja seda toetab Google.
Riigi haldamine seisneb muutujate õiges jälgimises veebirakenduses. Nt kui vestlusrakenduse kasutaja vahetas jututubasid, on see oleku muutus. Kui nad siis sõnumi saatsid, kuid saatmisfunktsioon ei olnud varasemast olekumuutustest teadlik, saatis see sõnumi eelmisesse jututuppa, mille tulemuseks oli väga halb UX.
Google'i Firebase on kõik-ühes mobiilirakenduste arendusplatvorm. See on tuntud oma algse reaalajas andmebaaside pakkumise poolest, kuid tänapäeval hõlmab see muu hulgas integreeritud krahhiaruandeid, autentimist ja varade majutamist.