UX & integrare

UX pentru autocomplete de adresă în România

De ce autocomplete-ul generic nu funcționează bine pe adrese românești, cum implementezi debounce corect și ce pattern-uri de accesibilitate sunt obligatorii.

10 iunie 2026·7 min citire

De ce autocomplete-ul generic nu e suficient

Librăriile de autocomplete de adrese proiectate pentru piețe vestice pornesc de la niște premise care nu se potrivesc cu România:

- Presupun că adresele au formatul "stradă, număr, oraș, cod poștal" în această ordine - Nu cunosc nomenclatura românească (Str., Bd., Calea, Șos., Aleea, B-dul) - Nu gestionează diacriticele românești — "ș", "ț", "ă", "â", "î" - Nu știu că "Sector 3" este o subdiviziune a Bucureștiului, nu un județ

Rezultatul: sugestii irelevante, frustrare la checkout și livrări la adrese incomplete.

Endpoint-ul de autocomplete

GET /v1/addresses/autocomplete?q={query}&limit={n} acceptă un query parțial și returnează până la n sugestii (implicit 10, maxim 25):

curl "https://api.posty.ro/v1/addresses/autocomplete?q=mihai+vite&limit=5" \
  -H "Authorization: Bearer psk_live_xxx"
{
  "suggestions": [
    {
      "display": "Strada Mihai Viteazu, Cluj-Napoca, Cluj, 400151",
      "components": {
        "street": "Strada Mihai Viteazu",
        "locality": "Cluj-Napoca",
        "county": "Cluj",
        "county_code": "CJ",
        "postal_code": "400151"
      },
      "highlight": "Strada <em>Mihai</em> <em>Vite</em>azu, Cluj-Napoca, Cluj, 400151",
      "score": 0.98,
      "place_id": "psl_Y2x1ai1zdHJlZXQ"
    }
  ],
  "query": { "q": "mihai vite", "normalized": "mihai vite" }
}

Latența țintă este sub 150 ms la percentila 95, potrivită pentru un dropdown live.

Pattern de implementare

1. Debounce, nu throttle

Userul tastează rapid. Un debounce de 200–300 ms reduce numărul de cereri fără a degrada experiența percepută:

import { ref, watch } from 'vue'

const query = ref('') const suggestions = ref([]) let timer: ReturnType<typeof setTimeout>

watch(query, (val) => { clearTimeout(timer) if (val.length < 3) { suggestions.value = []; return } timer = setTimeout(async () => { const res = await fetch( https://api.posty.ro/v1/addresses/autocomplete?q=${encodeURIComponent(val)}, { headers: { Authorization: Bearer ${apiKey} } } ) const data = await res.json() suggestions.value = data.suggestions }, 250) })

2. Începe de la 3 caractere

Sub 3 caractere, sugestiile sunt prea largi pentru a fi utile și consumă quota inutil. Poți afișa un hint "continuă să tastezi…" pentru UX.

3. Oferă o ieșire manuală

Autocomplete-ul nu va acoperi 100% din adrese — există construcții noi, adrese temporare sau greșeli în sursele de date. Asigură-te că userul poate ignora sugestiile și continua cu ce a scris.

4. Pre-completează câmpurile la selecție

Când userul selectează o sugestie, populează automat câmpurile de județ, localitate și cod poștal din components din răspuns, și afișează display în input. Elimini astfel o sursă majoră de greșeli.

function selectSuggestion(s) {
  form.street = s.components.street
  form.locality = s.components.locality
  form.county = s.components.county
  form.postalCode = s.components.postal_code
}

5. Validează la submit

Autocomplete reduce erorile, dar nu le elimină. La submit, trimite adresa la POST /v1/addresses/verify pentru a prinde modificările manuale pe care userul le-a făcut după selecție.

Diacritice și toleranță la greșeli

API-ul normalizează intern atât query-ul cât și datele. "mihai vitezu" (fără diacritice), "MIHAI VITEAZU" (majuscule) și "Mihai Viteazu" returnează aceleași sugestii. Nu trebuie să preprocesezi input-ul clientului.

Accesibilitate

Un dropdown de autocomplete trebuie să respecte pattern-ul combobox din WAI-ARIA: - role="combobox" pe input, cu aria-expanded și aria-controls corecte - role="listbox" pe container și role="option" pe fiecare sugestie - Navigare cu tastele săgeată, Enter pentru selecție, Escape pentru închidere

Biblioteca NuxtUI USelectMenu și UCommandPalette implementează acest pattern corect — le poți folosi ca bază.