De uitdaging begrijpen

Single Page Applications (SPA's) die zijn gebouwd met React, Vue, Angular of andere frameworks vormen een unieke uitdaging voor TermsFeed Cookie Consent, omdat:

  1. Geen paginavernieuwing. De pagina wordt niet opnieuw geladen zoals bij klassieke webpagina's.
  2. Dynamische inhoud. Inhoud wordt dynamisch geladen zonder dat DOMContentLoaded opnieuw wordt geactiveerd.
  3. Routewijzigingen. Navigatie gebeurt via JavaScript, niet via traditionele paginaladingen.
  4. Statusbeheer. De toestemmingsstatus moet behouden blijven bij routewijzigingen.

Oplossing: opnieuw laden bij navigatie

Probleem

"Ik heb een React SPA en de cookiebanner wordt alleen weergegeven bij het laden van de eerste pagina. Wanneer gebruikers naar andere pagina's navigeren, verschijnt de banner niet opnieuw als ze geen keuze hebben gemaakt. Ook als ze cookies op één pagina accepteren, worden scripts op volgende pagina's pas geladen na een volledige verversing."

Oplossing

Bij het implementeren van TermsFeed Cookie Consent in een SPA moet u het volgende doen:

  1. De toestemming bij de eerste laadbeurt initialiseren.
  2. Routewijzigingen detecteren.
  3. De banner indien nodig opnieuw laden.
  4. De status van de toestemming opnieuw controleren.

React-implementatie

Stap 1: Maak een Cookie Consent-component

// CookieConsent.js
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

const CookieConsent = () => {
    const location = useLocation();

    useEffect(() => {
        // Load TermsFeed script if not already loaded
        if (!window.cookieconsent) {
            const script = document.createElement('script');
            script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
            script.charset = 'UTF-8';
            script.onload = () => initializeCookieConsent();
            document.head.appendChild(script);
        } else {
            // Script already loaded, just check if banner needs to be shown
            checkBannerState();
        }
    }, []); // Run once on mount

    useEffect(() => {
        // Check banner state on route change
        if (window.cookieconsent) {
            checkBannerState();
        }
    }, [location]); // Run on every route change

    const initializeCookieConsent = () => {
        if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
            window.cookieconsent.run({
                "notice_banner_type": "headline",
                "consent_type": "express",
                "palette": "light",
                "language": "en",
                "website_name": "My SPA",
                "website_privacy_policy_url": "https://mysite.com/privacy",
                "open_preferences_center_selector": "#changePreferences",
                "page_load_consent_levels": ["strictly-necessary"],
                "callbacks": {
                    "scripts_specific_loaded": (level) => {
                        console.log("Scripts loaded for: " + level);
                        // Re-initialize any components that need tracking
                        if (level === 'tracking') {
                            reinitializeAnalytics();
                        }
                    }
                }
            });
        }
    };

    const checkBannerState = () => {
        // Check if user has already made a consent choice
        const consentCookie = getCookie('cookie_consent_level');
        if (!consentCookie) {
            // No consent yet, banner should be visible
            // No action needed - banner will show automatically
        }
    };

    const getCookie = (name) => {
        const value = ; ${document.cookie};
        const parts = value.split(; ${name}=);
        if (parts.length === 2) return parts.pop().split(';').shift();
    };

    const reinitializeAnalytics = () => {
        // Re-run any analytics initialization needed
        if (window.gtag) {
            window.gtag('config', 'G-XXXXXXXXXX', {
                'page_path': location.pathname
            });
        }
    };

    return null; // This component doesn't render anything
};

export default CookieConsent;

Stap 2: Voeg toe aan de App-component

// App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import CookieConsent from './components/CookieConsent';
import Home from './pages/Home';
import About from './pages/About';

function App() {
    return (
        <Router>
            <CookieConsent />
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/about" element={<About />} />
            </Routes>

            {/ Preferences button /}
            <button id="changePreferences" style={{
                position: 'fixed',
                bottom: '20px',
                right: '20px',
                zIndex: 9999
            }}>
                Cookie Settings
            </button>

            <noscript>
                Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed</a>
            </noscript>
        </Router>
    );
}

export default App;

Vue.js-implementatie

Stap 1: Maak de Cookie Consent-plugin aan

