blazor editform change events
Asked Answered
G

2

3

I want to have an InputSelect in a blazor editform that is bound to a model value and also has an onchange event that changes other properties in the model based on the new value.

binding to both @bind-Value and @onchange does not work (im guessing because bind value uses both the value and the value changed properties of the input.

I can bind to oninput, but I would like to know if there is a better way to do this.

<InputSelect id="inputPeriod" name="inputPeriod" class="form-control" @bind-Value="model.Period" @oninput="periodChanged">


protected void periodChanged(ChangeEventArgs e)
        {}

I can bind to oninput like this

but ideally I would like to bind to the @onchange event after the model property has been updated, or know what the best practice is for this. without using the bind-value the model validation will not work so the only alternative way I can think of is to have the change events working inside the properties in my model, but that seems wrong

Guereza answered 3/5, 2020 at 14:45 Comment(0)
O
25

Here's a silly sample in which you have to enter your name, and then select your pet, the result of which is renaming you after your dear pet. The sample describes how you can manipulate your model when a field changes:

<EditForm EditContext="@EditContext">
<DataAnnotationsValidator />

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

</div>
<div class="form-group">
    <label for="body">Select your pet: </label>
    <InputSelect @bind-Value="@person.Pet">
        <option value="Cat">Cat</option>
        <option value="Dog">Dog</option>
        <option value="Horse">Horse</option>
        <option value="Lion">Lion</option>
    </InputSelect>
     <ValidationMessage For="@(() => person.Pet)" />
</div>

<p>
    <button type="submit">Submit</button>
</p>
</EditForm>


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


    protected override void OnInitialized()
    {
        EditContext = new EditContext(person);
        EditContext.OnFieldChanged += EditContext_OnFieldChanged;

        base.OnInitialized();
    }

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

            person.Name = person.Pet;

        }
    }


    public class Person
    {
        public string ID { get; set; }
        public string Name { get; set; }
        public string Pet { get; set; }
    }
}

the only alternative way I can think of is to have the change events working inside the properties in my model, but that seems wrong

Not at all...

There's no requirement for models to implement INotifyPropertyChanged, but if they do, you can easily wire that up to the EditContext. Then you have no need to use the built-in Input* components - you can instead bind to regular HTML elements and still get modification notifications. This provides more flexibility in how the UI is rendered, at the cost of more complexity and boilerplate in your model classes.

Hope this helps...

Overestimate answered 3/5, 2020 at 18:51 Comment(3)
Thanks. this works great, except I tried it on a date field and it fires too early when im typing a year so I have to find a way to check the model is valid before changing the other values, but that's a different problemGuereza
You can validate the EditContext by calling EditContext.Validate() from within the OnFieldChanged event like this: if (EditContext.Validate()) { Disabled = null; } else { Disabled = "disabled"; }Overestimate
This only works reliably for scalar types. If you've got multiple complex objects connected to your model, and those complex objects happen to have the same name for a property that you bind to (such as "Id"), you'll end up updating fields whenever you change any of the InputSelect componens for those complex objects.Nonsectarian
C
2

This is one solution which achieves what you want without using the two way binding. Instead the value is bound to the selectbox only, which will trigger the validation.

<InputSelect 
    id="inputPeriod" name="inputPeriod"      
    value="model.Period"
    class="form-control" 
    @onchange="periodChanged"
> 



@code
{

    void periodChanged(ChangeEventArgs e) 
        {
            model.Period = (string) e.Value;
            //your logic... 
        }

}
Corrinecorrinne answered 3/5, 2020 at 16:20 Comment(2)
Thanks for the help. this looks like a nice solution, but when I tried it on a date field I get a runtime error : InvalidOperationException: Microsoft.AspNetCore.Components.Forms.InputDate`1[System.DateTime] requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.Guereza
You can't code like this... InputSelect is a component.Overestimate

© 2022 - 2024 — McMap. All rights reserved.