16

I have a simple child Blazor component (MyModal) like so:

<div class="modal">
    <h3>@Title</h3>
    @BodyTemplate
</div>

@code
{
    [Parameter] string Title { get; set; }
    [Parameter] RenderFragment BodyTemplate { get; set; }
}

In my Parent component I'm calling it like so:

<MyModal Title="Super Cool Modal">
        <BodyTemplate>
            @MyObject.Name
        </BodyTemplate>
</MyModal>

public MyObject MyObject { get; set; } = new MyObject();

Later on after the page has initially rendered I update MyObject but the Child component itself referencing @MyObject.Name never updates.

Seems I have to force refresh on the child object after I've updated the Object (StateHasChanged) but not sure how to do this with this example.

aherrick
  • 19,799
  • 33
  • 112
  • 188

5 Answers5

17

You can try this

Child

Create a public refresh method that you can call from parent

<div class="modal">
    <h3>@Title</h3>
    @BodyTemplate
</div>

@code
{
    [Parameter] string Title { get; set; }
    [Parameter] RenderFragment BodyTemplate { get; set; }

    public void RefreshMe()
    {
        StateHasChanged();
    }
}

Parent

Call child refresh method

<MyModal Title="Super Cool Modal"
         @ref="ChildComponent">
        <BodyTemplate>
            @MyObject.Name
        </BodyTemplate>
</MyModal>
@code
{
   public MyObject MyObject { get; set; } = new MyObject();
   protected UploadDownloadComponent ChildComponent;

   //Call this method to update the child
   private void Change(TreeEventArgs args)
   {
       ChildComponent.RefreshMe();
   }
}
Amine
  • 1,198
  • 11
  • 19
  • Worth noting if you derive your own MyComponentBase from ComponentBase, you could add the RefreshMe() method to MyComponentBase. Then you can inherit any of your custom components from MyComponentBase and you'll get the ability to invoke RefreshMe(). Its DRY -- arguably better than adding RefreshMe() to every single component that needs it. – JamesHoux Feb 21 '22 at 00:19
  • 1
    What is UploadDownloadComponent? – Yster Aug 11 '22 at 14:28
  • @Yster it is the child component – Isaac Ikusika Nov 04 '22 at 17:29
  • This answer makes no sense. Where does this UploadDownload component come into play? That's not the child component in this question. What is TreeEventArgs? What calls the Change function? – MC9000 Aug 21 '23 at 15:05
2

You can do "OnParametersSetAsync()" on child component:

Parent component:

<childCompoment param="@paramParent" />

Child component:

[Parameter]
public string param{get;set;}

protected override async Task OnInitializedAsync(){await Refresh();}

async Task Refresh(){}
Amal K
  • 4,359
  • 2
  • 22
  • 44
Martin VU
  • 97
  • 6
2

You can just use CascadingParameter.

In your parent component, just call child component like this:

<CascadingValue Value="selectedChats[0].Id" Name="chatID">
     <AdminChatComponent> </AdminChatComponent>
</CascadingValue>

Child Component:

[CascadingParameter(Name ="chatID")]
public string chatID { get; set; }

After that, all you need to do is just make a OnParametersSetAsync method in child component andjust bind your value in parent component. In my case I binded my value to datagrid row select.

After that whenever binded value changes on parent component, also your child component will trigger OnParametersSetAsync method.

SBU
  • 194
  • 9
1

Since <MyModal> has a RenderFragment parameter it's enough to call StateHasChanged() on the parent component, it will recalculate and render all child components with parameters from the parent.

hultqvist
  • 17,451
  • 15
  • 64
  • 101
0

I got a different example with a complex parameter object that has to be serialized to a form model.

OnParametersSetAsync() will update child component if parameter changes.

We need to bind child parameter using @bind-Job="_job"

Parent:

<div class="tab-pane active" id="kt_job_tab_1">
    <JobEdit @bind-Job="_job"></JobEdit>
</div>

@code
{
    private Job _job;

    protected override async Task OnInitializedAsync()
    {
        _job = await GetJob();

        await base.OnInitializedAsync();
    }

}

Child:

<EditForm Model="@_model" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />

    <div class="form-group row">
        <label class="col-xl-3 col-lg-3 col-form-label text-sm-left text-lg-right">Title</label>
        <div class="col-lg-9 col-xl-6">
            <InputText @bind-Value="_model.JobTitle" class="form-control form-control-solid form-control-lg" />
        </div>
    </div>
    <div class="form-group row">
        <label class="col-xl-3 col-lg-3 col-form-label text-sm-left text-lg-right">Department</label>
        <div class="col-lg-9 col-xl-6">
            <InputText @bind-Value="_model.Department" class="form-control form-control-solid form-control-lg" />
        </div>
    </div>
</EditForm>

@code {

    [Parameter]
    public Job Job { get; set; }

    [Parameter]
    public EventCallback<Job> JobChanged { get; set; }

    private Model _model;

    protected override async Task OnInitializedAsync()
    {
        InitializeModel();

        await base.OnInitializedAsync();
    }

    //THIS WILL UPDATE MODEL IF PARENT CHANGES
    protected override async Task OnParametersSetAsync()
    {
        InitializeModel();

        await base.OnParametersSetAsync();
    }

    private async void HandleValidSubmit()
    {
        //WE CODE HERE
        //THEN

        //THIS UPDATES PARENT IF CHILD JOB CHANGES
        await JobChanged.InvokeAsync(Job); 

        StateHasChanged();
    }

    private void InitializeModel()
    {
        _model = new Model
        {
            JobIdentifier = Job.Identifier,
            JobTitle = Job.JobTitle,
            Department = Job.Department
        };
    }

    private class Model
    {
        [Required]
        public string JobIdentifier { get; set; }

        [Required]
        public string JobTitle { get; set; }

        [Required]
        public string Department { get; set; }
    }

}
Javier Contreras
  • 871
  • 7
  • 16