Valgfagsprojekt

I front-end projektet på 3. semester har jeg udviklet en filterkomponent, KortFilter.vue, til et interaktivt kort over klatreparken GoMonkey. Kortet viser parkens baner, og med mit filter kan brugeren vælge, hvilke baner der skal fremhæves på kortet – fx kun de grønne begynderbaner eller kun de svære baner.

Selve kortet med zoom, træk og klik på baner er lavet af Lærke, mens min del er filteret: hentning af banedata fra et API, visning af filtermenuen og kommunikationen videre til kortet, så de valgte baner bliver fremhævet.

En datadreven løsning

Filteret er datadrevet. Alle oplysninger om banerne ligger i en JSON-struktur i Firebase Realtime Database. Hver bane har bl.a. et id, en titel, en undertekst, en svaerhed, et hojdekrav, en farve og et felt underpunkt, som fortæller om banen er en hovedgruppe eller en bane under en gruppe.

Fordelen er, at hvis parken fx ændrer højdekravet på en bane, skal det kun rettes i databasen – så opdateres filteret automatisk, uden at jeg skal ændre koden.

API-kald med fetch()

Data hentes i funktionen hentBaner(), som laver et API-kald til Firebase Realtime Database via dens REST-endpoint (…firebasedatabase.app/.json):

De vigtigste valg i koden:

  • async/await gør, at koden venter på svaret fra databasen, før den går videre, uden at hele siden fryser.
  • response.json() omdanner svaret til JavaScript-objekter, som Vue kan arbejde med.
  • Array eller objekt: Firebase kan returnere data både som et array og som et objekt med ID-nøgler. Derfor tjekker jeg med Array.isArray() og bruger Object.values(), så jeg altid ender med et array. Jeg fjerner også tomme værdier (null), som Firebase kan give, hvis der mangler et indeks.
  • try/catch/finally sørger for, at en fejl bliver fanget og logget, og at loading-tilstanden altid stoppes i finally – uanset om kaldet lykkes eller ej.
  • Loading-tilstand: Variablen indlaeser styrer, at brugeren ser teksten “Henter baner…”, indtil data er klar (v-if="indlaeser" / v-else).
  • onMounted() kalder hentBaner(), så snart komponenten vises på siden.

Brug af Vue.js

Jeg har bygget komponenten med Vue 3 og <script setup> (Composition API).

  • ref() til reaktive værdier: banerListe, valgteFiltre, erAaben, indlaeser og aabneGrupper. Når de ændres, opdaterer Vue automatisk det, brugeren ser.
  • computed() til at sortere data i grupper: grønne, blå og svære baner. Fx finder blaaUnderliggende alle baner, hvor underpunkt er true, og hvor id’et starter med bla. Computed-værdierne genberegnes automatisk, når data er hentet.
  • v-for til at lave et filterpunkt for hver bane i en gruppe, med :key=”bane.id” så Vue kan holde styr på elementerne.
  • v-if til kun at vise fx sværhedsgrad og højdekrav, hvis banen har de oplysninger, og kun at vise “Ryd filtre”, når der faktisk er valgt noget.
  • :class-binding til at give et filterpunkt klassen aktiv, når det er valgt, og til at åbne/lukke dropdown-grupperne.
  • @click og @change til at reagere på brugerens handlinger.
  • defineEmits() til at sende de valgte filtre videre til resten af appen.

Refleksion

Projektet var en udfordring i starten. Da vi kun havde haft få undervisningsgange i Vue.js, brugte jeg meget tid på at sætte mig ind i den her  nye  måde at kode på, samtidig med at jeg skulle bygge filterkomponenten. Derudover kom projektet midt i vores eksamensprojekt, hvilket gjorde det tidspresset at skulle fordele sin tid mellem de to opgaver.

Når det er sagt, har jeg fået en meget bedre forståelse af Vue og af, hvordan man håndterer API-kald med loading- og fejltilstande.

Hvad jeg vil forbedre: Templaten for de tre grupper er næsten ens. Den kunne gøres kortere ved at bruge en v-for over grupperne eller en lille underkomponent, så koden bliver nemmere at vedligeholde. Derudover fungerer “Ryd filtre” med det samme, hvorimod nye valg først slår igennem, når man klikker på “Anvend filtre” – det burde gøres mere ensartet.