DEV Community

Cover image for Blazor WASM with REST API - Step by Step
Mohamad Lawand
Mohamad Lawand

Posted on • Edited on

Blazor WASM with REST API - Step by Step

In this post i will be showing you today How to add create a blazor WASM app and connect it to our todoAPI which have created previously and load data dynamically from the API.

Some of the topics we will cover are creating Blazor WASM App, creating component, adding navigation, connecting to the API and doing calls to the API

You can watch the entire step by step video on YouTube:

As well download all of the source code:
https://github.com/mohamadlawand087/v9-BlazorWASMAppWithREST

This is part 1 of the Blazor Journey:
Part 2: https://dev.to/moe23/blazor-wasm-crud-with-rest-api-step-by-step-2ja

Before we start building our application we need to have 2 thinks:

Will start by defining what is a Client side blazor WASM: a Blazor WASM is the application is running as Web Assembly directly in the browser. the application is not running on the server, the application is compiled and then its downloaded to the browser as a standalone app. This app will then connect to the webAPI to fetch and manipulate data.

Now lets create our first blazor application and its supporting lib

dotnet new blazorwasm -n "TodoClient"
Enter fullscreen mode Exit fullscreen mode

A blazor project is consisted of C# and razor files and from the structure we can see its very similar to an Asp.Net core application

Razor files are composed of UI elements called components, component is build in C#

When a component start with

@page "/counter" 
Enter fullscreen mode Exit fullscreen mode

means we can navigate to this component and a component contain html and C# code, the C# code for a component lives inside the @code section

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now let's run the application to see how it works and how it looks with the boilerplate code that Microsoft has provided us with.

dotnet run
Enter fullscreen mode Exit fullscreen mode

Once we check it lets go back to our application and lets check the structure of our application, lets navigate the wwwroot folder and open the index.html page. this page is the entry point for our application. We can see that its a simple html page which contain references to javascript and css as well it contain an app tag

<app>Loading ...<app>
Enter fullscreen mode Exit fullscreen mode

This app tag will specify where our blazor application will be rendered, any blazor application will contain a root component which is called app.razor

in the end of the page we can see a reference to

<script src="_framework/blazor.webassembly.js"></script>
Enter fullscreen mode Exit fullscreen mode

This is a very important file as it will responsible to download the runtime for blazor as well the compiled blazor application. Blazor apps are compiled and downloaded as a dll to the browser.

Asp.Net Core and Blazor app shares alot of components, we can see from the files structure that we have a program.cs file and inside of it we have

builder.RootComponents.Add<App>("#app");
Enter fullscreen mode Exit fullscreen mode

In the code we can see how we are specifying our root component and how are assigning a tag for it which we can use across the app. the app tag is the same one that exist in the index.html that we have discussed earlier. As well we can see above the program.cs we have App.razor which is the component which is being referenced here.

Similarly to Asp.Net core blazor also contains a built in dependency injection

Lets open our App component, we can see that we have a router component, which is going to be responsible on all of the routing from within our application. So how does this work, well it actually very simple. When the application start the assembly is going to be scanned for all of the components that exist and then a reference to them will be stored in a list of pages, so when we navigate to a component the main component will match route with the list of scanned component and then redirects to it. it will also pass extra parameters and define the default layout

We can also define a main layout which be handling all of the layout in out application, the main layout is also a component inside the shared folder.

MainLayout is defining the structure of any page inside our application its like the master template that is being used, we can have multiple ones.

One final thought before we start our development adventures, as you saw in our razor pages we have @code section which contains the c# code which will manage the razor page, this is okay for small applications but for large applications and for scalability its better to have the C# code in a partial class with a code behind approach to make the application easier to manage.

After covering most of the basics of an Blazor App, lets do some clean up in our application.

From inside the pages folder we will remove the counter and the FetchData from the project

Inside our models folder we need to create our own new class will call it ItemData

