Search code examples
eventsblazorparent-child

Good practice to pass values from child component to parent page in Blazor?


Iam uncertain if I'am making a mistake using my approach to pass Objects from a Child Component to its consuming parent page in Blazor. I have a poco class with ID,Name. A component with a select div from a list of poco-class obj.. User selectes a entrie and that entrie shall be shown on the parent page. I am to unexperienct to see any problems with my solution. 1)Did I get it right? 2)If all is kind of okay what is with the OnInitializedAsync() there is no awaiting (Parent) shall I use OnInitialized() instead? 3)Is the use of Dispose correct? 4) Is this solution okay if I use the same approach maybe 20 times in one App for different components etc. Does this scale okay? Thx nogood

ParentPage:

@page "/"
@inject MitarbeiterEventService MitarbeiterEventService

@implements IDisposable

<h1>ParentPage</h1>
<MitarbeiterSel></MitarbeiterSel>
<br />

@if (SelMitarbeiterFromChild != null)
{
    <p>Selected Obj. from Child: @SelMitarbeiterFromChild.Name </p>
}

@code{
    public MitarbeiterLite SelMitarbeiterFromChild;

    protected override async Task OnInitializedAsync()
    {
        MitarbeiterEventService.EventMitarbeiterChangedInComp += HandelOnMitarbeiterChangedInComp;
    }

    public async void HandelOnMitarbeiterChangedInComp(object sender, MitarbeiterLite selMitarbeiter)
    {
        SelMitarbeiterFromChild = selMitarbeiter;

        await InvokeAsync(() =>
        {
            StateHasChanged();

        });
    }

    public void Dispose()
    {
        MitarbeiterEventService.EventMitarbeiterChangedInComp -= HandelOnMitarbeiterChangedInComp;
    }
}

The Code from the service (in the actual App I use MVVM so the event code would be in the ViewModel):

    public class MitarbeiterEventService
    {
        public event EventHandler<MitarbeiterLite> EventMitarbeiterChangedInComp;
        public void FireEvent(MitarbeiterLite selMitarbeiterFromComp)
        {
            EventMitarbeiterChangedInComp?.Invoke(this, selMitarbeiterFromComp);
        }
    }

And last the component it self:

@inject MitarbeiterEventService  MitarbeiterEventService

<h3>MitarbeiterSelection</h3>

<select class="form-control col-6" @onchange="@(x => OnMitarbeiterSelectedChanged(x.Value.ToString()))">
    <option value="" disabled selected hidden>--Mitarbeiter--</option>
    @foreach (var mita in MitarbeiterLitesLst)
    {
        <option [email protected]>@mita.Name </option>
    }
</select>

@code {
    //Ini List 
    public List<MitarbeiterLite> MitarbeiterLitesLst = new List<MitarbeiterLite>
{
        new MitarbeiterLite("Henry"),
        new MitarbeiterLite("John"),
        new MitarbeiterLite("Sue"),
        new MitarbeiterLite("Mary"),
        new MitarbeiterLite("Jimmy")
    };

    public void OnMitarbeiterSelectedChanged(string guidAsString)
    {
        MitarbeiterEventService.FireEvent(MitarbeiterLitesLst.FirstOrDefault(x => x.Id.ToString() == guidAsString));
    }
}

Solution

  • To communicate with the parent use EventCallback. An Event Service is more useful when the consumer of the event is not a parent or a child, but some other component in your application that does not have a direct relationship with the component where you select the item.

    In your scenario, I'd go for:

    Component

        <h3> MitarbeiterSelection </h3>
    
    <select class="form-control col-6"
            @onchange="@(x => OnMitarbeiterSelectedChanged(x.Value.ToString()))">
    
        <option value="" disabled selected hidden> --Mitarbeiter-- </option>
    
        @foreach (var mita in MitarbeiterLitesLst)
        {
            <option [email protected]>@mita.Name </option>
        }
    
    </select>
    
    @code {
        [Parameter]
        public EventCallback<MitarbeiterLite> OnItemSelected { get; set; }
    
        // Initialize List
        public List<MitarbeiterLite> MitarbeiterLitesLst = new List<MitarbeiterLite>
        {
            new MitarbeiterLite("Henry"),
            new MitarbeiterLite("John"),
            new MitarbeiterLite("Sue"),
            new MitarbeiterLite("Mary"),
            new MitarbeiterLite("Jimmy")
        };
    
        public void OnMitarbeiterSelectedChanged(string guidAsString)
        {
            MitarbeiterLite selected =
                MitarbeiterLitesLst
                .FirstOrDefault(x => x.Id.ToString() == guidAsString);
    
            OnItemSelected.InvokeAsync(selected);
        }
    }
    

    Parent Page

        @page "/"
    
        <h1> ParentPage </h1>
        <MitarbeiterSel OnItemSelected=@HandleOnMitarbeiterChangedInComp />
        <br />
        
        @if (SelMitarbeiterFromChild != null)
        {
            <p> Selected Obj. from Child: @SelMitarbeiterFromChild.Name  </p>
        }
        
        @code{
            public MitarbeiterLite SelMitarbeiterFromChild;
        
            public async Task HandelOnMitarbeiterChangedInComp(
                MitarbeiterLite selMitarbeiter)
            {
                SelMitarbeiterFromChild = selMitarbeiter;
            }
        }