Search code examples
c#blazorblazor-server-sideblazor-component

Blazor: Pass a function that takes parameters to a component and call it from it


I'm trying to split my Blazor Server project into components in order to make it more manageable.

I want my component to have a function passed in as a parameter to it. The function takes a string parameter and it is called with different names from the component when a button is clicked.

I've tried some answers I found online but they don't seem to work(Like binding events)

Code

I have a component named ItemMenu.razor and my index page Index.razor.

ItemMenu.razor:


<button @onclick="() => OnClickFunction.Invoke(foo)">foo</button>
<button @onclick="() => OnClickFunction.Invoke(ping)">ping</button>

@code {
    [Parameter] public Action<string> OnClickFunction { get; set; }

    const string foo = "bar";
    const string ping = "pong";
}

Index.razor:

@page "/"

<h1>This is my index page!</h1>

<ItemMenu OnClickFunction="ShowName" /> @*Error: No overload for 'ShowName' matches delegate 'Action'*@

@code {
    public void ShowName(string myName)
    {
        Console.WriteLine(myName);
    }
}


Error

Line 3 in Index.html throws the error No overload for 'ShowName' matches delegate 'Action'

Solution

Turns out I just needed to rename my variable to OnClick instead of OnClickFunction...

Ok @enet pointed out the variable name wasn't the problem. In blazor we should use the EventCallback and not Action.

Turns out the real problem in my case was Visual Studio as I found this solution earlier when looking at other answers. VS decided to show an error on this line even though it compiles and runs fine...


Solution

  • Note: The issue is not the naming of the function as you've expressed in your comment to Brian. The issue is that you use the Action delegate, but an Action delegate does not return a value, or rather it returns void. You could have used the Func delegate instead, so that you could invoke your method and return a value. However, the correct way in Blazor is to use the EventCallback struct, which produces the suitable delegate for you. It also has other benefits.

    ItemMenu.razor

    <button @onclick="() => OnClickFunction.InvokeAsync(foo)">foo</button>
        <button @onclick="() => OnClickFunction.InvokeAsync(ping)">ping</button>
        
        @code {
            [Parameter] public EventCallback<string> OnClickFunction { get; set; }
        
            const string foo = "bar";
            const string ping = "pong";
        }
    

    Index.razor

    @page "/"
    
    <h1>This is my index page!</h1>
    
    <ItemMenu OnClick="ShowName" /> @*Error: No overload for 'ShowName' matches delegate 'Action'*@
    
    @code {
        public void ShowName(string myName)
        {
            Console.WriteLine(myName);
        }
    }