Mozart Al Khateeb

Full Stack

Mobile

.Net Developer

ERP Developer

Mozart Al Khateeb

Full Stack

Mobile

.Net Developer

ERP Developer

Blog Post

To Do Rest API Using Asp.net Core – Blazor Client (Part 3)

To Do Rest API Using Asp.net Core – Blazor Client (Part 3)

In previous posts we created a simple but well documented Asp.Net Core Web API and I promised that we are gonna test out this API using several client side technologies, I have chosen to start with Blazor so we can explore this new framework.

Blazor is new framework developed by Microsoft to create interactive client side web UI with .Net.

Tools

You could make use of visual studio 2019 preview to create a new project using the template but Blazor is still new and I have found some issues installing the Blazor Extension but you can give it a try.

Getting Started

Blazor offers two hosting templates, client and server side. For information on the two Blazor hosting models, server-side and client-side, see Blazor hosting models.

In this post I will start with client side hosting model, maybe in future posts we can develop another app using the server side template.

Creating a new blazor project

Head over to your cmd and navigate to directory you wish to place the project within. In your cmd type the following and hit enter to generate a new project.

dotnet new blazor -o ToDo.Blazor

Then navigate to the newly created project folder and run your project.

cd todo.blazor
dotnet run

Now you should see something like this telling you the project url.

Hosting environment: Production
Content root path: D:\tmp\Blazor\ToDo.Blazor
Now listening on: http://localhost:5000
Now listening on: https://localhost:5001
Application started. Press Ctrl+C to shut down.

By default dotnet new creates the configuration for both HTTP and HTTPS to avoid errors and AntiVirus warnings you could open the HTTP version by pasting http://localhost:5000 in your browser and hitting enter. If you followed along you should see a sample App like the image below.

Model

Create a new Models folder in the root of your app, and inside add the following class.

namespace ToDo.Blazor.Models
{
    public class ToDo
    {
        public int Id { get; set; }
        public string Description { get; set; }
        public string Status { get; set; }
    }
}
Todo Page

Inside the pages folder add a new file called Todo.razor.

@page "/todo"

<h1>Todo</h1>

<ul>
    @foreach (var todo in todos)
    {
        <li>@todo.Description</li>
    }
</ul>

<input placeholder="Something todo" @bind="@description" />
<button @onclick="@AddTodo">Add todo</button>

@code {
    private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>();
    string description;
    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(description))
        {
            todos.Add(new ToDo.Blazor.Models.ToDo { Description = description });
            description = string.Empty;
        }
    }
}
Todo Menu

Go to the NavMenu.razor file and add a new li for our Todo page like below.

<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">ToDo.Blazor</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="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="todo">
                <span class="oi oi-task" aria-hidden="true"></span> ToDo
            </NavLink>
        </li>
    </ul>
</div>

@code {
    bool collapseNavMenu = true;

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

    void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}
Rebuild and Run

Press Crtl C to stop the server. Then rebuild and run again.

dotnet build
dotnet run

Now refresh your page in the browser and navigate to the Todo page, try adding new Todos and see how the <ul> is populated with new Todo <li>s

Until this point we are still not interacting with our web api, if you refresh the browser your Todo list is cleared, next I will show how we can persist data to our back end and load Todos from our API.

Working a Web API

Before we implement the razor code inside our Blazor project there is something we need to modify in our back end web API.

CORS

In short Cross-Origin Requests (CORS) is the ability to request resources on a different URL or Domain than yours, There are tons of article out there explaining how to work with CORS, in our case we have to add some code to the start up class in our Todo API. Open Startup.cs and add the following two snippets.

            #region Adding a CORS Policy
            services.AddCors(options =>
                    {
                        options.AddPolicy("ToDoCors",
                            builder =>
                                builder.AllowAnyHeader()
                                .AllowAnyMethod()
                                .AllowAnyOrigin());
                    });
            #endregion

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        #region Enabling CORS Policy
        app.UseCors("ToDoCors");
        #endregion
        app.UseMvc();
Get ToDos
Todo.razor
@page "/todo"
<!-- Import HTTP -->
@inject HttpClient Http 
.
.
.
@code {
    private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>();
    string description;

    protected override async Task OnInitAsync()
    {
        todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos");
    }

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(description))
        {
            todos.Add(new ToDo.Blazor.Models.ToDo { Description = description });
            description = string.Empty;
        }
    }
}
Todo.razor

Complete Todo.razor file (I added some styling to make this project more appealing), by default this project contains bootstrap 4 so I made use of it.

@page "/todo"
<!-- Import HTTP -->
@inject HttpClient Http 

<h1>Todo</h1>

<div class="row">
    <div class="col-10">
        <input placeholder="Something todo" @bind="@description" class="form-control" />
    </div>
    <div class="col-2">
        <button @onclick="@AddTodo" class="btn btn-primary  mb-2">Add Todo</button>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <ul class="list-group">
            @foreach (var todo in todos)
            {
                switch (@todo.Status)
                {
                    case "Pending":
                        <li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <span class="font-weight-bold">@todo.Status</span>
                        </li>
                        break;
                    case "In Progress":
                        <li class="list-group-item list-group-item-success d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <span class="font-weight-bold">@todo.Status</span>
                        </li>
                        break;
                    case "Done":
                        <li class="list-group-item list-group-item-primary d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <span class="font-weight-bold">@todo.Status</span>
                        </li>
                        break;
                    case "Cancelled":
                        <li class="list-group-item list-group-item-warning d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <span class="font-weight-bold">@todo.Status</span>
                        </li>
                        break;
                }               
            }
        </ul>
    </div>
</div>

