GuideAdresser
Adressefelt med autocomplete i React
Grundfasts adressefelt er et almindeligt script, der hægter sig på et inputfelt. I React vil du styre, hvornår det sker: når komponenten monteres, og ikke længere når den forsvinder. Her er en hook, der gør netop det, og en komponent, der bruger den.
Kaldet i guiden
På denne side6 trin
Opret en offentlig nøgle
Feltet kalder Grundfast direkte fra kundens browser, så det bruger en offentlig nøgle (gf_pub_live_). Den må gerne stå i sidens kildekode, fordi den kun virker fra de domæner, du skriver på den, og kun til adresseopslag. Opret den under Nøgler i dashboardet: vælg Offentlig og skriv dine domæner, fx minapp.dk og localhost:5173. Den lokale port er den, din udviklingsserver kører på.
Byg og test med en offentlig testnøgle (gf_pub_test_), som aldrig faktureres, og skift til live-nøglen, når feltet virker. Reglerne for domæner — www, underdomæner med *. og porte — står i dokumentationen for adressefeltet.
Indlæs scriptet én gang
Læg script-tagget i index.html (eller i root-layoutet, hvis du bruger et framework med server-rendering). Nøglen kan stå på tagget, så alle felter i appen bruger den:
<script
src="https://grundfast.dk/widget/v1/grundfast-adresse.js"
defer
data-key="gf_pub_live_..."
></script>Uden data-felt hægter scriptet sig ikke på noget af sig selv — det er hookens opgave. Står tagget ikke i index.html, indlæser hooken nedenfor filen selv, første gang en formular med adressefelt vises — så skal nøglen gives som hookens tredje argument, fx fra import.meta.env.
Hooken
useGrundfastAdresse tager en ref til inputfeltet og en callback. Den venter på scriptet, kalder GrundfastAdresse.attach og rydder op med destroy(), når komponenten afmonteres. Oprydningen er det, der gør hooken sikker i Strict Mode, hvor React monterer to gange under udvikling.
import { useEffect, useRef, type RefObject } from 'react';
export interface GrundfastAdresse {
betegnelse: string;
adresselinje: string;
vejnavn: string | null;
husnr: string | null;
etage: string | null;
doer: string | null;
postnr: string | null;
postnrnavn: string | null;
kommunekode: string | null;
adgangsadresse_id: string;
adresse_id: string | null;
bfe_nummer: number | null;
koordinat: { lon: number; lat: number } | null;
}
interface Widget {
attach(
input: HTMLInputElement,
opts: { key?: string; onSelect?: (a: GrundfastAdresse) => void },
): { destroy(): void } | undefined;
}
declare global {
interface Window { GrundfastAdresse?: Widget }
}
const SRC = 'https://grundfast.dk/widget/v1/grundfast-adresse.js';
let loading: Promise<Widget> | null = null;
/** Indlæs scriptet én gang, også hvis det ikke står i index.html. */
function loadWidget(): Promise<Widget> {
if (window.GrundfastAdresse) return Promise.resolve(window.GrundfastAdresse);
loading ??= new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = SRC;
s.onload = () => (window.GrundfastAdresse ? resolve(window.GrundfastAdresse) : reject());
s.onerror = () => { loading = null; reject(new Error('adressefeltet kunne ikke indlæses')); };
document.head.appendChild(s);
});
return loading;
}
export function useGrundfastAdresse(
ref: RefObject<HTMLInputElement | null>,
onSelect: (a: GrundfastAdresse) => void,
key?: string,
) {
// Den nyeste callback, uden at feltet skal hægtes af og på ved hver render.
const latest = useRef(onSelect);
latest.current = onSelect;
useEffect(() => {
let field: { destroy(): void } | undefined;
let cancelled = false;
loadWidget()
.then((w) => {
if (cancelled || !ref.current) return;
const onSelect = (a: GrundfastAdresse) => latest.current(a);
// Uden key bruges data-key fra script-tagget.
field = w.attach(ref.current, key ? { key, onSelect } : { onSelect });
})
.catch(() => { /* feltet virker som et almindeligt input */ });
return () => {
cancelled = true;
field?.destroy();
};
}, [ref, key]);
}Komponenten
Formularens tilstand ejer du selv. Kunden kan stadig skrive i feltet som normalt, og når en adresse vælges, sætter onSelect resten af felterne. Etage og dør er allerede med i adresselinje, når bygningen har lejligheder.
import { useRef, useState } from 'react';
import { useGrundfastAdresse } from './use-grundfast-adresse';
export function AdresseFelter() {
const ref = useRef<HTMLInputElement>(null);
const [adresse, setAdresse] = useState('');
const [postnr, setPostnr] = useState('');
const [by, setBy] = useState('');
const [bfe, setBfe] = useState<number | null>(null);
useGrundfastAdresse(ref, (a) => {
setAdresse(a.adresselinje);
setPostnr(a.postnr ?? '');
setBy(a.postnrnavn ?? '');
setBfe(a.bfe_nummer);
});
return (
<fieldset>
<label htmlFor="adresse">Adresse</label>
<input id="adresse" ref={ref} value={adresse}
onChange={(e) => setAdresse(e.target.value)} autoComplete="off" />
<label htmlFor="postnr">Postnummer</label>
<input id="postnr" value={postnr} onChange={(e) => setPostnr(e.target.value)} />
<label htmlFor="by">By</label>
<input id="by" value={by} onChange={(e) => setBy(e.target.value)} />
<input type="hidden" name="bfe" value={bfe ?? ''} />
</fieldset>
);
}Når scriptet skriver i adressefeltet, gør det det på samme måde som et tastetryk, så onChange kører, og React-tilstanden følger med. Callbacken sætter bagefter den endelige værdi. Har du autoComplete="off" på feltet, undgår du, at browserens egen udfyldning lægger sig oven på forslagslisten.
Next.js og andre frameworks med server-rendering
window og document inde i effekten, så den kører aldrig på serveren. Bruger du App Router, skal komponenten have 'use client' øverst.Tjek nøglen fra en terminal
Før du leder i platformens indstillinger, kan du se, om nøglen og domænet passer sammen. Kaldet nedenfor er det samme, feltet laver. Origin er dit domæne, præcis som browseren skriver det:
curl -G "https://api.grundfast.dk/v1/adresse/search" \
--data-urlencode "q=Vestergade 10" \
--data-urlencode "token=$GRUNDFAST_PUBLIC_KEY" \
-H "Origin: http://localhost:5173"Svarer det med adresser, er nøglen i orden. Et 403 med origin … is not allowed betyder, at domænet ikke står på nøglen — ret listen i dashboardet; det slår igennem inden for et halvt minut.
Hvis der ikke kommer forslag
- Åbn browserens konsol (F12, fanen Console). Feltet skriver én linje, der begynder med
[grundfast-adresse], med årsagen fra API’et. no public key— scriptet blev indlæst udendata-key.- Ingen linje og ingen liste — feltet blev ikke fundet. Tjek at selektoren i
data-feltrammer feltet: skrivdocument.querySelectorAll('…')med den samme selektor i konsollen. - Listen vises, men felterne udfyldes ikke — selektorerne i
data-postnrogdata-byrammer ikke noget. De slås op i samme formular som adressefeltet først, derefter på hele siden.
Feltet stopper aldrig formularen: fejler et kald, lukker listen, og kunden kan skrive adressen i hånden som før.
Var denne side nyttig?
Ofte stillede spørgsmål
Nej. Adressefeltet er én fil, som du indlæser fra grundfast.dk, og hooken i guiden er de linjer, du skal bruge for at styre den fra React. Vil du låse en bestemt version, så brug den faste versions-URL, der står i dokumentationen for adressefeltet.
React Strict Mode monterer komponenter, afmonterer dem og monterer dem igen for at finde effekter uden oprydning. Hooken kalder destroy i oprydningen, så der kun er ét felt tilbage. I produktion sker det kun én gang.
Ja. Brug feltets ref fra biblioteket og sæt værdierne med bibliotekets egen setValue i onSelect. Feltet skriver også i inputtet som et tastetryk, så biblioteker, der lytter på input-hændelser, ser værdien.
Ja. Den offentlige nøgle virker også fra din egen fetch-kode mod adressesøgningen og de andre adresse-endpoints. Scriptet sparer dig for tastaturstyring, skærmlæserstøtte og trinnet med etage og dør.
Prøv det på rigtige data
Gratis op til 5.000 kald/md · intet kreditkort · data fra Klimadatastyrelsen/Datafordeler, CC BY 4.0
Relateret