public class ItemData
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public bool Done { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

The first component we are going to be creating is a item overview, we will be creating it under the pages folder. Why are we going to create it under page since its a component that i would be able to navigate to. Under the pages folder we need to create 2 files

  • ItemOverview.razor
  • ItemOverview.cs ⇒ this is the code behind for our razor file

we need to convert ItemOverview.cs to a partial class using the below code.

public partial class ItemOverview
{

}
Enter fullscreen mode Exit fullscreen mode

The reason we made it a partial class as the razor file will be converted to a class once compiled so it will create an issue, having it as a partial will merge the 2 classes.

In order to navigate to the ItemOverview component we need to add the below on the ItemOverview.razor. the page directive will allow me to specify the url that i want to use to navigate to

@page "/itemoverview"
Enter fullscreen mode Exit fullscreen mode

For now we will need to add some static data to our partial class, later on this will be done by calling an API

public partial class ItemOverview
{
    public IEnumerable<ItemData> Items { get; set; }

    private void InitializeItems()
    {
        var e1 = new ItemData
        {
            Description = "Get bread, fruit, veg and cheese",
            Done = false,
            Id = 1,
            Title = "Food shopping"
        };

        var e2 = new ItemData
        {
            Description = "Study well for AZ204",
            Done = false,
            Id = 2,
            Title = "Study"
        };

        Items = new List<ItemData> { e1, e2 };
    }
}
Enter fullscreen mode Exit fullscreen mode

Now the next steps is to visualise our items inside our component, in order for us to do that we need to be aware of the component life cycle. there would be a number of methods that is going to be called as part of the the initialisation process of the component On of the most important methods is "OnInitialzedAsync" which we will override inside our ItemOverview partial class

protected override Task OnInitializedAsync()
{
    InitializeItems();
    return base.OnInitializedAsync();
}
Enter fullscreen mode Exit fullscreen mode

the constructor is not the best place in razor to do so. now lets start building our Blazor view

inside our ItemOverview.razor lets add the below code.

@page "/itemoverview"

<h1 class="page-title">All items</h1>

<!-- the check here rely on the 2 way binding which will automatically detecht 
        when the data has been returned by our class, in our case its static data
        but when we want to connect to our api to fetch tdata it will be useful -->
@if(Items == null)
{
    <p>Loading ...</p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <td>Id</td>
                <td>Title</td>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Items)
            {
                <tr>
                    <td>@item.Id</td>
                    <td>@item.Title</td>
                </tr>
            }
        </tbody>
    </table>
}
Enter fullscreen mode Exit fullscreen mode

Now that we have created our first component its time to jump at the second one. Our second component will be called ItemDetail which will allow us to see the item detail information in our pages folder lets create a new razor component called ItemDetail.razor and a partial class ItemDetail.cs

public partial class ItemDetail
{
    // in order for us to capture the specific id that we need to show its item
    // we are sending an Id and in order for blazor to recognise this we need to have
    //  a variable with the same name of the queryString and with the Parameter attribute
    [Parameter]
    public string Id { get; set; }

    // The item detail is bound to an item that we want to show
    public ItemData Item {get;set;} = new ItemData();

    public IEnumerable<ItemData> Items { get; set; }

    private void InitializeItems()
    {
        var e1 = new ItemData
        {
            Description = "Get bread, fruit, veg and cheese",
            Done = false,
            Id = 1,
            Title = "Food shopping"
        };

        var e2 = new ItemData
        {
            Description = "Study well for AZ204",
            Done = false,
            Id = 2,
            Title = "Study"
        };

        Items = new List<ItemData> { e1, e2 };
    }

    // This is the function that w will use to call functions that will be initializing data
    protected override Task OnInitializedAsync()
    {
        InitializeItems();

        Item = Items.FirstOrDefault(x => x.Id == Convert.ToInt32(Id) );

        return base.OnInitializedAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode
@page "/itemdetail/{Id}"

<h1>Item detail @Item.Title</h1>

<div class="row">
    <div class="col-md-12">
        <div class="form-group row">
            <label class="col-sm-4 col-form-label">Id</label>
            <div class="col-sm-8">
                <label type="text" readonly class="form-control-plaintext">@Item.Id</label>
            </div>
        </div>
        <div class="form-group row">
            <label class="col-sm-4 col-form-label">Title</label>
            <div class="col-sm-8">
                <label type="text" readonly class="form-control-plaintext">@Item.Title</label>
            </div>
        </div>
        <div class="form-group row">
            <label class="col-sm-4 col-form-label">Description</label>
            <div class="col-sm-8">
                <label type="text" readonly class="form-control-plaintext">@Item.Description</label>
            </div>
        </div>
        <div class="form-group row">
            <label class="col-sm-4 col-form-label">Is Done</label>
            <div class="col-sm-8">
                <label type="text" readonly class="form-control-plaintext">@Item.Done</label>
            </div>
        </div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Now we need to update the Index so we can navigate to this page so lets open itemOverview.razor and update the code

@page "/itemoverview"

<h1 class="page-title">All items</h1>

<!-- the check here rely on the 2 way binding which will automatically detecht 
        when the data has been returned by our class, in our case its static data
        but when we want to connect to our api to fetch tdata it will be useful -->
@if(Items == null)
{
    <p>Loading ...</p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <td>Id</td>
                <td>Title</td>
                <td></td>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Items)
            {
                <tr>
                    <td>@item.Id</td>
                    <td>@item.Title</td>
                    <td>
                        <a href="@($"itemdetail/{item.Id}")" class="btn btn-primary">
                            detail
                        </a>
                    </td>
                </tr>
            }
        </tbody>
    </table>
}
Enter fullscreen mode Exit fullscreen mode

Our next step is to update the menu, to do that we need to navigate to Shared → NavMenu.razor and edit it as the below

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">TodoManagement</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="ItemOverview">
                <span class="oi oi-plus" aria-hidden="true"></span> Items
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}
Enter fullscreen mode Exit fullscreen mode

