8

I have a crud operation using Blazor Server Side and Editform. Everything works great except for when I try to reset the form after editing an existing record.

When I change something in a form control and then click the reset button, it closes the form. The data that I change is updated to the HTML table, but it's not updated in the database.

Is there anyway I can prevent this?

Here is my model:

public class Address  
{
    public string province { get; set; }
    public string address { get; set; }
    public string contact_name { get; set; }
    public string phone_number { get; set; }
}

This is my EditForm:

<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div class="form-group">
        <InputText @bind-Value="@model.province" />
    </div> 
    <div class="form-group">
        <InputText @bind-Value="@model.contact_name" />
    </div>
    <div class="form-group">
        <InputText @bind-Value="@model.phone_number" />
    </div>
    <div class="form-group">
        <InputText @bind-Value="@model.address" />
    </div>
    <button type="submit" class="btn btn-primary">Save</button>
    <button type="reset" class="btn btn-warning">Cancel</button>
</EditForm>

Here is my HTML Table:

@if (address_list== null)
{
    <p>Loading</p>
}
else
{
    <table class="table table-striped text-center">
        <thead>
            <tr>
                <th scope="col" class="text-center">Province</th>
                <th scope="col" class="text-center">Contact Name</th>
                <th scope="col" class="text-center">Phone</th>
                <th scope="col" class="text-center">Address</th>
                <th scope="col" class="text-center">Edit</th>
                <th scope="col" class="text-center">Delete</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var d in  address_list)
            {
                <tr>
                    <td>@d.province</td>
                    <td>@d.contact_person</td>
                    <td>@d.phone_number</td>
                    <td>@d.address</td>
                    <td><button type="button" class="btn btn-link" @onclick="@(() => Edit(d))">Edit</button></td>
                    <td><button type="button" class="btn btn-link" @onclick="@(() => Delete(d))">Delete</button></td>  
                </tr>
            }
        </tbody>
    </table>
}
0

4 Answers 4

3

You don't show your server-side code related to the edit form, but I assume it looks something like this:

@code {
    public Address model { get; set; }
}

You can actually implement the getter and setter. In doing so, create a typical backing field, but then also create a clone field to capture your initial data. Also create a reset function that captures the values from the clone field and puts it back in to the current model state.

@code { 

    public Address model {
        get { return _model; }
        set {
            _model = value;
            _clone = new Address {
                province = value.province,
                address = value.address,
                contact_name = value.contact_name,
                phone_number = value.phone_number            
            }
        }
    Address _model;
    Address _clone;

    public void reset () {
        _model.province = _clone.province;
        _model.address = _clone.address;
        _model.contact_name = _clone.contact_name;
        _model.phone_number = _clone.phone_number;
    }

}

Then, in your edit form, instead of a 'reset' type button, do:

<button class="btn btn-warning" @onclick="reset" @onclick:preventDefault>Cancel</button>

This will last you until the guys at Microsoft implement something like:

<InputText @firstNonBlankAsInitial="true" @bind-Value="@model.province" />
Sign up to request clarification or add additional context in comments.

1 Comment

While the above code works, I had an issue communicating the Cancel event back to the parent component, e.g. to update the HTML table or close the child form. An EventCallback calls the setter, overwriting _clone. An Action will not call StateHasChanged, so nothing happens. Calling StateHasChanged manually has the same effect as EventCallback. My workaround is to create the _clone copy in the parent component, not in the setter of the child.
2

Extending to the answer provided by @pwilcox You can use the serializer

var modelString = JsonConvert.SerializeObject(model);
_clone = JsonConvert.DeserializeObject< Address >(modelString); 

and then in the reset function do the reverse

 var modelString = JsonConvert.SerializeObject(_clone);
  model = JsonConvert.DeserializeObject< Address >(modelString); 

1 Comment

Love this; obvious and simple. But in the form code behind, just hold on to the serialized object string; no need to create a duplicate object, no?
1

There can't be any easy solution since the problem is conceptual. You can't reset form to initial values with a button type reset. Reset button, reset all fields to their initial values, that is to the value they had when the HTML form was created.

However, the Html form may be destroyed and created several time during a single Edit because of automatic Blazor creation and destruction of components. For instance, if some fields or the whole form are within a TabItem of a Tab control fields and/or form is created/destroyed whenever you change the selected tab. For this reason form "initial values" differ from the initial values appearing the first time the form showed up, and are meaningless values.

Thus you can't act on Html fields or forms, but you need to act on the model that is bound to the form since just the underlying model is ensured to survive to all Blazor re-rendering.

Summing up the only solution is to store the initial property values of your model somewhere, and to copy on your model these initial values whenever the user cancel the operation either with a reset button or by closing a modal, and so on.

Comments

0

The "changed" data is not in sync anymore with the actual data in the database.

A quick (but not the most elegant) solution is to requery the data from the database.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.