DEV Community

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

Posted on

Blazor WASM - CRUD with Rest API - Step by Step

In this post I will be showing you today How to Add, Edit, Delete and Navigate within our Blazor App.

Some of the topics we will cover are New endpoints functionalities, Navigation Manager and 2 way binding.

Source code: https://github.com/mohamadlawand087/v10-BlazorWASMCRUDwitRest

This is part 2 of our Blazor App journey, and we will be basing our current work on our previous Blazor WASM application that we have created in our last video and our Todo API.

Part 1: https://dev.to/moe23/blazor-wasm-with-rest-api-step-by-step-2djo

API Source code: https://github.com/mohamadlawand087/v8-refreshtokenswithJWT

API Video: https://youtu.be/T_Hla1WzaZQ

Now that we have completed the first part of retrieving data from the API the next step is to be able to add, edit and delete data.

Blazor comes with the support of Data Binding, making it easy to retrieve and set values. Blazor has 3 types of bindings:

  • One-Way: read only, showing the information in a form. ex: showing data in label
  • Two-Way: will allow a bidirectional flow of data like having an Input field
  • Component Parameter

Blazor comes with ready made input components that we can use in a form, these will help us in creating our forms. The form itself will be an edit form which will support validation.

Razor also has some input component that we can use out of the box, these component are wrappers to the normal html controls that we have

  • InputText
  • InputTextArea
  • InputNumber
  • InputSelect
  • InputDate
  • InputCheckbox

The first thing we will do is to make sure our API is running in the background, you get the code from github, links will be available in the description down below. Lets open our API and run it

dotnet build
dotnet run
Enter fullscreen mode Exit fullscreen mode

The next step for us is to have the ability to add new Todo item, for that we will need a new component. Inside our pages folder lets create a new file called ItemEdit.razor and ItemEdit.cs

Lets update our ItemEdit.cs as follow. This new component will be used to Add, Edit and Delete Items for that we will need an Id for the parameter to be able to edit

public partial class ItemEdit
{
    [Inject]
    public ITodoDataService TodoDataService {get;set;}

    // When i am editing the item information i need a parameter to get the specific item
    [Parameter]
    public string Id {get;set;}

    // I will always need an ItemData to bind data against it 
    public ItemData Item {get;set;} = new ItemData();

        protected async override Task OnInitializedAsync()
    {
        var itemId = Convert.ToInt32(Id);
        Item = await TodoDataService.GetItemDetails(itemId);
    }
}
Enter fullscreen mode Exit fullscreen mode

Since there is no optional parameters yet in Blazor we need to add 2 page declarative in our razor view to cover the 2 scenarios that we have Add/Edit TodoItem.

@page "/itemEdit"
@page "/itemEdit/{Id}"

<section>
    <h1>Details for @Item.Title</h1>
    <EditForm Model="@Item">
        <div class="form-group">
            <label class="col-md-3">Title</label>
            <!-- To create a 2 way binding on our property we need use @bind-value="@Item.Title" -->
            <!-- The first binding happens when the component is loaded and second binding when the user edit the input and click away -->
            <InputText class="col-md-8" @bind-Value="@Item.Title" id="Title"></InputText>
        </div>
        <div class="form-group">
            <label class="col-md-3">Description</label>
            <InputTextArea class="col-md-8" @bind-Value="@Item.Description" id="Desciption"></InputTextArea>
        </div>
        <div class="form-group">
            <label class="col-md-3">Done</label>
            <InputCheckbox class="col-md-8" @bind-Value="@Item.Done" id="Done"></InputCheckbox>
        </div>
    </EditForm>
</section>
Enter fullscreen mode Exit fullscreen mode

Now lets update our ItemOverview by adding a link to the edit page, inside the ItemOverview.razor lets add the code below

<a href="@($"itemedit/{item.Id}")" class="btn btn-warning">
  edit
</a>
Enter fullscreen mode Exit fullscreen mode

lets build our application and check if everything is running

dotnet build
dotnet run
Enter fullscreen mode Exit fullscreen mode

The next step is to update the OnInitializedAsync method to detect if the form is going to be used as add or edit we do that by checking the Id if its empty or not. We also need to add state management to control the flow on the user side

// State Management
protected string Message = string.Empty;
protected bool Saved;

protected async override Task OnInitializedAsync()
{
        Saved = false;

    if(!String.IsNullOrEmpty(Id))
    {
        var itemId = Convert.ToInt32(Id);
        Item = await TodoDataService.GetItemDetails(itemId);
    }
}

