Updated date:

Blazor Component Dynamics: Interactive List (Edit Items)

blazor-component-dynamics-interactive-list-edit-items
blazor-component-dynamics-interactive-list-edit-items

This article will show you an example on how list items can be implemented and be made dynamic using components. In the end, you will have a simple user list, where you can edit emails and remove the items.

API

Since the this is a joined project, of API, class library and client-side blazor, we will have to look at API and the shared class library at the same time. As this is where the data comes from.

blazor-component-dynamics-interactive-list-edit-items
public  class SecondTestModel
    {
         
            public string bankaccountid { get; set; }
            public string bankname { get; set; }
            public string email { get; set; }
           
            public int userid { get; set; }

    }
 public  class TestModel
    {
        public int id { get; set; }
        public string fullname { get; set; }
        public int age { get; set; }
    }

Throughout the application, we only use two models - one for user data and one for their bank data.

 public static List<Shared.TestModel> users = new List<Shared.TestModel>()
        {
            new Shared.TestModel()
            {
                id = 1,
                age = 25,
                fullname = "Test Tester"
            },
             new Shared.TestModel()
            {
                id = 2,
                age = 36,
                fullname = "Mr. Tester"
            },
              new Shared.TestModel()
            {
                id = 3,
                age = 45,
                fullname = "Mrs. Tester"
            },
               new Shared.TestModel()
            {
                id = 4,
                age = 89,
                fullname = "Mister Test"
            },
                new Shared.TestModel()
            {
                id = 5,
                age = 72,
                fullname = "Mister testing"
            },
        };

        public static List<Shared.SecondTestModel> bankdetails = new List<Shared.SecondTestModel>()
        {
            new Shared.SecondTestModel()
            {
                userid = 1,
                bankaccountid ="xx1111",
                bankname = "test bank",
                email = "ee@m.l"
            },
            new Shared.SecondTestModel()
            {
                userid = 2,
                bankaccountid ="bb7777",
                bankname = "test bank",
                email = "ll@m.l"
            },
            new Shared.SecondTestModel()
            {
                userid = 3,
                bankaccountid ="xx3333",
                bankname = "testing bank",
                email = "rr@m.l"
            },
            new Shared.SecondTestModel()
            {
                userid = 4,
                bankaccountid ="xx2222",
                bankname = "test bank",
                email = "uu@m.l"
            },
            new Shared.SecondTestModel()
            {
                userid = 5,
                bankaccountid ="aa1111",
                bankname = "test bank",
                email = "vv@m.l"
            },
        };

For test data, we simply have a couple of static lists, in a FakeDatabase class.

 [Route("/getusers")]
       public List<Shared.TestModel> GetUsers()
        {
            return FakeDatabase.users;
        }


        [Route("/getbankdetailsforusers")]
        public Shared.SecondTestModel GetBankDetailsForUser(int id)
        {
            return FakeDatabase.bankdetails.Find(x => x.userid == id);
        }

For the data to be retrieved, we need a couple of controllers - GetUsers and GetBankDetailsForUser methods.

[Route("/deleteuser")]
        public bool DeleteUser(int id)
        {
            try
            {
                FakeDatabase.users.RemoveAt(FakeDatabase.users.FindIndex(x => x.id == id));
                return true;
            }
            catch (Exception e)
            {

                return false;
            }
          
        }


        [Route("/updateemail")]
        public bool UpdateEmailAsync([FromQuery]int id, [FromBody]string newemail)
        {
            try
            {
                FakeDatabase.bankdetails.Find(x => x.userid == id).email = newemail;
                return true;
            }
            catch (Exception)
            {

                return false;
            }

        }

For the updates, we have two more action methods - DeleteUser and UpdateEmailAsync. The first one removes the user in the fake database list and the second one updates the email.

Client-side

For the client-side, we simply have to retrieve and display the values. There is no logic on, except for the visual changes.

blazor-component-dynamics-interactive-list-edit-items
@inject HttpClient http



 
    <tr>
        @if (user != null)
        {
            <td>@user.id</td>
            <td>@user.age</td>
            <td>@user.fullname</td>
        }
        @if (bankdetails != null)
        { 
            <td>@bankdetails.bankaccountid</td>
            <td>@bankdetails.bankname</td>
            <td>
                @if (!is_editoropened)
                {


                    <button @onclick="@(async () => await OpenEditor())">Edit</button>
                    @bankdetails.email
                }
                else
                {
                    <button @onclick="@(async () => await UpdateEmail())">Done</button>
                    <input @bind="@bankdetails.email" />
                }


            </td>
            <td @onclick="@(async () => await OnRemove.InvokeAsync(user))">close</td>
        }
        </tr>


@code {

    [Parameter]
    public BlazorApp1.Shared.TestModel user { get; set; }

    BlazorApp1.Shared.SecondTestModel bankdetails;

    protected override async Task OnParametersSetAsync()
    {
        bankdetails = await http.GetJsonAsync<BlazorApp1.Shared.SecondTestModel>("/getbankdetailsforusers?id=" + user.id);
    }

    bool is_editoropened;

    async Task OpenEditor()
    {
        is_editoropened = true;
    }

    async Task UpdateEmail()
    {
        await http.PutJsonAsync<bool>("/updateemail?id="+user.id, bankdetails.email);
        is_editoropened = false;

    }

    [Parameter]
    public EventCallback<BlazorApp1.Shared.TestModel> OnRemove { get; set; }
}

The main procedures will occur in the list item component. The user data is retrieved in the parent page, but the bank details we retrieve once the user parameter is set. Other than that, the data is properly displayed in the appropriate fields. The fields are actually part of a table, basically the component is a single row for the table.


Getting to the interesting part, you can see how smooth the whole editing approach is. We only need a boolean value and two different arrangements for what user can see. The will either see a button and the value, or an edit field and a button. Once the first button is clicked, the boolean is_editoropened is set to true and therefore the editing view opens up. Then, once the "Done" button is clicked, the application simply send and API request to make the change and displays it.


You may also see that we have an event callback for removal of an item. That will be handled in the parent page, as the item displayed come from a list in the parent page.

@page "/"
@inject HttpClient http

<table style="text-align:center;">
    <thead>
        <tr><th>user id</th><th>age</th><th>full name</th><th>bank account</th><th>bank name</th><th>email</th></tr>
    </thead>
    <tbody>
        @if (users != null)
        {
            @foreach (var item in users)
            {
                <UserComponent OnRemove="@(async (arg) => await RemoveItem(arg))" user="@item"></UserComponent>
            }
        }
    </tbody>
</table>


@code {
    List<BlazorApp1.Shared.TestModel> users;

    protected override async Task OnInitializedAsync()
    {
        users = await http.GetJsonAsync<List<BlazorApp1.Shared.TestModel>>("/getusers");
    }


    async Task RemoveItem(BlazorApp1.Shared.TestModel usertoremove)
    {
        try
        {
            await http.DeleteAsync("/deleteuser?id="+usertoremove.id);
            users.Remove(usertoremove);
        }
        catch (Exception)
        {

            throw;
        }
        
    }

}

For the main page, we first have the main table. Remember, the component is a row of a table and in the main part of the table we generate components using a foreach loop. We have users list, which contain user data and that is passed into each component. We retrieve the items when the page is loaded, so there is really nothing fancy about that. The more interesting part here is the RemoveItem method, which takes the user data and invokes an API call to remove it in our fake database. Once that is done, it simply removes the item from the list, the loop for rows gets re-evaluated and you do not see that item anymore.

Conclusion

As you can see, between the main page and the component, we have quite a few lines of code. So, if nothing else, components will make it more readable.

Related Articles