// plugins/cookieConsent.js
export default {
    install(app, options) {
        // Load script
        const loadCookieConsent = () => {
            if (window.cookieconsent) return Promise.resolve();

            return new Promise((resolve, reject) => {
                const script = document.createElement('script');
                script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
                script.charset = 'UTF-8';
                script.onload = () => resolve();
                script.onerror = () => reject();
                document.head.appendChild(script);
            });
        };

        // Initialize
        const initCookieConsent = () => {
            if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
                window.cookieconsent.run({
                    "notice_banner_type": "headline",
                    "consent_type": "express",
                    "palette": "light",
                    "language": "en",
                    "website_name": options.websiteName || "My Vue SPA",
                    "website_privacy_policy_url": options.privacyUrl || "#",
                    "open_preferences_center_selector": "#changePreferences",
                    "page_load_consent_levels": ["strictly-necessary"]
                });
            }
        };

        // Make available globally
        app.config.globalProperties.$cookieConsent = {
            load: loadCookieConsent,
            init: initCookieConsent
        };

        // Auto-initialize on mount
        if (typeof window !== 'undefined') {
            loadCookieConsent().then(() => {
                document.addEventListener('DOMContentLoaded', initCookieConsent);
            });
        }
    }
};

Stap 2: Gebruik in de hoofdapp

// main.js
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import cookieConsentPlugin from './plugins/cookieConsent';

const router = createRouter({
    history: createWebHistory(),
    routes: [
        { path: '/', component: () => import('./views/Home.vue') },
        { path: '/about', component: () => import('./views/About.vue') }
    ]
});

const app = createApp(App);

app.use(router);
app.use(cookieConsentPlugin, {
    websiteName: 'My Vue App',
    privacyUrl: 'https://mysite.com/privacy'
});

app.mount('#app');

Stap 3: Maak de app-component aan

<!-- App.vue -->
<template>
    <div id="app">
        <router-view />

        <button id="changePreferences" class="cookie-settings-btn">
            Cookie Settings
        </button>

        <noscript>
            Free cookie consent management tool by
            <a href="https://www.termsfeed.com/">TermsFeed</a>
        </noscript>
    </div>
</template>

<script>
export default {
    name: 'App',
    mounted() {
        // Watch for route changes
        this.$router.afterEach((to, from) => {
            // Check if consent banner needs to be re-shown
            this.checkConsentState();
        });
    },
    methods: {
        checkConsentState() {
            const consentCookie = document.cookie
                .split('; ')
                .find(row => row.startsWith('cookie_consent_level='));

            if (!consentCookie) {
                console.log('No consent given yet');
            }
        }
    }
};
</script>

<style>
.cookie-settings-btn {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 9999;
    padding: 10px 20px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}
</style>

Angular-implementatie

Stap 1: Maak de Cookie Consent-service aan

// services/cookie-consent.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';

declare global {
    interface Window {
        cookieconsent: any;
    }
}

@Injectable({
    providedIn: 'root'
})
export class CookieConsentService {
    private scriptLoaded = false;

    constructor(private router: Router) {}

    initialize(): Promise<void> {
        return this.loadScript().then(() => {
            this.initializeConsent();
            this.watchRouteChanges();
        });
    }

