Introductie
Wat bouw je precies?
Je maakt een Film Tracker: een app waarin je films kunt bijhouden die je wilt kijken of al gezien hebt. Denk aan een overzicht met filmkaarten, filters op genre en status, en een detailpagina per film.
Dit moet er minimaal in komen:
- Een overzichtspagina met filmkaarten
- Films kunnen toevoegen via een formulier
- Films filteren op genre en status (gezien / wil kijken)
- Films markeren als gezien of nog te kijken
- Zoeken in films
- Films bewaren in localStorage
- Meerdere pagina's met Vue Router (overzicht, login, filmdetail)
- Een Pinia store voor de inlogstatus
Hoe werk je deze opdrachten uit?
- Gebruik één Vue oefenproject voor alle niveaus van deze Film Tracker
- Breid per niveau je bestaande code uit in plaats van opnieuw te beginnen
- Maak aparte componenten, pagina's en folders zodat je structuur netjes blijft
- Begin klein: eerst template, daarna reactivity, daarna interactie
- Gebruik de theorie-links als je vastloopt
- Werk per opdracht in losse
.vue-bestanden, niet alles inApp.vue - Test steeds tussendoor in de browser en in de Vue DevTools
Styling door de hele app
Je werkt gedurende alle niveaus aan de styling van je app. Per niveau staan er kleine styling-doelen die je meeneemt — de styling groeit dus mee met de functionaliteit. In niveau 7 maak je de puntjes op de i.
Kies je eigen aanpak: je mag zelf kiezen of je werkt met scoped CSS
in elk .vue-bestand (<style scoped>) of met
Tailwind CSS (utility classes in de template). Beide zijn prima. Wissel niet halverwege.
Voorstel projectstructuur
film-tracker/
├── src/
│ ├── components/
│ │ ├── AppHeader.vue
│ │ ├── AppSidebar.vue
│ │ ├── FilmCard.vue
│ │ ├── FilmList.vue
│ │ ├── FilmForm.vue
│ │ ├── FilterBar.vue
│ │ ├── SearchBar.vue
│ │ ├── InfoPanel.vue
│ │ └── BasePanel.vue
│ ├── pages/
│ │ ├── HomeView.vue
│ │ ├── LoginView.vue
│ │ ├── FilmDetailView.vue
│ │ └── WatchlistView.vue
│ ├── stores/
│ │ └── auth.js
│ ├── composables/
│ │ └── useFilms.js
│ ├── data/
│ │ └── films.js
│ ├── router/
│ │ └── index.js
│ ├── App.vue
│ └── main.js
Niveau 1: Basisopzet van de Film Tracker
1.1 Maak je project aan
Maak een nieuw Vue-project aan met Vite. Verwijder daarna alle demo-inhoud die Vite automatisch aanmaakt, zodat je met een schone lei begint.
Wat moet je zien: een lege pagina met alleen de tekst Film Tracker.
- Nieuw project:
npm create vite@latest film-tracker -- --template vue - Aanpassen:
src/App.vue(alle demo-inhoud weghalen, alleen Film Tracker tonen) - Aanpassen:
src/style.css(Vite-stijlen leeghalen) - Verwijderen:
src/components/HelloWorld.vue
Klaar als npm run dev werkt en de
browser toont alleen
de titel, zonder Vite-voorbeeldcontent.
Theorie Project Setup
1.2 Maak je eerste Single File Component: FilmCard
Maak een apart .vue-bestand voor een FilmCard component. Dit component
toont de informatie van één film: de titel, het genre, het jaar en de status (gezien of wil kijken).
Wat moet je zien: een kaart op de pagina met bijvoorbeeld:
- Titel: Interstellar
- Genre: Sci-Fi
- Jaar: 2014
- Status: Wil kijken
Tip gebruik <script setup> —
dat is de
moderne Vue 3 manier. Je hebt dan geen export default nodig.
- Nieuw:
src/components/FilmCard.vue(template met hardcoded filminfo) - Aanpassen:
src/App.vue(importeerFilmCarden gebruik hem in de template)
Klaar als de kaart zichtbaar is op de pagina met de hardcoded filmgegevens.
Theorie Single File Components · Template Syntax
1.3 Zet de basisstructuur op
Maak een AppHeader en een AppSidebar als aparte componentbestanden. Zorg
dat alle drie de componenten samen zichtbaar zijn op de pagina.
Wat moet je zien: bovenaan een header met de naam van de app, een sidebar met
navigatie-items zoals Overzicht en Mijn Watchlist, en de FilmCard uit
de
vorige opdracht.
Stijl geef je layout een eerste vorm — header bovenaan, sidebar links, FilmCard met een duidelijke kaart-uitstraling. Netjes is genoeg, mooi hoeft nog niet.
- Nieuw:
src/components/AppHeader.vue - Nieuw:
src/components/AppSidebar.vue - Aanpassen:
src/App.vue(gebruik alle drie de componenten in de template) - Bij scoped CSS:
<style scoped>per component voor de layout - Bij Tailwind: utility classes direct in de template (geen apart CSS-blok nodig)
Klaar als je hebt minstens drie aparte
.vue-bestanden
en
de pagina toont alle drie de onderdelen tegelijk met een herkenbare layout.
Theorie Denken in Code · SFC gebruiken · Styling in Vue
Niveau 2: Components & Props
2.1 Breid de componentenstructuur uit
Voeg twee nieuwe componenten toe: een FilterBar en een FilmList. Gebruik ze
allebei in je overzichtspagina.
Wat moet er in de componenten komen:
FilterBar: een rij met knoppen — Alles, Gezien en Wil kijken. De knoppen hoeven nog niets te doen, ze moeten alleen zichtbaar zijn.FilmList: een container die één of meerdereFilmCard-componenten in zich heeft. Zet er voorlopig twee of drieFilmCard's in met vaste inhoud.
Wat moet je zien: onder je AppHeader de FilterBar met
knoppen, en daaronder de FilmList met meerdere FilmCard's.
- Nieuw:
src/components/FilterBar.vue(knoppen, nog geen actie) - Nieuw:
src/components/FilmList.vue(container met meerdereFilmCard's) - Aanpassen:
src/App.vue(importeer en gebruik beide componenten)
Klaar als FilterBar en
FilmList staan elk
in een eigen bestand en zijn zichtbaar op de pagina.
Theorie Single File Components
2.2 Werk met props via defineProps()
Pas FilmCard aan zodat hij de filmgegevens ontvangt via props in plaats van hardcoded
waarden. Gebruik defineProps() met een object zodat je ook typen kunt aangeven. Render
daarna minstens drie verschillende films met hetzelfde component.
Wat moet je zien: drie filmkaarten met elk andere inhoud, allemaal gemaakt met
hetzelfde FilmCard component. Bijvoorbeeld:
- Kaart 1: Interstellar — Sci-Fi — 2014 — Gezien
- Kaart 2: Parasite — Thriller — 2019 — Gezien
- Kaart 3: Dune: Part Two — Sci-Fi — 2024 — Wil kijken
Stijl geef FilmCard een
herkenbare opmaak
met padding, een rand of schaduw, en zorg dat de filmtitel opvalt — dikker en groter dan de rest.
- Aanpassen:
src/components/FilmCard.vue(defineProps()toevoegen, hardcoded waarden vervangen door prop-waarden in de template) - Aanpassen:
src/components/FilmList.vue(drie keerFilmCardmet verschillende props)
Klaar als de gegevens van elke film worden
doorgegeven via props en
er staat niets meer hardcoded in FilmCard.vue.
Theorie Props & defineProps()
2.3 Gebruik slots voor een herbruikbaar paneel
Maak een herbruikbaar BasePanel component met een titel (via een prop) en een plek voor
willekeurige inhoud via <slot>. Gebruik dit component op minstens twee plekken met
telkens andere inhoud.
Wat moet je zien: twee panelen op de pagina, elk met een andere titel en andere
inhoud erin. Bijvoorbeeld een "Nu trending"-blok en een "Aanbevolen voor jou"-blok — beide gebouwd met
hetzelfde BasePanel.
- Nieuw:
src/components/BasePanel.vue(proptitle+<slot />in de template) - Aanpassen:
src/App.vue(twee keerBasePanelmet andere inhoud erin)
Klaar als je hebt één BasePanel
component dat je
minstens twee keer gebruikt met telkens andere slot-inhoud.
Theorie Slots
2.4 Render een filmlijst met v-for
Maak een array met filmobjecten in een apart databestand en render voor elk object een
FilmCard via v-for. Geef elk object een unieke id die je als
:key gebruikt.
Wat moet je zien: minstens vier filmkaarten die automatisch uit de array worden gegenereerd — je hebt ze dus niet handmatig in de template geschreven.
- Nieuw:
src/data/films.js(array met filmobjecten, elk met een uniekeid) - Aanpassen:
src/components/FilmList.vue(importeer de array, gebruikv-formet:key="film.id"omFilmCard's te renderen)
Klaar als als je een extra object aan de array toevoegt, verschijnt er automatisch een extra kaart op de pagina. Er staan geen sleutelwaarschuwingen in de console.
Theorie Lists & Keys · Props
Niveau 3: Reactivity & Events
3.1 Maak een gezien-toggle
Voeg bij elke filmkaart een knop toe waarmee je de status wisselt tussen Gezien en
Wil kijken. Gebruik ref() voor de lokale status en @click voor de
knop.
Wat moet je zien: als je op de knop klikt, verandert de tekst of stijl van de kaart direct — zonder paginarefresh.
Tip voor nu mag elke FilmCard z'n
eigen lokale
status-ref hebben. In niveau 4 tillen we dit omhoog zodat je ook kunt filteren op status.
- Aanpassen:
src/components/FilmCard.vue(ref()voor status + knop met@clickdie de waarde omwisselt)
Klaar als elke filmkaart heeft een eigen knop die de status wisselt zonder andere kaarten te beïnvloeden.
Theorie Reactivity (ref & reactive) · Events
3.2 Toggle een detailpaneel met v-if
Voeg aan elke filmkaart een knop Meer info toe. Als je erop klikt, verschijnt extra
informatie over die film in de kaart zelf. Klik je nog een keer, dan verdwijnt de informatie weer.
Gebruik v-if (of v-show) om dit te regelen.
Wat moet je zien: bij een klik klapt er extra informatie open, zoals een korte beschrijving of een regisseur. Een tweede klik sluit het weer.
Let op dit is een eenvoudige inline-toggle — geen aparte detailpagina. In niveau 6 bouw je pas een echte detailpagina met een eigen URL.
- Aanpassen:
src/components/FilmCard.vue(extraref()voortoonDetails+v-ifop de extra info)
Klaar als de extra informatie is standaard verborgen en wordt zichtbaar na een klik, zonder dat de pagina ververst.
Theorie v-if vs v-show · ref()
3.3 Actieve filterknop met dynamische class-binding
Maak drie filterknoppen — Alles, Gezien en
Wil kijken. De knop die je aanklikt moet er visueel anders uitzien dan de andere
twee.
Gebruik :class binding voor de conditionele styling.
Wat moet je zien: de actieve knop heeft een andere kleur of stijl. Als je op een andere knop klikt, wisselt de actieve stijl mee.
Let op de filmlijst hoeft in deze stap nog
niet echt gefilterd
te worden. Je oefent hier alleen met ref en dynamische class-binding — het echte filteren
komt in niveau 4.
Stijl zorg dat het verschil tussen actieve en inactieve knoppen duidelijk zichtbaar is.
- Aanpassen:
src/components/FilterBar.vue(ref()vooractieveFilter+:classop de knoppen op basis van de actieve waarde)
Klaar als er is altijd precies één actieve knop en de styling wisselt correct mee bij elke klik.
Theorie Events · ref() · :bind & class-binding
3.4 Live zoekpreview met v-model
Maak een invoerveld waarin een gebruiker een filmtitel kan typen. Gebruik v-model om de
waarde bij te houden. Onder het veld verschijnt meteen een preview van wat er getypt wordt.
Wat moet je zien: terwijl je typt verschijnt er onder het veld: Je zoekt naar: Interstellar. De preview past zich aan bij elke toetsaanslag.
- Nieuw:
src/components/SearchBar.vue(input metv-model+ live preview eronder met{{ zoekterm }}) - Aanpassen:
src/App.vue(importeer en gebruikSearchBar)
Klaar als de preview verandert live terwijl je typt, zonder dat je op een knop hoeft te klikken.
Theorie v-model Deep Dive · Events
3.5 Uitklapbare infopaneel in de sidebar
Maak een paneel in de sidebar met extra informatie, zoals "Recente toevoegingen" of een tip. Het paneel moet open en dicht kunnen via een knop.
Wat moet je zien: een knop waarmee het paneel zichtbaar en onzichtbaar wordt. Het paneel is standaard gesloten.
- Nieuw of aanpassen:
src/components/InfoPanel.vue(ref()voor open/dicht +v-showop de inhoud) - Aanpassen:
src/components/AppSidebar.vue(gebruik hetInfoPanel)
Klaar als het paneel opent en sluit via één knop, zonder paginarefresh.
Theorie ref() · v-if vs v-show
Niveau 4: Formulieren & Computed
4.1 Filter op status met computed
Voeg de filterknoppen toe boven je filmlijst — Alles, Gezien en
Wil kijken. Gebruik een computed() property om de gefilterde lijst te
berekenen op basis van de actieve filter.
Wat moet je zien: klik je op Gezien, dan verdwijnen de films met status Wil kijken. Klik je op Alles, dan zijn alle films weer zichtbaar.
- Aanpassen:
src/App.vue(state vooractieveFilter+ eencomputed()die de filmarray filtert) - Aanpassen:
src/components/FilterBar.vue(emit de gekozen filter omhoog naar de parent viadefineEmits()) - Aanpassen:
src/components/FilmList.vue(ontvangt de gefilterde lijst als prop)
Klaar als elke filterknop toont alleen de bijbehorende films en de computed herberekent automatisch als de filter of de filmarray verandert.
Theorie Computed Properties · ref() & reactive()
4.2 Bouw een film-toevoegformulier met v-model
Maak een formulier waarmee je een nieuwe film kunt toevoegen. Na het invullen en versturen verschijnt
de nieuwe film meteen in de lijst. Gebruik v-model op alle invoervelden.
Wat moet je zien: een formulier met minimaal een invoerveld voor de filmtitel en een knop Toevoegen. Na het klikken verschijnt de nieuwe film in de lijst en is het invoerveld weer leeg.
Let op de pagina mag niet refreshen bij het
versturen van het
formulier. Gebruik @submit.prevent.
- Nieuw:
src/components/FilmForm.vue(v-modelop input +@submit.prevent, emit de nieuwe film omhoog) - Aanpassen:
src/App.vue(ontvang de emit, voeg de film toe aan de array)
Klaar als nieuwe films verschijnen direct in de lijst na het versturen, zonder paginarefresh.
Theorie Forms & Input · v-model Deep Dive
4.3 Breid het formulier uit met meerdere velden
Voeg extra velden toe aan je filmformulier: titel, genre, jaar, regisseur en status
(Gezien / Wil kijken). Sla alle velden op in één reactive() object in plaats van losse
refs.
Wat moet je zien: een formulier met vijf velden. Na het versturen toont de nieuwe filmkaart al deze gegevens.
- Aanpassen:
src/components/FilmForm.vue(reactive()voor een formulierobject,v-modelmet property-binding per veld) - Aanpassen:
src/components/FilmCard.vue(de extra velden tonen)
Klaar als alle velden zijn gebonden aan één
reactive
object en de nieuwe filmkaart toont alle ingevulde gegevens.
Theorie Forms & Input · reactive()
4.4 Voeg formuliervalidatie toe
Zorg dat het formulier een foutmelding toont in drie situaties:
- Het titelsveld is leeg.
- Het jaarveld bevat geen getal of een ongeldig jaar (voor 1888 of na het huidige jaar).
- Er is nog geen genre geselecteerd.
De film mag pas worden toegevoegd als alle invoer correct is.
Wat moet je zien: laat je het titelveld leeg en klik je op Toevoegen, dan verschijnt er een duidelijke foutmelding. Bij elk fout veld een eigen melding.
- Aanpassen:
src/components/FilmForm.vue(eenrefofreactivevoor errors, validatielogica in de submit-handler, toon fouten metv-if)
Klaar als elke ongeldige situatie geeft een eigen zichtbare foutmelding en blokkeert het toevoegen van de film.
Theorie Forms & validatie · v-if
4.5 Reageer op wijzigingen met een watcher
Voeg een watch() toe die reageert op de zoekterm in je zoekbalk. Elke keer als de
zoekterm verandert, wordt een melding gelogd in de console met de nieuwe waarde. Voeg daarna een
watcher toe die ook de filmlijst live filtert op de zoekterm via een computed().
Wat moet je zien: typ je in de zoekbalk, dan past de filmlijst zich direct aan en zie je de zoekterm in de console.
- Aanpassen:
src/App.vue(watch(zoekterm, ...)voor de console-log, combineer zoeken én filteren in ééncomputed()) - Aanpassen:
src/components/SearchBar.vue(emit de zoekterm omhoog of geef hem mee via props)
Klaar als de filmlijst filtert live mee en de console toont de zoekterm bij elke wijziging. Als er geen resultaten zijn, staat er een melding op het scherm.
Theorie Watchers · Computed Properties
Niveau 5: Data & Opslag
5.1 Bewaar films in localStorage
Zorg dat de filmlijst bewaard blijft als de gebruiker de pagina refresht of de browser sluit. Gebruik
onMounted() om de opgeslagen films te laden en een watch() om ze op te slaan
als de lijst verandert.
Wat moet je zien: voeg een film toe, refresh de browser, en de film staat er nog steeds.
- Aanpassen:
src/App.vue(onMounted()om films te laden uitlocalStorage,watch(films, ...)om op te slaan bij elke wijziging)
Klaar als films verdwijnen niet meer na een paginarefresh.
Theorie localStorage in Vue · Lifecycle Hooks · watch()
5.2 Haal filmdata op van een API
Voeg een nieuw component TrendingMovies.vue toe dat een lijst van populaire films
ophaalt
van de DummyJSON API. Toon de opgehaalde films als een apart blok naast of onder je eigen lijst.
API
https://dummyjson.com/products?limit=10&category=smartphones — of gebruik een andere
openbare API naar keuze. Haal data op in onMounted() met fetch en
async/await.
Let op dit blok staat los van je eigen filmlijst. De opgehaalde data kun je niet bewerken of toevoegen aan je lijst. Het doel is leren hoe je externe data ophaalt en toont.
Wat moet je zien: een apart blok op de pagina met minstens vijf items uit de API, elk met minstens een naam en een extra gegeven. De data wordt geladen bij het openen van de pagina.
- Nieuw:
src/components/TrendingMovies.vue(onMounted()metfetch, opgeslagen in eenref([]), weergegeven metv-for) - Aanpassen:
src/App.vue(importeer en gebruikTrendingMovies)
Klaar als het blok staat op de pagina, haalt data op bij laden, en toont minstens vijf items.
Theorie Data Fetching · onMounted()
5.3 Voeg loading- en error-states toe
Breid opdracht 5.2 uit met drie extra situaties in het trending-blok: een laadmelding terwijl de data wordt opgehaald, een foutmelding als het ophalen mislukt, en een melding als er geen resultaten zijn.
Wat moet je zien:
- Tijdens het laden: Films laden...
- Bij een fout: een duidelijke foutmelding
- Als er geen items zijn: Geen films gevonden
Tip voor testen maak tijdelijk de URL kapot om de foutmelding te zien. Vergeet hem daarna weer terug te zetten.
- Aanpassen:
src/components/TrendingMovies.vue(extraref's voorloadingenerror, metv-if/v-else-if/v-elsevoor de drie situaties)
Klaar als alle drie de situaties een eigen zichtbare melding tonen.
Theorie Loading & Error States · v-if / v-else
5.4 Bewaar gebruikersvoorkeur
Maak een voorkeur die bewaard blijft na het refreshen van de pagina. Kies er één: een dark mode toggle of een standaard sorteerorde voor de filmlijst (bijv. op jaar of op titel).
Wat moet je zien: zet dark mode aan, refresh de pagina, en de app staat nog steeds in dark mode. Of: kies een sortering, refresh, en de sortering is bewaard.
- Aanpassen:
src/App.vue(refvoor de voorkeur, init vanuitlocalStorage,watchdie opslaat bij wijziging) - Bij scoped CSS: dark mode via een extra class op de root-wrapper
- Bij Tailwind:
dark-class conditioneel op de wrapper +dark:variants
Klaar als de gekozen voorkeur blijft bewaard na een paginarefresh.
Theorie localStorage · watch()
Niveau 6: Routing & Auth
6.1 Maak meerdere pagina's met Vue Router
Bouw je app uit naar meerdere pagina's: een overzichtspagina, een loginpagina en een filmdetailpagina. Voeg een navigatiebalk toe zodat je tussen de pagina's kunt wisselen.
Aanpak in 4 stappen:
- Installeer
vue-routeren maaksrc/router/index.jsaan. - Maak drie views aan in
src/pages/:HomeView.vue,LoginView.vue,FilmDetailView.vue. Verhuis de huidige inhoud vanApp.vuenaarHomeView.vue. - Koppel de routes in
router/index.jsen registreer de router inmain.js. - Zet
<RouterView />inApp.vueen gebruik<RouterLink>in je navigatie.
Wat moet je zien: je kunt via de navigatiebalk wisselen tussen minstens drie pagina's, zonder dat de browser ververst.
Stijl laat duidelijk zien op welke pagina
de gebruiker
zich bevindt — de RouterLink met de actieve klasse (.router-link-active)
anders stylen.
- Installeren:
npm install vue-router - Nieuw:
src/router/index.js(routes voor home, login en filmdetail) - Aanpassen:
src/main.js(router registreren metapp.use(router)) - Nieuw:
src/pages/HomeView.vue(inhoud van huidige App.vue hierheen) - Nieuw:
src/pages/LoginView.vue - Nieuw:
src/pages/FilmDetailView.vue - Aanpassen:
src/App.vue(alleen nog<RouterView />en de navigatiebalk)
Klaar als alle pagina's bereikbaar zijn via de navigatiebalk en de URL verandert mee bij elke pagina.
Theorie Vue Router
6.2 Werk met URL parameters voor filmdetail
Maak de filmkaarten klikbaar. Als je op een film klikt, ga je naar een detailpagina met de ID van die film in de URL. Op de detailpagina zie je de volledige informatie van die specifieke film.
Wat moet je zien: klik je op film 3, dan ga je naar een URL zoals
/films/3. Op die pagina staan alle gegevens van die film.
- Aanpassen:
src/router/index.js(route toevoegen:/films/:id) - Aanpassen:
src/pages/FilmDetailView.vue(useRoute()omidte lezen, film opzoeken in de array of store) - Aanpassen:
src/components/FilmCard.vue(<RouterLink :to="`/films/${film.id}`">rond de kaart of titel)
Klaar als elke film heeft een eigen detailpagina met de juiste gegevens en een werkende URL.
Theorie useRouter & useRoute
6.3 Navigeer programmatisch na een actie
Maak een eenvoudig loginformulier met een veld voor gebruikersnaam en wachtwoord. Na het versturen
navigeert de app automatisch naar de overzichtspagina met useRouter(), zonder dat de
gebruiker op een link hoeft te klikken.
Wat moet je zien: vul het formulier in, klik op Inloggen, en de app stuurt je automatisch naar het overzicht.
Houd het simpel: je hoeft de ingevulde gegevens nog niet echt te controleren —
elke invoer mag meteen doorsturen. Het doel is programmatisch navigeren met
useRouter().
In 6.4 maken we dit af met een echte Pinia store.
- Aanpassen:
src/pages/LoginView.vue(formulier +useRouter(), in de submit-handler →router.push('/'))
Klaar als na het versturen navigeert de app
automatisch naar het
overzicht via router.push().
Theorie useRouter · Login & navigatie
6.4 Maak een Pinia store voor de auth-state
Bouw een Pinia store die bijhoudt of een gebruiker ingelogd is en wat de gebruikersnaam is. De
navigatiebalk toont andere opties afhankelijk van de inlogstatus. Bewaar de staat ook in
localStorage zodat de gebruiker na een refresh ingelogd blijft.
Pas opdracht 6.3 aan: bij het versturen van het loginformulier roep je login() aan in
de store vóór je navigeert.
Wat moet je zien:
- Niet ingelogd: navigatiebalk toont alleen Login
- Wel ingelogd: navigatiebalk toont Overzicht, de gebruikersnaam en Uitloggen
- Refresh na login: je blijft ingelogd
- Installeren:
npm install pinia - Aanpassen:
src/main.js(app.use(createPinia())) - Nieuw:
src/stores/auth.js(defineStoremetuserstate,login()enlogout()actions, sync metlocalStorage) - Aanpassen:
src/pages/LoginView.vue(useAuthStore()aanroepen) - Aanpassen:
src/components/AppHeader.vueof navigatiebalk (useAuthStore()voor conditionele navigatie-items)
Klaar als de navigatiebalk past automatisch aan als de inlogstatus verandert, en een refresh gooit je er niet uit.
Theorie Pinia · Auth Store Setup · localStorage
6.5 Bescherm routes met een Navigation Guard
Zorg dat de overzichtspagina en de filmdetailpagina alleen toegankelijk zijn voor ingelogde
gebruikers. Niet-ingelogde bezoekers worden automatisch doorgestuurd naar de loginpagina via een
router.beforeEach() guard.
Wat moet je zien: log uit, typ / of /films/1 in de
adresbalk, en je komt automatisch op de loginpagina terecht.
- Aanpassen:
src/router/index.js(router.beforeEach()diemeta.requiresAuthcontroleert + doorverwijst naar login) - Aanpassen: routes voor home en filmdetail (
meta: { requiresAuth: true }toevoegen)
Klaar als beveiligde routes zijn onbereikbaar voor niet-ingelogde gebruikers, ook als ze de URL direct intypen.
Theorie Protected Routes · Navigation Guards
Niveau 7: Vue Specials & Afwerking
7.1 Deel thema-state via Provide / Inject
Verplaats je dark mode state van App.vue naar een centrale plek via
provide(). Componenten diep in de boom kunnen de staat dan uitlezen met
inject() — zonder dat je hem door elke laag als prop hoeft door te geven.
Wat moet je zien: een dark mode toggle in de header die via inject()
de staat opheft, en de rest van de app reageert direct zonder props te gebruiken.
- Aanpassen:
src/App.vue(provide('darkMode', darkMode)enprovide('toggleDarkMode', toggleFn)) - Aanpassen:
src/components/AppHeader.vue(inject('darkMode')eninject('toggleDarkMode'))
Klaar als de dark mode toggle werkt via provide/inject zonder dat de state als prop wordt doorgegeven.
Theorie Provide / Inject
7.2 Extraheer logica naar een Composable
Maak een composable useFilms.js die alle filmlogica bevat: de filmarray, de computed
voor filteren/zoeken, de functie voor toevoegen, en de localStorage-synchronisatie. Importeer deze
composable in HomeView.vue in plaats van dat alle logica direct in de component staat.
Wat moet je zien: HomeView.vue is een stuk kleiner en roept alleen nog
useFilms() aan. Alle filmlogica zit netjes in één apart bestand.
- Nieuw:
src/composables/useFilms.js(alle film-state, computed en functies hierheen verplaatsen, alles retourneren) - Aanpassen:
src/pages/HomeView.vue(import { useFilms } from '../composables/useFilms'+ destructuren)
Klaar als de app werkt nog steeds exact hetzelfde, maar de filmlogica zit nu in een aparte composable.
Theorie Composables
7.3 Gebruik Template Refs voor DOM-toegang
Voeg een zoekbalk toe die automatisch focus krijgt als de pagina laadt. Gebruik daarvoor een
ref in de template (ref="zoekInput") en roep .focus() aan in
onMounted().
Wat moet je zien: als je naar de overzichtspagina navigeert, staat de cursor meteen in het zoekveld — zonder dat je erop hoeft te klikken.
- Aanpassen:
src/components/SearchBar.vueofsrc/pages/HomeView.vue(const zoekInput = ref(null)+ref="zoekInput"op het input-element +zoekInput.value.focus()inonMounted())
Klaar als het zoekveld heeft automatisch focus als de pagina laadt.
Theorie Template Refs · Lifecycle Hooks
7.4 Bouw een modal met Teleport
Maak een bevestigingsmodal die verschijnt als je een film wilt verwijderen uit je lijst. Gebruik
<Teleport to="body"> zodat de modal buiten de normale componentenstructuur in de
DOM staat — wat z-index-problemen voorkomt.
Wat moet je zien: klik je op een verwijderknop bij een film, dan verschijnt er een
modal met de vraag "Weet je het zeker?" met twee knoppen: Verwijderen en Annuleren.
De modal staat in de body in de DOM-inspector.
- Nieuw:
src/components/ConfirmModal.vue(<Teleport to="body">als wrapper, twee knoppen met emits) - Aanpassen:
src/components/FilmCard.vue(verwijderknop + gebruik vanConfirmModal) - Aanpassen:
src/composables/useFilms.jsofHomeView.vue(verwijder-handler op basis van film-id)
Klaar als de modal verschijnt bij het klikken op
verwijderen,
Verwijderen verwijdert de film echt, en in de DevTools staat de modal direct als
kind van body.
Theorie Teleport
7.5 Maak de styling af
Je hebt tijdens de vorige niveaus al stap voor stap aan styling gewerkt. In deze opdracht poets je de app op tot een geheel dat er samenhangend uitziet. Loop je app door van de loginpagina tot de filmdetailpagina en zorg dat alles consistent oogt.
Wat moet je zien:
- Eén herkenbaar kleurenpalet door de hele app.
- Consistente afstanden, lettergroottes en knopstijlen tussen pagina's.
- De app werkt ook goed op een kleiner scherm (responsive).
- Foutmeldingen, statusindicatoren en actieve knoppen vallen duidelijk op.
Je werkt met wat je aan het begin hebt gekozen: scoped CSS of Tailwind. Voeg geen nieuwe stylingmethode toe — polish wat je hebt.
- Aanpassen:
src/style.css(globale stijlen: CSS-variabelen voor kleuren, basis lettertype) - Aanpassen:
<style scoped>blokken in alle componenten (consistente spacing en kleuren) - Of bij Tailwind:
tailwind.config.js(eigen kleuren intheme.extend) + responsive classes toepassen
Klaar als de app oogt als één geheel met consistente kleuren, lettertypes en knoppen op alle pagina's, en de layout schaalt netjes op een smaller scherm.
Theorie Styling in Vue · Tailwind CSS
7.6 Publiceer je app op GitHub Pages
Zet je Film Tracker online zodat iedereen hem kan bekijken. Gebruik de stappen uit de theoriepagina om je Vue-app te builden en te deployen via GitHub Pages.
Wat moet je zien: een werkende URL (bijv.
https://jouwgebruikersnaam.github.io/film-tracker/) waarop je app volledig functioneert,
inclusief routing.
Let op Vue Router in history-mode werkt niet
standaard op
GitHub Pages. Schakel over naar hash-mode of volg de stappen in de theoriepagina voor een
404.html-oplossing.
- Aanpassen:
vite.config.js(baseinstellen op de naam van je GitHub-repository) - Aanpassen:
src/router/index.js(eventueel omschakelen naar hash-mode) - Uitvoeren:
npm run build+ deployen via GitHub Pages of GitHub Actions
Klaar als je app bereikbaar is op een publieke GitHub Pages URL en de routing werkt.
Theorie Website Publiceren
7.7 Eindchallenge
Voeg zelf minstens drie extra features toe aan je Film Tracker. Kies features die voor jou interessant zijn.
Voorbeelden van extra features:
- Sterrenbeoordeling toevoegen aan gezien films (1-5 sterren)
- Sorteren op jaar, titel of beoordeling
- Favorieten markeren met een hartje
- Een watchlist-pagina met alleen de films die je nog wilt zien
- Animaties bij het toevoegen of verwijderen van films met
v-motionof CSS transitions - Een statistiekenpagina: totaal gezien, favoriete genre, gemiddelde beoordeling
- Films bewerken na het toevoegen
Wat moet je kunnen laten zien: welke drie features je hebt toegevoegd, waarom je ze hebt gekozen en wat ze doen in de app.
- Afhankelijk van je keuze — per feature meestal een nieuw component in
src/components/en aanpassingen in de relevante view of store - Voorbeelden:
- Sterrenbeoordeling →
src/components/StarRating.vue(knopjes + emit naar parent) - Favorieten →
src/components/FilmCard.vue(hartje-knop) +useFilms.js(favoriet-toggle opslaan) - Statistieken →
src/pages/StatsView.vue+ route + computed-berekeningen
- Sterrenbeoordeling →
Klaar als minstens drie zelfgekozen features werken aantoonbaar in de app.
Theorie Composables · Pinia · Computed