Local storage provides a convenient way to store data locally in the browser, offering persistence data across sessions and page refreshes. Let's explore how to effectively utilise local storage in Blazor.
Introduction
Local storage, a key-value data structure available within modern web browsers, enables applications to store data in the browser's memory. This data persists even after the user closes the browser or navigates away from the page. It serves as a temporary storage solution for non-sensitive static data, reducing server load, and improving performance by minimizing server calls.
Background
Blazor applications can access local storage using JavaScript interop, allowing seamless integration with browser-based storage. In this article, we'll explore how Blazor leverages local storage to enhance user experiences and optimise application performance.
Demo
Here is the demo of the final product, You can see how Blazor utilizes the local storage:
Gif 1: User can Add, Update or Delete data from browser's local storage
What's Happening Here?
We're developing an application to demonstrate the use of local storage. Our goal is to show how to add, update, and delete data from local storage effectively. To achieve this, I've designed a simple form with three buttons, as shown in demo above. Our application manages a list of programming languages, including fields such as Name
, Creator
, and Year
.
Here's how it works:
- Adding Data: Users fill out the form with the necessary details and click the Save button to add the entry to local storage.
- Retrieving Records: To fetch records, users provide a key, which is typically the name of the programming language. They then click the Load button to retrieve the corresponding data.
- Deleting Entries: To delete an entry, users simply click the Delete button. Local storage removes the record associated with the provided key.
- Updating Entries: Users can update existing entries by giving the name of the language, later they can edit the Creator or Year fields. If the language name is already present, local storage updates the existing entry, Otherwise, it creates a new entry.
LocalStorage Service
Let's begin by creating a service to encapsulate local storage functionality. The LocalStorageService
class facilitates interaction with the browser's local storage through JavaScript.
using Microsoft.JSInterop;
namespace BlazingLocalStorage.LocalStorage
{
public class LocalStorageService
{
private readonly IJSRuntime _jsRuntime;
public LocalStorageService(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task AddItem(string key, string value)
{
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, value);
}
public async Task RemoveItem(string key)
{
await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
}
public async Task<string> GetItem(string key)
{
return await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
}
}
}
Listing 1: LocalStorageSerive.cs
Explanation of listing 1:
- Constructor: Initializes the
LocalStorageService
class with an instance of IJSRuntime
. - AddItem Method: Adds an item to local storage using JavaScript's
localStorage.setItem(key, value)
function. - RemoveItem Method: Removes an item from local storage using JavaScript's
localStorage.removeItem(key)
function. - GetItem Method: Retrieves an item from local storage using JavaScript's
localStorage.getItem(key)
function.
In order to represent our form fields, let's create a model called ProgrammingLanguage
:
public class ProgrammingLanguage
{
public string Name { get; set; }
public string Creator { get; set; }
public int Year { get; set; }
}
Listing 2: ProgrammingLanguage.cs
ProgrammingLanguage Service
The ProgrammingLanguageService
acts as a mediator between the UI and the local storage service. It manages programming languages stored in local storage and provides methods for adding, retrieving, and deleting language entries.
using System.Text.Json;
namespace BlazingLocalStorage.LocalStorage
{
public class ProgrammingLanguageService
{
private readonly LocalStorageService _localStorageService;
public ProgrammingLanguageService(LocalStorageService localStorageService)
{
_localStorageService = localStorageService;
}
public async Task AddOrUpdateLanguageAsync
(string languageName, ProgrammingLanguage languageToAdd)
{
var jsonLanguage = JsonSerializer.Serialize(languageToAdd);
await _localStorageService.AddItem(languageName, jsonLanguage);
}
public async Task DeleteLanguageAsync(string languageName)
{
await _localStorageService.RemoveItem(languageName);
}
public async Task<ProgrammingLanguage> GetLanguageAsync(string languageName)
{
ProgrammingLanguage language = null; ;
var jsonLanguage = await _localStorageService.GetItem(languageName);
if (jsonLanguage != null)
{
language = JsonSerializer.Deserialize<ProgrammingLanguage>(jsonLanguage);
}
return language;
}
}
}
Listing 3: ProgrammingLanguageService.cs
- AddOrUpdateLanguageAsync Method: Adds or updates a programming language entry in local storage. It calls the
AddItem
method of LocalStorageService
to add or update a programming language in the local storage. It has to serialize the languageToAdd
object into JSON before sending it as parameter to AddItem
method. - DeleteLanguageAsync Method: It calls the
RemoveItem
method of LocalStorageService which removes a programming language from the local storage. - GetLanguageAsync Method: It calls the
GetItem
method of LocalStorageService
which retrieves a programming language from the local storage based on the provided languageName
. Then we need to deserializes the JSON into a ProgrammingLanguage
object.
The Component
The UI component, LanguageManagement.razor
, enables users to add, edit, and remove programming language entries. It interacts with the ProgrammingLanguageService
to perform these operations seamlessly.
@page "/"
@using LocalStorage
@inject ProgrammingLanguageService LanguageService
<div class="row">
<div class="col-md-4">
<h3>Add, Edit or Remove Language</h3>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control"
@bind="Language.Name" id="name" />
</div>
<div class="form-group">
<label for="creator">Creator:</label>
<input type="text" class="form-control"
@bind="Language.Creator" id="creator" />
</div>
<div class="form-group">
<label for="year">Year:</label>
<input type="text" class="form-control"
@bind="Language.Year" id="year" />
</div>
<div class="form-group mt-3">
<button class="btn btn-primary" @onclick="SaveLanguage">Save</button>
<button class="btn btn-danger" @onclick="DeleteLanguage">Delete</button>
<button class="btn btn-secondary" @onclick="LoadLanguage">Load</button>
</div>
</div>
</div>
@code {
private ProgrammingLanguage Language = new ProgrammingLanguage();
private async void SaveLanguage()
{
await LanguageService.AddOrUpdateLanguageAsync
(Language.Name, new ProgrammingLanguage()
{
Name = Language.Name,
Creator = Language.Creator,
Year = Language.Year
});
}
private async void DeleteLanguage()
{
await LanguageService.DeleteLanguageAsync(Language.Name);
Language.Name = Language.Creator = string.Empty;
Language.Year = 0;
}
private async void LoadLanguage()
{
Language = await LanguageService.GetLanguageAsync(Language.Name);
StateHasChanged();
}
}
Listing 4: LanguageManagement.razor
- Component Routing and Injection: The first three lines of code specify the component's route and the namespace where the
ProgrammingLanguageService
is located. Additionally, you need to inject the ProgrammingLanguageService
into the component. - HTML Markup: The HTML markup defines a form with input fields for entering the name, creator, and year of a programming language. It also includes buttons for saving, deleting, and loading languages.
- @code Block:
- The
Language
object holds the data of the programming language being manipulated in the form. - The
SaveLanguage
method is invoked when the user clicks the Save button. It calls the AddOrUpdateLanguageAsync
method of the ProgrammingLanguageService
to add or update the language in the local storage. - The
DeleteLanguage
method is invoked when the user clicks the Delete button. It calls the DeleteLanguageAsync
method of the ProgrammingLanguageService
to remove the language from the local storage. - The
LoadLanguage
method is invoked when the user clicks the Load button. It calls the GetLanguageAsync
method of the ProgrammingLanguageService
to retrieve the language from the local storage and populates the form fields with its data.
The UI looks like image 1 shown below:
Image 1: User clicks on Save button to add data to the local storage
Conclusion
Local storage operates entirely on the client side, reducing the need for frequent server calls to fetch or update data. This makes it an ideal choice for storing user preferences, application settings, theme preferences, language preferences, and display options that need to be accessed across sessions.
Now that we understand what to store in local storage, it's important to be aware of what not to store. Local storage is not a secure storage solution for sensitive or confidential information, such as passwords, credit card details, or personally information.
By leveraging local storage in Blazor applications, you can enhance user experiences. With proper usage, local storage can significantly improve the responsiveness of web applications.
History
- 5th February, 2024: Initial version