Creating a Blazor WebAssembly Application — Part 3
Blazor WebAssembly is a great way to create Single Page Application (SPA) apps using C# language. In this part 3 of the series “Creating a Blazor WebAssembly Application”, I present how to create the Razor components
(also known as Blazor components
) for Categories and Books. If you want to check the previous articles, you can read part 1 by clicking here, and part 2 by clicking here.
Components Overview
As mentioned in the first article of this series, Blazor apps are based on components, similar to other SPAs. A component in Blazor is an element of UI (such as a page, a dialog, a button, etc), which can be used once or multiple times in the app. A component has the .razor
extension in its file name, and it’s built with C# code, HTML, and contains the UI and the logic of this UI.
There are four components that we will need for this project:
- A component to add/edit a category
- A component to show a list of book categories
- A component to show a list of books
- A component to add/edit a book
At the end of the implementation, that’s how the components should look like:
The component to list the categories:
The Component to Add or Edit a category:
The component to list the books:
The component to Add/Edit a book:
Install Blazored Toast
Before starting with the components implementation, let’s add a package that will be used to show messages of success and error in the app. In order to do that, we can create our own message component, or we can make use of some existing package for that. For this project, let’s add the “Blazored Toast” package, which can be installed in the project via NuGet package (search by Blazored.Toast
):
The complete documentation related to how to configure (personalization) and how to use the component can be found on this GitHub repository.
With this component (after being configured in the project), we can make use of Toast messages like this:
Adding references in Index.html
Let’s add the following references in the index.html
file:
- First, update the app’s title in the
head
(line 5) - Add the CSS configuration for
bootstrap
(line 8) - Add the configuration for
font-awesome
(line 9) - Add the CSS configuration for
Blazored Toast
(line 10)
[Extra]: Alternatively, instead of using bootstrap from a CDN, you can also install bootstrap directly in your project. If you want to do that, right-click in the project > select the option Add > select “Client-side library” > in the “Provider” field select cdnjs
, and in the “Library” search for bootstrap
and select the version you want and click on Install. This will add the bootstrap files to a new lib
folder inside wwwroot
, so just remember to update the configuration in the index.html
file.
In this same file, on line 24, there is a reference to the blazor.webassembly.js
file:
<script src="_framework/blazor.webassembly.js"></script>
This is the JavaScript file that loads the Blazor application and it is already included in a Blazor project. We don’t need to configure anything, this is automatically configured by Microsoft.
MainLayout.razor
The MainLayout.razor
component, is the default layout when creating a Blazor project using the template. Let’s then update the CSS
for changing the color of our app. You can click on the left arrow and you will see the CSS
class for this component. In this CSS
class I changed the background color to light blue:
Still in the MainLayout.razor
, let’s remove the nav bar at the top of our page, and our razor page should be like this:
- On lines 5 to 10, the
BlazorToasts
was added, with the configuration related to where the notification will be displayed, the time the notification will be shown, and which icons will be used. - On line 13 there is the
NavMenu
component, which is responsible for the menu of the app, which we are going to update next. - On line 18, there is the Blazor syntax
@Body
, and this is used to specify the location in the layout markup where the content is rendered, so the content of the pages will be added inside this section.
NavMenu.razor
In the NavMenu.razor
, let’s create the menu with some NavLink
, which will generate a button in the UI that will redirect to the components that we are going to have in this app, which are: Home
, Categories
and Books
. We don’t have those components yet but don’t worry about that for now, let’s first create the initial structure and later we are going to implement those components. This is the NavMenu.razor with the changes:
On line 4 there is an @onclick
event that calls the method ToggleNavMenu
, this method will be executed every time the user clicks on the button, and it is responsible to collapse the nav menu depending on the resolution of the screen.
This is the new menu now:
In the Index.razor
file (on the Pages
folder), let’s do some changes for the Home
page:
On line 1 we have the @page
directive, as this is the Home page, we only need to use @page "/"
and this means that once the user doesn’t type any specific rout in the URL, it will be redirected to Home.
On line 6, we have a NavLink
which refers to books
. Since these pages don’t exist yet, in case you run the app and click on some of these buttons, you should see a message saying “Sorry, there’s nothing at this address”.
If you run the application, this is how the UI looks like:
And it is also mobile responsiveness:
Creating the Components
We need to create four components: a component for the list of categories (CategoryList.razor
), a component for add/edit a category (CategoryEdit`.razor
), a component for the list of books (BookList.razor
) and a component for add/edit a book (BookEdit.razor
). For demonstration purposes, I’m going to use the two book components as an example, and you can find the complete code in my GitHub by clicking on this link.
When creating components, the name of the component needs to start with an upper case. To create a component, inside the “Pages” folder I create a new folder named “Categories”, where all components related to Category will be added, and a folder named “Books”, where we will create the components related to Book. To create a component, right-click on the folder you want to create> click in Add > and click on “Razor Component…”:
Let’s start implementing the BookList.razor
component. This is the final result of this component:
At the beginning of the component, we need to add the @page
directive with the name of the page we want this component to have, which in this case is /books
. We also need to add the usings
that we need, which is the Interfaces
and Models
. For confirmation dialog, let’s use the IJSRuntime
(which represents an instance of a JavaScript runtime to which calls may be dispatched). And let’s also include the IToastService
, for the notifications:
With IJSRuntime
, we can have a confirmation dialog like this (is not the most beautiful dialog I must say, but it’s helpful and easy to use without creating a component for that):
Now let’s implement the HTML
for this component. As demonstrated before, we need a title, a button to add a new book, a search field and a grid to display books. This is the initial part of the Html
for this component:
- On line 2, there is the title of the page
- On line 6, there is a button with the directive
@onclick
, which will call the methodAddBookPage
, to redirect the user to a new page - On line 15, there is an icon for the search field
- On lines 18 and 19, there is the
input
for the search field. For thisinput
, we have the directive@bind-value
(or only@bind
), which handles the value that the user types in this field. There is also the directive@bind-value:event
, and this is used in order to update theSearchTerm
variable with the value that is written in this field. And there is another directive@onkeyup
, and this is used to call the methodSearchBook
every time the user types something in this field.
The second part of the HTML
, we have the grid of books:
- On line 1, we first check if the variable
Books
has value, if has, means that the data were already received from the API, otherwise it will display a message saying it is loading (line 3). - On line 7 up to 40, there is the table with the columns.
- On line 20, there is a
foreach
, which will read theBooks
variable and create the lines in the grid. - On line 30, there is a
href
, which will redirect the user to the page to edit the book. - On line 33, there is the
@onclick
directive, which calls the methodDeleteBook
, and receives the own book as a parameter. This method will open a dialog asking for a confirmation if the user wants to delete the book.
To implement the component’s code, there are two ways to add logic to the component, you can create the C# code inside the @code
directive in your Razor
component, or you can have a separate file for the code. I will demonstrate how to do it for both cases. For this component, let’s use the code inside the same file, to do that, below the HTML
code, add the directive @code
, and add the code inside the braces:
- On line 1, there is the
@code
directive, which means that from this line starts the C# code. - On line 2, there is the
[Inject]
attribute, this is necessary when we want to inject something into the component, which in this case it is theBookService
on line 3. - On line 6, there is the
NavigationManager
, which will be used to redirect the user to another page. - On line 8, there is a list of Books, which will be returned from the API.
- On line 9, there is another variable for a list of books, and this will be used in order to avoid multiple requests to the API when the user search for a book.
- On line 11 there is a property used for binding the searched value that the user types.
Now let’s implement the methods we need:
- The method on line 1, is the
OnInitializedAsync
, this method is executed when the component is initialized. On this method we are making a request to the Service class, to retrieve the list of books, and adding the books into theBooks
variable, and to theCompleteBooks
variable. - On line 7, there is the method
AddBookPage
, which will redirect the user to the page to add a new book.
This is the DeleteBook
method:
- On line 1, there is the
DeleteBook
method, which receives as a parameter the book that will be deleted. - On line 3, we are using a the
JsRunTime
to display a confirmation dialog. - On line 6, the method to delete the book in the Service class is being called, and if the delete succeeds, we make a new request to retrieve the updated list of books (line 19).
- On line 22 we use the toast component to display a message saying that the book was successfully deleted.
- On line 23 there is the
StateHasChanged
method, this is used to notify the component that its state has changed, if we don’t call this method, the list of books that after the delete operation, will not be updated.
And the last method is the SearchBook
. This method will execute an in-memory filter (it will not make new requests to the API):
This method is called when the user types something in the search field.
- On line 3, we check if the value to search is not null, if it is not null, we execute the search filter, and if it is null we return the list with all books.
- On line 5, we convert the searched value to lowercase, to ignore if the value has lower or capital case.
- On line 6, we use
LINQ
to do a search in the list of books, filtering by name, author and category. Note that the filter is done using theCompleteListBooks
variable, and the reason is because we always need to filter using the completed list of books, and not the filtered list.
The next component we need is the BookEdit.razor
component. This component will be used to add a new book and also to edit an existent book. This is how we want to have this component:
For this component, let’s create the C# code in a separate file. So we will need two files, one for the HTML
, which is the BookEdit.razor
, and one file for the C# code, which is the BookEdit.cs
file. Let’s start with the HTML
:
- On lines 1 and 2, there are two
@page
directive. The one on line 1, it’s for when the user wants to add a new book, so there is no id yet. The one on line 2 it’s for when the user wants to edit a book, so it will inform the book and the id. Note that on line 2, we explicitly informed that the value must be an int (...{BookId:int}
), this way if the user types a string for the id of the book in the URL, it will not work. - On line 6, there is the title of the page, which can be “Add a new book”, or “Edit …” (and the name of the book).
- On line 8 there is an
EditForm
, which model is theBook
(from the Model we created before in the previous article). There is also theOnValidSubmit
, which calls the methodHandleValidSubmit
when the form is valid. - On line 9, there is the
DataAnnotationsValidator
. This is necessary to make use of the validations that were added in the Model class, by usingDataAnnotations
. - On line 15, there is also something that was not shown yet, which is the
ValidationMessage
. and for this line, it’s being assigned theBook.Name
, which means that it will read the validation for the propertyName
from the Model class.
The C# code for this component, instead of adding in the same HTML
file, we created a new file for it. This class then needs to be a partial class:
- On line 1, note that it is a partial class. This is how you can separate the C# code from the
HTML
. With a partial class in C#, you can implement the functionality of a single file into multiple files, and when the application compiles, all these files are combined into a single class file. - On line 3, there is the annotation
[Parameter]
, and this is the value we received via URL (check theBookEdit.razor
, in the@page
directive which receives theBookId
).
The first method we have is the OnInitializedAsync
:
- On line 3, it will get the list of categories to display in the dropdown field.
- On line 5, it’s checking if there is some id for the book, in case there is it will get the book searching for the informed id.
- On line 7, the
BookService
class will execute theGetById
method to search for the book. In case the book is found, it will be returned to display in the UI, otherwise will return a message saying that could not load the book.
After a book is added or edited, the user is redirected to the book list page. For that, there is a private method that will execute this action by using the NavigationManager
, which is the NavigateToBooksPage
:
The next method is the HandleValidSubmit
:
This method is executed when the page is submitted (when the user clicks on the Save
button). It will check if the book should be added or updated. If there is an id, means that it is an existent book, so it will update the book, otherwise, it will add a new book.
This is the AddBook
method:
- On line 3, the service method to add a book is called.
- On line 4, the result of the adding operation is checked, if the result is different than null, that is, a book was returned, then a message saying that the book was successfully added is displayed (line 6) and the user is redirected to the book list page (line 7), otherwise an error message is shown (line 11).
This is the UpdateBook
method:
- On line 3, the service method to update a book is called.
- On line 4, the result of the adding operation is checked, if the result is true means that was returned a successful response, then a message saying that the book was successfully updated is displayed (line 6) and the user is redirected to the book list page (line 7), otherwise an error message is shown (line 11).
Conclusion
As presented in this article, with Blazor we can create components using HTML
and C#
and Razor
, and we can also make use of JavaScript
functionalities in our components. Now we have a functional Single Page Application (SPA) made with Blazor WebAssembly
and .NET 6
.
This is the link for the project in GitHub:
https://github.com/henriquesd/BlazorSPABookStore
If you like this project, I kindly ask you to give a ⭐️ in the repository.
Thanks for reading!