protected async Task HandleValidSubmit()
{
    if(String.IsNullOrEmpty(Id)) // add new item
    {
        var res = await TodoDataService.AddItem(Item);
        if(res != null) {
            Saved = true;
            Message = "Item has been added";
        } else
        {
            Message = "Something went wrong";
        }
    }
    else // update item
    {
        await TodoDataService.UpdateItem(Item);
        Saved = true;
        Message = "Item has been updated";
    }
}

protected void HandleInvalidSubmit()
{
    Message = "There is issues with validation.";
}
Enter fullscreen mode Exit fullscreen mode

Now that we have our form working and we are able see and navigate to our data page. Its time for us to add the save functionality as well the validation

For the validation Blazor provide us with out of the box validation similar to ASP.Net core, all we need to do is add attributes to the model class and utilise the EditForm in our razor view. lets update our model

public class ItemData
{
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }

    [Required]
    public string Description { get; set; }

    public bool Done { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Lets update our ItemEdit.razor by adding the data annotation for validation, we add this attribute inside our EditFrom

<DataAnnotationsValidator />
<ValidationSummary></ValidationSummary>
Enter fullscreen mode Exit fullscreen mode

Will start by updating our TodoDataService by adding the methods that will be responsible of adding and updating a product. Lets navigate to Service ⇒ ITodoDataService and add these 2 methods

Task<ItemData> AddItem(ItemData item);
Task UpdateItem(ItemData item);
Task DeleteItem(int id);
Enter fullscreen mode Exit fullscreen mode

Next we need to update the TodoDataService

public async Task<ItemData> AddItem(ItemData item)
{
    var itemJson = new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json");

    var response = await _httpClient.PostAsync("api/todo", itemJson);

    if(response.IsSuccessStatusCode)
    {
        var responseBody = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<ItemData>
            (responseBody, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
    }

    return null;
}

public async Task DeleteItem(int id)
{
    await _httpClient.DeleteAsync($"api/todo/{id}");
}

public async Task UpdateItem(ItemData item)
{
    try
    {
        var jsonString = JsonSerializer.Serialize(item);
        var itemJson = new StringContent(jsonString, Encoding.UTF8, "application/json");
        var url = $"api/todo/{@item.Id}";
        var response = await _httpClient.PutAsync(url, itemJson);

        if(response.IsSuccessStatusCode)
        {
            var responseBody = await response.Content.ReadAsStreamAsync();
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
Enter fullscreen mode Exit fullscreen mode

then we need to do is to update the form in the ItemEdit.Razor, we start by assigning methods to our form

<EditForm Model="@Item" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
Enter fullscreen mode Exit fullscreen mode

we need to add a submit button and a message result

<div class="form-group">
    <button type="submit" class="btn btn-warning"> Save Item</button>
</div>

<label>@Message</label>
Enter fullscreen mode Exit fullscreen mode

Now we need to add the add new item link in the item over view page

<div class="row">
    <div class="col-md-12">
        <a href="/itemedit" class="btn btn-success">
            Create New Item
        </a>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Note: in case you are getting cors issues while doing a put update your api cors config to the following:

options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
Enter fullscreen mode Exit fullscreen mode

Lets now add a button that will allow us to return to the itemsOverview page from the edit page, inside the itemEdit page we need to add the following

<a @onclick="@goToOverview" class="btn">Back</a>
Enter fullscreen mode Exit fullscreen mode

Inside the itemEdit.cs we need to add the method goToOverview as well add the navigationManager, it is provided for us by default from the .Net framework

[Inject]
public NavigationManager navigationManager {get;set;}

protected void goToOverview()
        {
            navigationManager.NavigateTo("/ItemOverview");
        }
Enter fullscreen mode Exit fullscreen mode

The final piece of the puzzel is adding the delete functionality to our application, to do that we need to update the itemEdit.razor and add a link to the delete method

@if(!String.IsNullOrEmpty(Id))
            {
                 <a @onclick="@DeleteItem" class="btn btn-danger">
                        delete
                        </a>
            }
Enter fullscreen mode Exit fullscreen mode

now we need to update itemEdit.cs

protected async Task DeleteItem()
        {
            if(!String.IsNullOrEmpty(Id))
            {
                var itemId = Convert.ToInt32(Id);
                await TodoDataService.DeleteItem(itemId);

                navigationManager.NavigateTo("/ItemOverview");
            }

            Message = "There is issues with the delete function.";
        }
Enter fullscreen mode Exit fullscreen mode

Finally we need to make sure everything still builds and run

dotnet build
dotnet run
Enter fullscreen mode Exit fullscreen mode

In our next part we will explore authentication and authorisation in Blazor.

Latest comments (0)