Vodnik za pridobivanje soglasja za piškotke in implementacijo SPA

Razumevanje izziva

Enostranske aplikacije (SPA), zgrajene z React, Vue, Angular ali drugimi okvirji, predstavljajo edinstvene izzive za TermsFeed Cookie Consent, ker:

  1. Stran se ne osveži. Stran se ne ponovno naloži kot klasične spletne strani.
  2. Dinamična vsebina. Vsebina se nalaga dinamično, ne da bi ponovno sprožila DOMContentLoaded.
  3. Spremembe poti. Navigacija poteka prek JavaScript, ne pa prek tradicionalnega nalaganja strani.
  4. Upravljanje stanja. Stanje soglasja mora ostati nespremenjeno kljub spremembam poti.

Rešitev: Ponovno nalaganje ob navigaciji

Problem

»Imam React SPA in pasica s piškotki se prikaže le ob prvem nalaganju strani. Ko uporabniki preidejo na druge strani, se pasica ne prikaže ponovno, če niso izbrali možnosti. Poleg tega, če sprejmejo piškotke na eni strani, se skripti na naslednjih straneh ne nalagajo, dokler ne pride do popolne osvežitve.«

Rešitev

Pri implementaciji TermsFeed Cookie Consent v SPA morate:

  1. Inicializirati soglasje ob prvem nalaganju.
  2. Zaznati spremembe poti.
  3. Po potrebi ponovno naložiti pasico.
  4. Ponovno preveriti stanje soglasja.

Implementacija v React

Korak 1: Ustvarite komponento za soglasje s piškotki

// 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;

Korak 2: Dodajte komponento v aplikacijo

// 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;

Implementacija Vue.js

Korak 1: Ustvarite vtičnik Cookie Consent

// 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);
            });
        }
    }
};

Korak 2: Uporabite v glavni aplikaciji

// 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');

Korak 3: Ustvarite komponento aplikacije

<!-- 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>

Implementacija Angular

Korak 1: Ustvarite storitev za soglasje s piškotki

// 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();
        }
    }
}

Korak 2: Inicializirajte v komponenti aplikacije

// 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);
        });
    }
}

Pogoste težave s SPA in soglasjem za piškotke

Težava 1: Pasica se ob navigaciji ne prikaže ponovno

Problem

Uporabnik navigacijo nadaljuje, ne da bi izbral možnost, pasica pa izgine.

Rešitev

Preverite, ali je bilo soglasje dano ob spremembi poti.

// 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
    }
});

Težava 2: Skripti se ne naložijo po privolitvi na novih poteh

Problem

"Uporabnik sprejme piškotke, navigacijo na drugo stran v moji aplikaciji React, vendar Google Analytics ne sledi ogledu nove strani."

Rešitev

Uporabite povratne klice za sledenje ogleda strani ob vsaki spremembi poti.

// 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
                });
            });
        }
    }
}

Težava 3: Prikaže se več primerov pasice

Problem

Pasica se pri hitrem prehodu med stranmi prikaže večkrat.

Rešitev

Uporabite varovalo, da preprečite večkratno inicializacijo

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

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

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

Programsko odprite center nastavitev

Za SPA-je boste morda želeli odpreti nastavitve brez statičnega gumba:

// 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>