@code {
    private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>();
    string description;

    protected override async Task OnInitAsync()
    {
        todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos");
    }

    private void AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(description))
        {
            todos.Add(new ToDo.Blazor.Models.ToDo { Description = description });
            description = string.Empty;
        }
    }
}
Adding a new Todo Item

First we will install a new package that will help up serialize our Todo object into JSON, so in you cmd stop your server using Ctrl C, then type the following command and hit enter.

dotnet add package Newtonsoft.Json --version 12.0.2
Add ToDo

To save our new Todos to the back end we need to modify our AddTodo method like below.

    private async Task AddTodo()
    {
        if (!string.IsNullOrWhiteSpace(description))
        {
            using (var client = new HttpClient())
            {
                var newTodo = new ToDo.Blazor.Models.ToDo { Description = @description, Status = "Pending" };
                
                var body = Newtonsoft.Json.JsonConvert.SerializeObject(newTodo);

                System.Net.Http.HttpResponseMessage result = await client.PostAsync(@"http://localhost:58635/api/ToDos", new StringContent(body, System.Text.Encoding.UTF8, "application/json"));

                string resultContent = await result.Content.ReadAsStringAsync();

                if (result.IsSuccessStatusCode)
                {
                    todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos");
                }
                Console.WriteLine(resultContent);
            }
            
            description = string.Empty;
        }
    }
Update Todo

Finally we need to add the Modify Todo, so we have to make some changes to the Todo.razor page, please apply the following changes to your page and restart your app.

@page "/todo"
<!-- Import HTTP -->
@inject HttpClient Http 

<h1>Todo</h1>

<div class="row">
    <div class="col-8">
        <input placeholder="Something todo" @bind="@description" class="form-control" />
    </div>
    <div class="col-2">
        <select @bind="@status">
            <option value="Pending">Pending</option>
            <option value="In Progress">In Progress</option>
            <option value="Done">Done</option>
            <option value="Cancelled">Cancelled</option>
        </select>
    </div>
    <div class="col-2">
        <button @onclick="@SaveTodo" class="btn btn-primary  mb-2">Save Todo</button>
    </div>
</div>

<div class="card">
    <div class="card-body">
        <ul class="list-group">
            @foreach (var todo in todos)
            {
                switch (@todo.Status)
                {
                    case "Pending":
                        <li class="list-group-item list-group-item-info d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <div>
                                    <span class="font-weight-bold">@todo.Status</span>
                                    <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span>
                                </div>
                        </li>
                        break;
                    case "In Progress":
                        <li class="list-group-item list-group-item-success d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <div>
                                    <span class="font-weight-bold">@todo.Status</span>
                                    <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span>
                                </div>
                        </li>
                        break;
                    case "Done":
                        <li class="list-group-item list-group-item-primary d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <div>
                                    <span class="font-weight-bold">@todo.Status</span>
                                    <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span>
                                </div>
                        </li>
                        break;
                    case "Cancelled":
                        <li class="list-group-item list-group-item-warning d-flex justify-content-between align-items-center">
                                <span>@todo.Description</span>
                                <div>
                                    <span class="font-weight-bold">@todo.Status</span>
                                    <span><button class="btn btn-outline-primary oi oi-pencil" @onclick="@(e => Edit(@todo.Id))"></button></span>
                                </div>
                        </li>
                        break;
                }               
            }
        </ul>
    </div>
</div>

@code {
    private IList<ToDo.Blazor.Models.ToDo> todos = new List<ToDo.Blazor.Models.ToDo>();
    string description;
    string status;
    int  _selectedId = 0;

    private void Edit(int id){
        _selectedId = id;
        var selectedTodo= todos.FirstOrDefault(q => q.Id == id);
        description = selectedTodo.Description;
        status = selectedTodo.Status;
        Console.WriteLine(id);
    }
 
    protected override async Task OnInitAsync()
    {
        todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos");
    }

    private async Task SaveTodo()
    {
        if (!string.IsNullOrWhiteSpace(description))
        {
            using (var client = new HttpClient())
            {
                var newTodo = new ToDo.Blazor.Models.ToDo { Id = _selectedId, Description = @description, Status = status };
                
                var body = Newtonsoft.Json.JsonConvert.SerializeObject(newTodo);

                System.Net.Http.HttpResponseMessage result;
                if(_selectedId == 0){
                    result = await client.PostAsync(@"http://localhost:58635/api/ToDos", new StringContent(body, System.Text.Encoding.UTF8, "application/json"));
                }else{
                    result = await client.PutAsync($@"http://localhost:58635/api/ToDos/{_selectedId}", new StringContent(body, System.Text.Encoding.UTF8, "application/json")); 
                }

                string resultContent = await result.Content.ReadAsStringAsync();

                if (result.IsSuccessStatusCode)
                {
                    todos = await Http.GetJsonAsync<List<ToDo.Blazor.Models.ToDo>>("http://localhost:58635/api/ToDos");
                }
                Console.WriteLine(resultContent);
            }
            
            description = string.Empty;
            status = "Pending";
            _selectedId = 0;
        }
    }
}

We can not judge a framework with a simple app like this but at first glance I find that Blazor is the easiest framework I’ve worked with so far. Why ? I’m a c# developer there is no huge learning curve, you just have to understand how routing, events and binding work and that’s all.

Hope you’ve had some benefits from this simple tutorial, see you in upcoming posts.

Source Code

Taggs:
3 Comments
  • strikethrough text facebook 3:12 am November 9, 2020 Reply

    Nice blog here! Also your website loads up fast! What hostare you using? Can I get your affiliate link to your host?I wish my site loaded up as fast as yours lol

    • Mozart Al Khateeb 10:41 pm November 26, 2020 Reply

      Here is an affiliate link I hope you get a good discout.

  • Ocie Doporto 8:00 am December 8, 2020 Reply

    outstanding article

Write a comment