8

In a Blazor form, I'd like to be able to detect whenever a form value has changed, and set a boolean value as a result.

Here is some code to illustrate how I am currently doing it:

<EditForm Model="Person" OnValidSubmit="OnSubmitValidateForm">
   <div class="form-group">
      <label>First Name</label>
      <input type="text" value="@Person.FirstName" @onchange="OnFirstNameChanged" />
   </div>
   <div class="form-group">
      <label>Surname</label>
      <input type="text" value="@Person.Surname" @onchange="OnSurnameChanged" />
   </div>
   <input type="submit" class="btn btn-primary" value="Save"/>
</EditForm>

@code {
   Person Person = new Person();
   bool dataChanged = false;
  
   void OnFirstNameChanged(ChangeEventArgs e)
   {
      Person.FirstName = e.Value.ToString();
      dataChanged = true;
      StateHasChanged();
   }
   
   void OnSurnameChanged(ChangeEventArgs e)
   {
      Person.Surname = e.Value.ToString();
      dataChanged = true;
      StateHasChanged();
   }
}

In the above form I have two fields - "FirstName" and "Surname" which are properties of the "Person" class. There is an input textbox bound to each property. Whenever the value changes in either textbox, the respective method is called which will update the property value, and also indicate that the form data has changed.

It seems excessive to have an event handler for every form item, when in each case all it's really doing is what @bind="" would do, plus setting dataChanged to true.

My question is, whilst the approach works, it seems like I have to write quite a lot of extra code. Is there a better way to handle this?

4
  • Maybe you don't need that boolean? What do you really want to achieve?
    – Porkopek
    Commented Nov 25, 2020 at 17:31
  • You're doing this like if you were working with react. Bind your values to @bind-Value, instead to value
    – Porkopek
    Commented Nov 25, 2020 at 17:34
  • I guess my answer was not useful to you, right? Please, don't be shy, tell us what you're looking for.
    – enet
    Commented Nov 26, 2020 at 19:05
  • @enet it was extremely useful thank you. I just didn't get a change to try it out until now. It worked brilliantly. Thanks again. Commented Nov 27, 2020 at 12:28

2 Answers 2

10

You should use the native Forms Components, such as InputText, InputDate, etc., and implement the OnFieldChanged event. The OnFieldChanged event is raised for each field in the model.

Code sample

@page "/"

@using Microsoft.AspNetCore.Components.Forms;

<EditForm EditContext="@ValidationContext.EditContext" 
            OnValidSubmit="HandleValidSumit">
    <DataAnnotationsValidator />

    <div class="form-group">
        <label for="name">FirstName: </label>
        <InputText Id="FirstName" Class="form-control" @bind- 
      Value="@person.FirstName"></InputText>
        <ValidationMessage For="@(() => person.FirstName)" />

    </div>
    <div class="form-group">
        <label for="body">Surname: </label>
        <InputText Id="body" Class="form-control" @bind- 
       Value="@person.Surname"></InputText >
        <ValidationMessage For="@(() => person.Surname)" />
    </div>
    <button type="submit" class="btn btn-success">Submit</button>
</EditForm>


@code
{
   Person person = new Person();
   protected override void OnInitialized()
   {
        EditContext = new EditContext(person);
        EditContext.OnFieldChanged += EditContext_OnFieldChanged;
       
        base.OnInitialized();
   }     
    private async Task HandleValidSumit()
    {
       
        Console.WriteLine("Saving...");
        Console.WriteLine(person.FirstName);
        Console.WriteLine(Person.Surname);
    }

   
    // Note: The OnFieldChanged event is raised for each field in the 
     // model
    private void EditContext_OnFieldChanged(object sender, 
            FieldChangedEventArgs e)
    {
        Console.WriteLine(e.FieldIdentifier.FieldName);

    }
    
}

Note: Calling the StateHasChanged() method from event handlers for UI events such as Click, Change, etc. is superfluous. It is automatically called by the framework.

2
  • That works like an absolute charm. That's exactly what I was looking for thank you. I am already using the form components - I just stripped them out of my example to make it as simple as possible (although it worked against me on this occasion). Commented Nov 27, 2020 at 12:26
  • 2
    Is there any way we can do using <input> html tags and not <InputText> tag Commented Apr 10, 2021 at 8:10
1

You must use EditContext instead of Model in your EditForm. Sample:

<EditForm EditContext="PersonContext" OnValidSubmit="@OnSubmitValidateForm">
    <div class="form-group">
        <label>First Name</label>
        <input type="text" @bind-value="person.FirstName" />
    </div>
    <div class="form-group">
        <label>Surname</label>
        <input type="text" @bind-value="person.Surname" />
    </div>
    <input type="submit" class="btn btn-primary" value="Save" />
</EditForm>

@code {
    EditContext PersonContext;
    Person person = new Person();

    protected override void OnParametersSet()
    {
        PersonContext = new(person);
    }

    void OnSubmitValidateForm()
    {
        if (PersonContext.IsModified())
        {
            //you logic
        }
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.