Reputation: 376
I have a Blazor component that needs to pass a boolean to its child component to disable the form submit button when submitting.
<EditForm Model="Model" OnValidSubmit="SubmitSearch">
<div class="panel-body">
<ChildComponent IsSubmitting="@IsSubmitting"/>
</div>
</EditForm>
My child component is just a series of inputs with a submit button
<div>
// inputs etc.
<span class="pull-left">
<button class="btn btn-success" type="submit" disabled="@IsSubmitting">
Submit Search
</button>
</span>
</div>
@code {
[Parameter]
public bool IsSubmitting { get; set; }
}
and my submit method sets IsSubmitting like so
public async void SubmitSearch()
{
IsSubmitting = true;
var result = await Service.GetStuff();
// do stuff with result
IsSubmitting = false;
}
I notice that the button never disables. Am I missing some sort of lifecycle hook to trigger a re-render when the parameter updates?
Any help appreciated.
Upvotes: 1
Views: 1967
Reputation: 45586
The only issue with your code is that you use void instead of Task in the SubmitSearch method, which should be like this:
public async Task SubmitSearch()
{
IsSubmitting = true;
// await Task.Delay(3000);
// var result = await Service.GetStuff();
// do stuff with result
IsSubmitting = false;
}
The above code perfectly works and do the job...
Upvotes: 2
Reputation: 376
I found a solution in the last answer here: How to disable/hide a button as soon as clicked in Blazor?
Even though SubmitSearch
was an async method, its call to the back-end service was largely synchronous.
As @HenkHolterman said in that answer, GUI is not updated unless the method calls are purely async.
So I created an async Task Dispatcher like so:
async Task DispatchSubmit()
{
IsSubmitting = true;
await Task.Delay(1); // allow the GUI to catch up
await SubmitSearch();
IsSubmitting = false;
}
that fixed everything!
Upvotes: 5
Reputation: 45586
Here's a code sample that disables the submit button as long as the the model is not valid.
using System.ComponentModel.DataAnnotations
<h1>My articles</h1>
<p>Leave me a comment</p>
<EditForm EditContext="@EditContext">
<DataAnnotationsValidator />
<div class="form-group">
<label for="name">Name: </label>
<InputText Id="name" Class="form-control" @bind-Value="@Model.Name">
</InputText>
<ValidationMessage For="@(() => Model.Name)" />
</div>
<div class="form-group">
<label for="body">Text: </label>
<InputTextArea Id="body" Class="form-control" @bind-Value="@Model.Text">
</InputTextArea>
<ValidationMessage For="@(() => Model.Text)" />
</div>
</EditForm>
<p>
<button>Action 1</button>
<button>Action 2</button>
<button disabled="@Disabled" @onclick="Save">Save</button>
</p>
@code
{
private EditContext EditContext;
private Comment Model = new Comment();
protected string Disabled { get; set; } = "disabled";
private async Task Save ()
{
await Task.Delay(3000);
Console.WriteLine("Saving...");
Console.WriteLine(Model.Name);
Console.WriteLine(Model.Text);
}
protected override void OnInitialized()
{
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
base.OnInitialized();
}
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs
e)
{
Console.WriteLine(e.FieldIdentifier.FieldName);
SetSaveDisabledStatus(e);
}
private void SetSaveDisabledStatus(FieldChangedEventArgs e)
{
if (EditContext.Validate())
{
Disabled = null;
}
else
{
Disabled = "disabled";
}
}
public class Comment
{
[Required]
[MaxLength(10)]
public string Name { get; set; }
[Required]
public string Text { get; set; }
}
}
Upvotes: 1
Reputation: 17404
You just cannot do that :
public async void SubmitSearch() // async method MUST return a Task
{
IsSubmitting = true;
var result = await Service.GetStuff();
// do stuff with result
IsSubmitting = false;
// The component is refreshed after the method call
}
But this should work
public void SubmitSearch()
{
IsSubmitting = true;
Service.GetStuff()
.ContinueWith(t =>
{
IsSubmitting = false;
InvokeAsync(StateHasChanged());
if (t.Exception != null)
{
throw t.Exception;
}
var result = t.Result;
// do stuff with result
});
}
Upvotes: 1