Search code examples
c#blazorparameter-passingkeyeventargs

How to pass a data object and KeyEventArgs from a Child Component to a Parent Component in Blazor Server App


In Blazor Server App I have a Child Component which has a button on it. The Child Componet has some data from the parent bound to (a Book objec). I am calling a click event handler in the parent when I click this button on the Child Component. However, this button clinic event Handler in the parent needs to know wheter the user clicked the button on the chid with Shift Key Pressed or not. I am able to pass the data object (a Book object) from the Child to the parent, but I have not been able to figure out how I pass KeyEventArg also from the Child to the parent, which will help the parent know whether the Shift Key was pressed or not when the user clicked on a button on the child. I would appreciate help. I include code for my Child Component and Parent Component.

// Code for the Child

<div class="card-faheem">
        
        <div >
            <span class="span-fa">@BookContainer.ValueWord</span>
            
        </div>
        <div class="button-div">
            <button class="btn btn-primary" title="View" @onclick="@(async () => await OnSelected.InvokeAsync(BookContainer))">
            <i class="bi bi-binoculars"></i>
        </button>
        </div>
    </div>

@code 
{
    [Parameter, EditorRequired] public TextContainer BookContainer { get; set; } = default!;
    [Parameter, EditorRequired] public EventCallback<TextContainer> OnSelected { get; set; }
}

// Code for the parent
@inject TextContainerRepository tcr
<h3>CardParentPage</h3>

<div >
    @foreach (var book in books)
    {
        <CardComponent BookContainer=book OnSelected="HandleSelectedBook" />
    }
    </div>
@code {

    List<TextContainer> books { get; set; }

    protected override async Task OnInitializedAsync()
    {
        books = tcr.GetBooksByAuthorName("Jane Auston");

    }


    private void HandleSelectedBook(TextContainer book)
    {
       if (true)
       //     (args.ShiftKey)
        {
            // Do something if ShifKey was pressed
        } else
        {
            // Do something else if ShifKey was no pressed
        }

    }


}

enter image description here

enter image description here


Solution

  • You can create a simple struct or record, but the simplest in one off situations is the Tuple.

    [Parameter, EditorRequired] public EventCallback<Tuple<MouseEventArgs,TextContainer>> OnSelected { get; set; }
    
     <button class="btn btn-primary" title="View" @onclick="(e) => Selected(e, BookContainer)">
    
    //....
    
        private async Task Selected(MouseEventArgs e, TextContainer book)
            => await OnSelected.InvokeAsync(new Tuple<MouseEventArgs, TextContainer>(e, book));
    
    
    
        private void HandleSelectedBook(Tuple<MouseEventArgs,TextContainer> data)
        {
            var x = data.Item1.AltKey;
    
            //.....
        }
    

    Here's my demo code:

    Component:

    <h3>BookComponent</h3>
    <button class="btn btn-success" @onclick="@((e) => OnSelected(e, "Mansfield Park"))">Select Pride and Passion</button>
    @code {
        [Parameter] public EventCallback<Tuple<MouseEventArgs, string>> BookSelected { get; set; }
    
        private async Task OnSelected(MouseEventArgs e, string book)
            => await BookSelected.InvokeAsync(new Tuple<MouseEventArgs, string>(e, book));
    }
    

    Page:

    @page "/"
    <PageTitle>Index</PageTitle>
    
    <BookComponent BookSelected=OnBookSelected />
    
    <div class="alert alert-info">
        @message
    </div>
    
    @code {
        private string message = "Not Set";
    
        private Task OnBookSelected(Tuple<MouseEventArgs,string> data)
        {
            message = data.Item1.ShiftKey ? $"Shifted {data.Item2}" : $"{data.Item2}";
            return Task.CompletedTask;
        }
    }