I implemented the same program as the demo below in my application. Calling the SetTheme method saves the setting in a cookie, but calling the SetWcag method does not, and the setting reverts to its original state the next time I access the application.
The environment is Blazor Web Apps (Interactive Server), and I'm using Radzen.Blazor 9.0.4.
Indeed the CookieThemeService only persist the current theme name only. You can create your own service to persist anything else you would like. You can use the source of the CookieThemeService as a start.
I copied the source code of RadzenCookieThemeService, created RadzenCookieThemeWcagService, and set it up in the same way.
I'm now able to reflect the Wcag setting values in cookies, but there's one thing that just won't work.
Set the Wcag value to true and then close the app.
Start the app and set the Wcag value to false → Error occurs.
System.AggregateException: One or more errors occurred. (TypeError: Cannot read properties of null (reading 'removeChild'))
---> System.InvalidOperationException: TypeError: Cannot read properties of null (reading 'removeChild')
at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32
updatedComponents)
--- End of inner exception stack trace ---
Uncaught Error: No interop methods are registered for renderer 1
at A (blazor.web.js:1:13622)
at blazor.web.js:1:13528
at D (blazor.web.js:1:13711)
at R (blazor.web.js:1:13502)
at P.dispatchGlobalEventToAllElements (blazor.web.js:1:16123)
at P.onGlobalEvent (blazor.web.js:1:15332)
The following program was created:
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
namespace Radzen
{
public class CookieThemeWcagServiceOptions
{
/// <summary>
/// Gets or sets the cookie name.
/// </summary>
public string Name { get; set; } = "Theme";
/// <summary>
/// Gets or sets the cookie duration.
/// </summary>
public TimeSpan Duration { get; set; } = TimeSpan.FromDays(365);
/// <summary>
/// Gets or sets a value indicating whether to use secure cookies.
/// </summary>
public bool IsSecure { get; set; }
/// <summary>
/// Gets or sets the SameSite attribute for the cookie.
/// </summary>
public CookieSameSiteMode? SameSite { get; set; }
}
public class CookieThemeWcagService : IDisposable
{
private readonly CookieThemeWcagServiceOptions options;
private readonly IJSRuntime jsRuntime;
private readonly ThemeService themeService;
public CookieThemeWcagService(IJSRuntime jsRuntime, ThemeService themeService, IOptions<CookieThemeWcagServiceOptions>? options)
{
this.jsRuntime = jsRuntime;
this.themeService = themeService;
this.options = options?.Value ?? new CookieThemeWcagServiceOptions();
if (themeService != null)
{
themeService.ThemeChanged += OnThemeChanged;
}
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
try
{
var cookies = await jsRuntime.InvokeAsync<string>("eval", "document.cookie");
var wcagCookie = cookies?.Split("; ").Select(x =>
{
var parts = x.Split("=");
return (Key: parts[0], Value: parts[1]);
})
.FirstOrDefault(x => x.Key == options.Name);
if (bool.TryParse(wcagCookie?.Value, out var wcag))
{
themeService.SetWcag(wcag);
}
}
catch (InvalidOperationException)
{
}
}
private void OnThemeChanged()
{
var wcag = themeService.Wcag == true;
var expiration = DateTime.Now.Add(options.Duration);
var cookie = $"{options.Name}={wcag}; expires={expiration:R}; path=/";
if (options.SameSite.HasValue)
{
cookie += $"; SameSite={options.SameSite}";
}
if (options.IsSecure)
{
cookie += "; Secure";
}
//_ = jsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{cookie}\"");
}
/// <inheritdoc />
public void Dispose()
{
themeService.ThemeChanged -= OnThemeChanged;
GC.SuppressFinalize(this);
}
}
public static class CookieThemeWcagServiceCollectionExtensions
{
public static IServiceCollection AddRadzenCookieThemeWcagService(this IServiceCollection services)
{
services.AddOptions<CookieThemeWcagServiceOptions>();
services.AddScoped<CookieThemeWcagService>();
return services;
}
public static IServiceCollection AddRadzenCookieThemeWcagService(this IServiceCollection services, Action<CookieThemeWcagServiceOptions> configure)
{
services.Configure(configure);
services.AddScoped<CookieThemeWcagService>();
return services;
}
}
}