just for reference NavLink is also a component provided for us by Microsoft for routing purposes

Now that we have our application ready but with static data, the next step will be to make it dynamic and connect our blazor app to our API

We will be adding dynamic data to replace the static data that we hardcoded into our classes.

You can get the latest version of our API app from git hub, links in the description

Will start first by modifying the ports for our API so we can connect to it from our blazor app.

We open the application and navigate to the Program.cs and add the following line

webBuilder.UseUrls("http://*:8090");
Enter fullscreen mode Exit fullscreen mode

Then for the testing purposes we are going to remove the authentication requirement on our TodoController by commenting out the line

//[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Enter fullscreen mode Exit fullscreen mode

We will have another article after this to add authentication to our application. The change we will do to our API is enable CORS so we can connect to it from another application. Inside our startup class we need to add the code below in the ConfigureServicesMethod:

services.AddCors(options =>
{
    options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader());
});
Enter fullscreen mode Exit fullscreen mode

And in the Configure method we need to add the below before app.UseEndpoints

app.UseCors("Open");
Enter fullscreen mode Exit fullscreen mode

We will be using IHttpClientFactory to connect to our API, Microsoft has provided it for us out of the box, The HttpClient will be available for us through dependency injection.

To utilise the HttpClientFactory we need to add the nuget package for it

dotnet add package Microsoft.Extensions.Http 
Enter fullscreen mode Exit fullscreen mode

Once the nuget is added we need to do is to create a new folder called services in the root directory, this services folder will contain our HttpClientService

We need first to create our interface ITodoDataService

public interface ITodoDataService
{
     Task<IEnumerable<ItemData>> GetAllItems();
     Task<ItemData> GetItemDetails(int id);
}
Enter fullscreen mode Exit fullscreen mode

then the TodoDataService

public class TodoDataService : ITodoDataService
{
    public async Task<IEnumerable<ItemData>> GetAllItems()
    {
        throw new System.NotImplementedException();
    }

    public async Task<ItemData> GetItemDetails(int id)
    {
       throw new System.NotImplementedException();
    }
}
Enter fullscreen mode Exit fullscreen mode

After that we need to update the Program.cs to inject the httpclient to our services so we can utilise them directly from service

builder.Services.AddHttpClient<ITodoDataService, TodoDataService>(x  => x.BaseAddress = new Uri("http://localhost:8090"));
Enter fullscreen mode Exit fullscreen mode

Lets now implement the TodoDataService and utilise our API

public class TodoDataService : ITodoDataService
{
      // Using the httpclient to call our API, 
    // its being initialized via the constructor injection
    private readonly HttpClient _httpClient;

    public TodoDataService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<IEnumerable<ItemData>> GetAllItems()
    {
        var apiResponse = await _httpClient.GetStreamAsync($"api/todo");
        return await JsonSerializer.DeserializeAsync<IEnumerable<ItemData>>
                            (apiResponse, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
    }

    public async Task<ItemData> GetItemDetails(int id)
    {
        var apiResponse = await _httpClient.GetStreamAsync($"api/todo/{id}");
        return await JsonSerializer.DeserializeAsync<ItemData>
                            (apiResponse, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
    }
}
Enter fullscreen mode Exit fullscreen mode

The next step is we need to update our ItemDetail and ItemOverview and remove all of the static data and utilise the API

Lets open the ItemOverview partial class and update to the below

public partial class ItemOverview
{
    public IEnumerable<ItemData> Items { get; set; }

        // Dynamicly inject from our DI container
    [Inject]
    public ITodoDataService TodoDataService {get;set;}

    // This is the function that w will use to call functions that will be initializing data
    protected async override Task OnInitializedAsync()
    {
                // Call our data service which call the API
        Items = (await TodoDataService.GetAllItems()).ToList();
    }
}
Enter fullscreen mode Exit fullscreen mode

Now lets open the ItemDetail partial class and update it

public partial class ItemDetail
{
    // in order for us to capture the specific id that we need to show its item
    // we are sending an Id and in order for blazor to recognise this we need to have
    //  a variable with the same name of the queryString and with the Parameter attribute
    [Parameter]
    public string Id { get; set; }

    // The item detail is bound to an item that we want to show
    public ItemData Item {get;set;} = new ItemData();

    // Dynamicly inject from our DI container
    [Inject]
    public ITodoDataService TodoDataService {get;set;}

    // This is the function that w will use to call functions that will be initializing data
    protected async override Task OnInitializedAsync()
    {
        Item = await TodoDataService.GetItemDetails(Convert.ToInt32(Id)); 
    }
}
Enter fullscreen mode Exit fullscreen mode

Lets now run both the Blazor App and the API at the same time and test our application.

As we can see the application is now connected to the API and the data is being pulled dynamically.

In the next article we will be covering how to add/edit/delete items from our blazor App

Top comments (0)