Guía de implementación del consentimiento de cookies y SPA
Comprender el reto
Las aplicaciones de página única (SPA) creadas con React, Vue, Angular u otros marcos de trabajo plantean retos únicos para el consentimiento de cookies de TermsFeed porque:
- No se actualiza la página. La página no se recarga como las páginas web clásicas.
- Contenido dinámico. El contenido se carga dinámicamente sin volver a activar DOMContentLoaded.
- Cambios de ruta. La navegación se realiza mediante JavaScript, no mediante cargas de página tradicionales.
- Gestión del estado. El estado del consentimiento debe persistir a pesar de los cambios de ruta.
Solución: Recargar al navegar
Problema
«Tengo una aplicación React SPA y el banner de cookies solo se muestra al cargar la primera página. Cuando los usuarios navegan a otras páginas, el banner no vuelve a aparecer si no han tomado una decisión. Además, si aceptan las cookies en una página, los scripts no se cargan en las páginas siguientes hasta que se realiza una actualización completa».
Solución
Al implementar TermsFeed Cookie Consent en una SPA, debes:
- Inicializar el consentimiento en la primera carga.
- Detectar cambios de ruta.
- Recargar el banner si es necesario.
- Volver a comprobar el estado del consentimiento.
Implementación en React
Paso 1: Crear un componente de consentimiento de cookies
// 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;
Paso 2: Añadir al componente de la aplicación
// 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;
Implementación en Vue.js
Paso 1: Crear el complemento de consentimiento de cookies
// 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);
});
}
}
};
Paso 2: Usarlo en la aplicación principal
// 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');
Paso 3: Crear el componente de la aplicación
<!-- 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>
Implementación en Angular
Paso 1: Crear el servicio de consentimiento de cookies
// 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();
}
}
}
Paso 2: Inicializar en el componente de la aplicación
// 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);
});
}
}
Problemas comunes con las SPA y el consentimiento de cookies
Problema 1: El banner no vuelve a aparecer al navegar
Problema
El usuario navega sin tomar una decisión y el banner desaparece.
Solución
Comprueba si se ha dado el consentimiento al cambiar de ruta.
// 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
}
});
Problema 2: Los scripts no se cargan tras el consentimiento en nuevas rutas
Problema
«El usuario acepta las cookies, navega a otra página de mi aplicación React, pero Google Analytics no registra la nueva visita a la página».
Solución
Utiliza callbacks para rastrear la visita a la página en cada cambio de ruta.
// 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
});
});
}
}
}
Problema 3: Aparecen varias instancias del banner
Problema
El banner aparece varias veces al navegar rápidamente.
Solución
Utiliza un guard para evitar inicializaciones múltiples
// Use a guard to prevent multiple initializations
let isInitialized = false;
const initCookieConsent = () => {
if (isInitialized) return;
isInitialized = true;
cookieconsent.run({ / config / });
};
Abrir el Centro de preferencias mediante programación
En el caso de las aplicaciones de página única (SPA), es posible que desee abrir las preferencias sin un botón estático:
// 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>