mb1231
mb1231

Reputation: 376

Passing disabled attribute to an HTML element in a child component is not working

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

Answers (4)

enet
enet

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

mb1231
mb1231

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

enet
enet

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

agua from mars
agua from mars

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

Related Questions