How to ensure razor components only show valid object states? (i.e. as seen in data store)

140 Views Asked by At

I'm venturing into ASP.NET Blazor Server, and I don't quite understand the component lifecycle there. My project is a simple 'employee manager' app in which users can CRUD an employee repository. Employee data is stored in a remote database.

The main page ("/" and "/Employee/List") lists all employee data in a table. Each entry in that table has an 'update' button that redirects to an update page for the related employee ("/Employee/Update/{EmployeeId:int}"), where I have an EditForm to handle the changes and update my database. There is also a 'back to list' link in that page.

All goes well in the happy path. If I make changes and save, they are replicated as expected to the database and displayed in the list page when I navigate back. However, if I do changes and click the 'back to list' button or my browser's back button without submitting the form, I will still see those changes in my list page.

Naturally, those changes are not replicated to the database, and if I refresh the list page I get the expected values back. But why does that happen in the first place? And, more importantly, how to fix it?

List.razor

@page "/"
@page "/Employee/List"

@inject IEmployeeRepository EmployeeRepository

[...]
<table border="1" cellpadding="10">
    <tr>
        <th>Employee ID</th>
        <th>Employee Name</th>
        <th colspan="2">Actions</th>
    </tr>
    @foreach (var item in Employees)
    {
        <tr>
            <td>@item.EmployeeId</td>
            <td>@item.EmployeeName</td>
            <td><a class="link-button" href="/employee/update/@item.EmployeeId">Update</a></td>
            <td><a class="link-button" href="/employee/delete/@item.EmployeeId">Delete</a></td>
        </tr>
    }
</table>

@code {
    private List<EmployeeModel> Employees { get; set; }

    protected override void OnInitialized()
    {
        Employees = EmployeeRepository.GetEmployees();
    }
}

Update.razor

@page "/Employee/Update/{EmployeeId:int}"

@inject IEmployeeRepository EmployeeRepository

[...]
<EditForm Model="Employee" OnSubmit="OnSaveClicked">
    <table border="0" cellpadding="10">
        <tr>
            <td class="right"><label for="EmployeeId">Employee ID</label></td>
            <td><span id="EmployeeId">@Employee.EmployeeId</span></td>
        </tr>
        <tr>
            <td class="right"><label for="EmployeeName">Employee Name</label></td>
            <td><InputText id="EmployeeName" @bind-Value="Employee.EmployeeName"></InputText></td>
        </tr>
        <tr>
            <td colspan="2"><button type="submit">Save</button></td>
        </tr>
    </table>
</EditForm>
<a href="/employee/list">Back to List of Employees</a>

@code {
    [Parameter]
    public int EmployeeId { get; set; }
    private EmployeeModel Employee { get; set; }

    protected override void OnInitialized()
    {
        Employee = EmployeeRepository.GetEmployee(EmployeeId);
    }

    private void OnSaveClicked()
    {
        EmployeeRepository.UpdateEmployee(Employee);
    }
}
1

There are 1 best solutions below

0
MrC aka Shaun Curtis On

The why is relatively easy. When you get your data from the data source you get it as a class - a reference object. You pass that reference into your editor and then edit the object that reference points to. The list points to that same object and thus displays the edited values. When you refresh from the data store you get the original values back again.

The how to fix it depends on your data pipeline design. I can provide a (lengthy) answer, but first I'd like to see what EmployeeRepository looks like to understand how it's interacting with your data store (probably EF).