I'm developing a Blazor WebAssembly application where I have a parent component Father
that contains two child components (Child1
and Child2
). Child2
should be enabled only when a specific condition in Child1 is met, but I'm encountering issues with maintaining the correct state between these components.
In Child1
, I have an autocomplete component MudAutocomplete that, upon selection, should enable certain functionality in Child2
by setting a state Model.SuperUser
in the parent component Father
. I've ensured that StateHasChanged()
is called after updating the state in Child1
, but the input in Child2
remains disabled even when the condition Model.SuperUser != null
is met.
@page "/father"
<Child1 Model="model" />
<Child2 Model="model" />
@code {
private MyModel model;
protected override void OnInitialized()
{
model = new MyModel();
base.OnInitialized();
}
public class MyModel
{
public int? AdminUserId { get; set; }
public UserDTO? AdminUser { get; set; }
public int? CustomerUserId { get; set; }
public UserDTO? CustomerUser { get; set; }
public int? SuperUserId { get; set; }
public UserDTO? SuperUser { get; set; }
}
}
// Child1.razor
<!-- SuperUser -->
<div class="form-group col-6">
<label class="required" for="SuperUser">@Localizer["SuperUser"]</label>
<MudAutocomplete T="UserDTO"
ResetValueOnEmptyText="true"
SearchFunc="@SearchSuperUser"
ToStringFunc="@(e => e == null ? null : $"{e.Email}")"
ShowProgressIndicator="true"
Variant="Variant.Outlined"
Clearable="true"
Placeholder="@Localizer["PleaseSelectSuperUser"]"
OnClearButtonClick="OnSuperUserCleared"
ProgressIndicatorColor="Color.Primary"
ValueChanged="OnSuperUserValueChanged">
<NoItemsTemplate>
<MudList Clickable="false">
<MudListItem>
@Localizer["NoItemsWasFound"]
</MudListItem>
</MudList>
</NoItemsTemplate>
<BeforeItemsTemplate>
<MudText Color="Color.Primary" Class="mud-list-item mud-list-item-gutters mud-list-item-clickable mud-ripple">@Localizer["PleaseSelectSuperUser"]</MudText>
</BeforeItemsTemplate>
<ProgressIndicatorInPopoverTemplate>
<MudList Clickable="false">
<MudListItem>
@Localizer["Loading"]
</MudListItem>
</MudList>
</ProgressIndicatorInPopoverTemplate>
</MudAutocomplete>
<ValidationMessage For="@(() => Model.SuperUserId)" />
</div>
@code {
[Parameter][EditorRequired] public MyModel Model { get; set; } = null!;
private async Task<IEnumerable<UserDTO>> SearchSuperUser(string value)
{
var result = await _myService.GetSuperUserFiltered(value);
if (result != null)
{
return result;
}
return new List<UserDTO>();
}
private void OnSuperUserCleared(MouseEventArgs args)
{
Model.SuperUser = null;
Model.SuperUserId = null;
StateHasChanged();
}
private void OnSuperUserValueChanged(UserDTO value)
{
Model.SuperUser = value;
Model.SuperUserId = value.Id;
StateHasChanged();
}
}
// Child2.razor
<!-- AdminUser -->
<div class="form-group col-6">
<label class="required" for="AdminUser">@Localizer["AdminUser"]</label>
<MudAutocomplete T="UserDTO"
ResetValueOnEmptyText="true"
SearchFunc="@SearchAdminUser"
ToStringFunc="@(e => e == null ? null : $"{e.Email}")"
ShowProgressIndicator="true"
Variant="Variant.Outlined"
Clearable="true"
Placeholder="@Localizer["PleaseSelectAdminUser"]"
OnClearButtonClick="OnAdminUserCleared"
ProgressIndicatorColor="Color.Primary"
ValueChanged="OnAdminUserValueChanged"
Disabled="Model.SuperUser == null"> <!-- It should be enabled only id SuperUser is selected -->
<NoItemsTemplate>
<MudList Clickable="false">
<MudListItem>
@Localizer["NoItemsWasFound"]
</MudListItem>
</MudList>
</NoItemsTemplate>
<BeforeItemsTemplate>
<MudText Color="Color.Primary" Class="mud-list-item mud-list-item-gutters mud-list-item-clickable mud-ripple">@Localizer["PleaseSelectAdminUser"]</MudText>
</BeforeItemsTemplate>
<ProgressIndicatorInPopoverTemplate>
<MudList Clickable="false">
<MudListItem>
@Localizer["Loading"]
</MudListItem>
</MudList>
</ProgressIndicatorInPopoverTemplate>
</MudAutocomplete>
<ValidationMessage For="@(() => Model.AdminUserId)" />
</div>
@code {
[Parameter][EditorRequired] public MyModel Model { get; set; } = null!;
private async Task<IEnumerable<UserDTO>> SearchAdminUser(string value)
{
if (Model.SuperUserId.HasValue && Model.SuperUserId.Value > 0)
{
var filter = new { Model.SuperUserId, value };
var result = await _myService.GetAdminUserBySuperUserFiltered(filter);
if (result != null)
{
return result;
}
}
return new List<UserDTO>();
}
private void OnAdminUserCleared(MouseEventArgs args)
{
Model.AdminUser = null;
Model.AdminUserId = null;
StateHasChanged();
}
private void OnAdminUserValueChanged(UserDTO value)
{
Model.AdminUser = value;
Model.AdminUserId = value.Id;
StateHasChanged();
}
}
Despite updating Model.SuperUser
correctly in Child1
and calling StateHasChanged()
, the Disabled="Model.SuperUser == null"
condition in Child2
does not enable the input as expected.
Any suggestions on how to properly enable/disable input in Child2
based on the state of Model.SuperUser
from Child1
?
You can use cascading values to pass the Model
from the Parent
to the Childred
elements. Then use an event callback within the Children
to pass changes back to the Parent
.
Here's a modified version of the code you provided.