    private loadScript(): Promise<void> {
        if (this.scriptLoaded || window.cookieconsent) {
            return Promise.resolve();
        }

        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = '//www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js';
            script.charset = 'UTF-8';
            script.onload = () => {
                this.scriptLoaded = true;
                resolve();
            };
            script.onerror = () => reject();
            document.head.appendChild(script);
        });
    }

    private initializeConsent(): void {
        if (window.cookieconsent && typeof window.cookieconsent.run === 'function') {
            window.cookieconsent.run({
                notice_banner_type: 'headline',
                consent_type: 'express',
                palette: 'light',
                language: 'en',
                website_name: 'My Angular App',
                website_privacy_policy_url: 'https://mysite.com/privacy',
                open_preferences_center_selector: '#changePreferences',
                page_load_consent_levels: ['strictly-necessary'],
                callbacks: {
                    scripts_specific_loaded: (level: string) => {
                        console.log('Scripts loaded for:', level);
                        this.handleConsentUpdate(level);
                    }
                }
            });
        }
    }

    private watchRouteChanges(): void {
        this.router.events.pipe(
            filter(event => event instanceof NavigationEnd)
        ).subscribe(() => {
            // Check consent state on route change
            this.checkConsentState();
        });
    }

    private checkConsentState(): void {
        const consentCookie = this.getCookie('cookie_consent_level');
        if (!consentCookie) {
            console.log('No consent given yet');
        }
    }

    private handleConsentUpdate(level: string): void {
        // Handle consent updates
        if (level === 'tracking') {
            this.initializeAnalytics();
        }
    }

    private initializeAnalytics(): void {
        // Re-initialize analytics on consent
        if ((window as any).gtag) {
            (window as any).gtag('config', 'G-XXXXXXXXXX', {
                page_path: this.router.url
            });
        }
    }

    private getCookie(name: string): string | null {
        const value = ; ${document.cookie};
        const parts = value.split(; ${name}=);
        if (parts.length === 2) {
            return parts.pop()?.split(';').shift() || null;
        }
        return null;
    }

    openPreferences(): void {
        if (window.cookieconsent && typeof window.cookieconsent.openPreferencesCenter === 'function') {
            window.cookieconsent.openPreferencesCenter();
        }
    }
}

Stap 2: Initialiseer in de app-component

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { CookieConsentService } from './services/cookie-consent.service';

@Component({
    selector: 'app-root',
    template: 
        <router-outlet></router-outlet>

        <button id="changePreferences" class="cookie-settings-btn">
            Cookie Settings
        </button>

        <noscript>
            Free cookie consent management tool by
            <a href="https://www.termsfeed.com/">TermsFeed</a>
        </noscript>
    ,
    styles: [
        .cookie-settings-btn {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 9999;
            padding: 10px 20px;
            background: #007bff;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
    ]
})
export class AppComponent implements OnInit {
    constructor(private cookieConsentService: CookieConsentService) {}

    ngOnInit(): void {
        this.cookieConsentService.initialize().catch(error => {
            console.error('Failed to initialize cookie consent:', error);
        });
    }
}

Probleem 1: Banner verschijnt niet opnieuw bij navigatie

Probleem

De gebruiker navigeert zonder een keuze te maken, de banner verdwijnt.

Oplossing

Controleer of er toestemming is gegeven bij het wisselen van route.

// Check if consent was given on route change
router.afterEach(() => {
    const hasConsent = getCookie('cookie_consent_level');
    if (!hasConsent) {
        // Banner should be visible - it will show automatically
        // No manual action needed if properly configured
    }
});

Probleem 2: Scripts worden niet geladen na toestemming op nieuwe routes

Probleem

"Gebruiker accepteert cookies, navigeert naar een andere pagina in mijn React-app, maar Google Analytics registreert de nieuwe paginaweergave niet."

Oplossing

Gebruik callbacks om de paginaweergave bij elke routewijziging bij te houden.

// In your callback
"callbacks": {
    "scripts_specific_loaded": (level) => {
        if (level === 'tracking' && window.gtag) {
            // Track page view on every route change
            router.afterEach((to) => {
                gtag('config', 'G-XXXXXXXXXX', {
                    'page_path': to.path
                });
            });
        }
    }
}

Probleem 3: Er verschijnen meerdere bannerinstanties

Probleem

De banner verschijnt meerdere keren bij snel navigeren.

Oplossing

Gebruik een guard om meerdere initialisaties te voorkomen

// Use a guard to prevent multiple initializations
let isInitialized = false;

const initCookieConsent = () => {
    if (isInitialized) return;
    isInitialized = true;

    cookieconsent.run({ / config / });
};

Open het voorkeurencentrum programmatisch

Voor SPA's wilt u misschien de voorkeuren openen zonder een statische knop:

// Method 1: Using the openPreferencesCenter() method
const openPreferences = () => {
    if (window.cookieconsent && typeof window.cookieconsent.openPreferencesCenter === 'function') {
        window.cookieconsent.openPreferencesCenter();
    } else {
        console.error('Cookie consent not initialized');
    }
};

// In React
<button onClick={openPreferences}>Change Cookie Settings</button>

// In Vue
<button @click="openPreferences">Change Cookie Settings</button>

// In Angular
<button (click)="openPreferences()">Change Cookie Settings</button>