Blazor按钮,使用父组件@onclick

7

在使用@onclick父组件方法时,是否可以直接使用,还是需要从子组件中调用?

假设我想调用父组件方法Foo()。

父组件

@page "/"

// Custom component button
<Button Text="Some text" @onclick="Foo" Apperance="@Style.secondary" /> 


@code {
    async Task Foo() 
    {
        // ...
    }
}

子元素

<button class="btn @_cssClass"><span>@Text</span></button>

@code {
    public enum Style
    {
        primary,
        secondary,
        tertiary,
        danger
    }

    [Parameter]
    public string Text { get; set; }
    [Parameter]
    public Style Apperance { get; set; }
    private string _cssClass { get; set; }

    protected override void OnInitialized()
    {
        _cssClass = Apperance switch
        {
            Style.secondary => "btn--secondary",
            Style.tertiary => "btn--tertiary",
            Style.danger => "btn--danger",
            _ => "btn--primary"
        };
    }
}

我遇到了这个错误:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Object of type 'Tidrapport.Client.Components.Button' does not have a property matching the name 'onclick'.
System.InvalidOperationException: Object of type 'Tidrapport.Client.Components.Button' does not have a property matching the name 'onclick'.
我确实需要一个子按钮组件,并且当它在父组件中被点击时,我希望调用父组件中的方法。
4个回答

8

我已经找到了我的问题的答案,你需要设计一个在文档中找到的EventCallback

对我来说,看起来是这样的。

父组件

@page "/"

// Custom component button
<Button Text="Some text" OnClickCallback="@Foo" Apperance="@Style.secondary" /> 

@code {
    async Task Foo() 
    {
        // ...
    }
}

子元素

<button class="btn @_cssClass" @onclick="OnClickCallback"><span>@Text</span></button>

@code {
    public enum Style
    {
        primary,
        secondary,
        tertiary,
        danger
    }

    [Parameter]
    public string Text { get; set; }
    [Parameter]
    public Style Apperance { get; set; }
    private string _cssClass { get; set; }
    [Parameter]
    public EventCallback OnClickCallback { get; set; } // this is where to you pass the parent function to be executed as a callback

    protected override void OnInitialized()
    {
        _cssClass = Apperance switch
        {
            Style.secondary => "btn--secondary",
            Style.tertiary => "btn--tertiary",
            Style.danger => "btn--danger",
            _ => ""
        };
    }
}

因此,onclick事件绑定到您在子组件中设计的事件处理程序。 如果您想要使用参数,则可以使用T类型,关于此更多信息请参阅文档。


6
你可以使用属性扩展,这样你就可以继续使用Razor的@onclick属性,而不需要声明一个回调函数。
只需在你的子类中添加以下内容:
  1. 一个捕获未匹配值的参数
  2. 在子元素上添加@attributes Razor属性。
例如,创建一个名为Button.razor的文件,内容如下:
<button class="btn" @attributes="AdditionalAttributes">@Text</button>

@code {

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

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> AdditionalAttributes { get; set; }
}

然后你可以这样调用它:
<Button Text="Some text" @onclick="Foo" />

请注意,这将应用所有未匹配的属性,而不仅仅是 @onclick,并且会覆盖在其之前声明的任何属性。在某些情况下,这可能不是理想的做法,此时您可以使用 EventCallback

我认为这是唯一正确的答案,假设您不需要在回调中使用任何自定义参数或其他内容。 - Napoleon
文档的链接已经不是最新的了。属性展开不再是“组件”页面上的一个部分,而是有自己的页面:https://learn.microsoft.com/en-us/aspnet/core/blazor/components/splat-attributes-and-arbitrary-parameters - Dan Z
@Napoleon - 为什么你认为这是“唯一”正确的答案?当然,它可以工作,但接受的答案也可以,并且编码量相同。我个人更喜欢EventCallback方法,因为它更直观,并且通过坚持使用单一方法来保持我的代码统一,无论回调是否需要参数。 - Dan Z

3
您不能以这种方式使用 @onclick 指令... 此指令用于HTML元素,指示编译器为单击事件创建事件处理程序,例如:
<button type="button" @onclick="ClickEventHandler">Click me</button>

@code{
   private void ClickEventHandler()
   {
      Cosole.WriteLine("Hello Blazor");
    }
}

当您绑定组件时,应该像这样操作:

ChildComponent.razor

<input type="text" @bind="Text" @bind:event="oninput" />

@code {
        private string text { get; set; }

        [Parameter]
        public string Text
        {
            get { return text; }
            set
            {
                if (text != value) {
                    text = value;
                    if (TextChanged.HasDelegate)
                    {
                        TextChanged.InvokeAsync(value);
                    }
                }
            }
        }

        [Parameter]
        public EventCallback<string> TextChanged { get; set; }
    }

如您所见,我定义了一个名为“Text”的属性参数... 为了创建双向数据绑定,您还应该定义一个EventCallback '委托'来更新父组件中绑定的对象。因此,在父组件中,您将编写以下代码:

<ChildComponent @bind-Text="Text" />

请注意,EventCallback委托的名称由Text单词加上Changed单词组成。
指令@bind-Text将子组件中定义的Text属性绑定到父组件中定义的Text属性(参见下面的代码)。
因此,规则是@bind-加上子组件中属性的名称,例如Value变为@bind-Value,它的值应该是一个属性的名称,比如MyValue,因此: @bind-Value="MyValue" 但在子组件中,您应该定义名为Value的参数属性和一个名为ValueChanged的EventCallback...
以下是完整的工作代码片段,演示了父组件与其子组件之间的双向数据绑定:

ParentComponent.razor

@page "/ParentComponent"

<h1>Parent Component</h1>

<input type="text" @bind="Text" @bind:event="oninput" />
<p></p>

<ChildComponent @bind-Text="Text" />


@code {
    [Parameter]
    public string Text { get; set; } = "Hello Blazor";
}

希望这能够正常工作...


谢谢,但这是使用字符串的双向数据绑定,不是我想要的。也许我表达不够清楚。所以,我确实想要一个子按钮组件,并且当它被点击时 - 我想在父组件中调用一个方法。 - Henkolicious

2
所以,我需要一个子按钮组件,当它被点击时 - 我想在父组件中调用一个方法。
您的ChildComponent应该如下所示:
<button type="button" @onclick="CallMethodParent">Call a method on parent component</button>


    @code {
    
        [Parameter]
        public EventCallback<string> ParentMethod { get; set; }
    
        private async Task CallMethodParent()
        {
            if(ParentMethod.HasDelegate)
            {
              await  ParentMethod.InvokeAsync("A message from child component...");
            }
        }
    
    }

使用方法

<p>@message</p>

<ChildComponent ParentMethod="ParentMethod"/>

@code{
    private string message;

    private void ParentMethod(string message)
    {
        this.message = message;
    }

}

注意:从中可以得出的主要内容是,您应该在子组件中定义一个参数EventCallback属性,该属性存储在父组件中定义的方法。希望这可以帮助到您...

再次感谢您的评论,我在找到解决方案后才看到它。 - Henkolicious

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接