Creating a Blazor WebAssembly Application — Part 3

Henrique Siebert Domareski
13 min readNov 3, 2022

--

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:

CategoryList.razor

The Component to Add or Edit a category:

CategoryEdit.razor
CategoryEdit.razor

The component to list the books:

BookList.razor

The component to Add/Edit a book:

BookEdit.razor
BookEdit.razor

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, Categoriesand 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 Homepage:

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 method AddBookPage, 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 this input, 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 the SearchTerm variable with the value that is written in this field. And there is another directive @onkeyup, and this is used to call the method SearchBook 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 the Books 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 method DeleteBook, 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 the BookService 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 the Books variable, and to the CompleteBooks 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 the CompleteListBooks 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 the Book (from the Model we created before in the previous article). There is also the OnValidSubmit, which calls the method HandleValidSubmit 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 using DataAnnotations.
  • On line 15, there is also something that was not shown yet, which is the ValidationMessage. and for this line, it’s being assigned the Book.Name, which means that it will read the validation for the property Name 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 the BookEdit.razor, in the @page directive which receives the BookId).

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

--

--

No responses yet