带有继承的Blazor模板组件



我有一个基本组件PetTemplate和第二个继承并使用PetTemplate模板的PetDogPetTemplate有一个名为ToggleDisplay的方法。我的目标是单击Index页面上调用PetDog.ToggleDisplay方法的按钮,并在页面上显示/隐藏PetDog详细信息。

";"内部";下面示例代码中的按钮起作用;外部";按钮不要。如何从页面或父组件正确调用ToggleDisplay方法?

索引.razor

@page "/"
<button @onclick="ShowPetDetails">Show Details (Outside)</button>
<PetDog @ref="dog" />
@code {
PetDog dog;
void ShowPetDetails()
{
dog.ToggleDisplay();
}
}

PetDog.razor

@inherits PetTemplate
<PetTemplate Name="Dog">
<div>Someone's best friend!</div>
</PetTemplate>

PetTemplate.razor

<div class="mt-3">
<button @onclick="ToggleDisplay">Show Details (Inside)</button>
<h3>Pet Name: @Name</h3>
<div style="display:@display">
@ChildContent
</div>
</div>
@code {
string display = "none";
[Parameter]
public string Name { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
public void ToggleDisplay()
{
display = display == "none" ? "block" : "none";
StateHasChanged();
}
}

使用时

<PetDog @ref="dog" />
@code {
PetDog dog;
void ShowPetDetails()
{
dog.ToggleDisplay();
}
}

实际上,您创建了一个对PetDog组件的引用,然后尝试调用一个派生方法dog。ToggleDisplay((,在您没有引用的对象上(PetTemplate的实例(。为了使其工作,您必须获得对父组件(PetTemplate(的引用,并将其提供给派生组件(PetDog(,如下所示:

PetTemplate.razor

<div class="mt-3">
<button @onclick="ToggleDisplay">Show Details (Inside)</button>
<h3>Pet Name: @Name</h3>
<div style="display:@display">
@ChildContent
</div>
</div>
@code {
string display = "none";
string val;
[Parameter]
public string Name { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
public void ToggleDisplay()
{
display = display == "none" ? "block" : "none";

InvokeAsync(() => StateHasChanged());
}
}

PetDog.razor

@inherits PetTemplate
<PetTemplate @ref="petTemplate" Name="Dog">
<div>Someone's best friend!</div>
</PetTemplate>
@code
{
PetTemplate petTemplate;
public PetTemplate PetTemplateProp { get; set; }
protected override void OnAfterRender(bool firstRender)
{
if(firstRender)
{
PetTemplateProp = petTemplate;
}
base.OnAfterRender(firstRender);
}
}

索引.razor

@page "/"
<button @onclick="ShowPetDetails">Show Details (Outside)</button>

<PetDog @ref="dog" />
@code {
PetDog dog;
void ShowPetDetails()
{
dog.PetTemplateProp.ToggleDisplay();

}
}

注意:尽管Razor组件是C#类,但不能将它们视为普通类。他们的行为不同。例如,您不能在组件之外定义变量实例并设置其参数等。充其量,您可以捕获对组件的引用,并在组件实例上调用公共方法,就像在当前示例中所做的那样。简而言之,组件对象不同于普通类。

同样重要的是要记住,每个组件都是一个独立的岛,可以独立于其父母和子女进行渲染。

但我只是想知道如何从外部更改继承/使用模板的组件参数值。我尝试了文档或资源中的方法,但它不适用于我的案例

您不应该(这是一个警告(,也可能不能(现在可能是一个错误(在组件之外更改组件参数的值。例如,您不能捕获对组件的引用并为其参数属性赋值:

<PetTemplate @ref="petTemplate">
<div>Someone's best friend!</div>
</PetTemplate>
PetTemplate petTemplate; 

这是不允许的:petTemplate.Name="Dog",因为这正在更改其组件之外的参数。你只能这样做:

<PetTemplate Name="Dog">
<div>Someone's best friend!</div>
</PetTemplate>

此外,不赞成从组件内部修改参数属性(目前您应该得到一个警告,至少Steve Sanderson向Blazor团队建议这样做(。为了明确起见,您不应该在PetTemplate组件中修改参数属性Name。参数属性应该是自动属性;也就是说,有一个像这样的获取和设置访问者:[Parameter] public string Name { get; set; }你不应该这样使用它:

private string name;
[Parameter]
public string Name 
{ 
get => name;
set 
{ 
if (name != value)
{
name = value;
// Code to a method or whatever to do something
}
} 
}

这是不赞成的,因为它可能有副作用。组件参数应被视为DTO,不应进行修改。如果您希望对参数值进行一些操作,请将其复制到局部变量中,然后执行您的操作。

正如@enet-Blazor所指出的,组件继承的行为并不像人们直观预期的那样。当你想控制一个可以内部和外部控制的UI功能时,这是一种更干净的方法:

  1. 在基本组件中声明当UI状态从组件中更改时引发的事件。还让控制状态的变量成为一个参数。在你的情况下

PetTemplate.razor:

[Parameter]
public EventCallback OnToggleRequested {get;set;}
[Parameter]
public string Display {get;set;}
protected async Task RaiseToggle()
{
await OnToggleRequested.InvokeAsync(); 
}
  1. 在您的宠物狗中,当内部点击时,只需调用toggle方法

PetDog.razor:

<button @onclick="RaiseToggle">Show Details (Inside)</button>
  1. 在您的容器(在本例中为index.razor(中,监听事件并进行更改。也可以将外部按钮连接到相同的方法:

Index.razor:

<button @onclick="ToggleDisplay">Show Details (Outside)</button>

<PetDog OnToggleRequested="ToggleDisplay" Display="@display"/>

string display = "block";

void ToggleDisplay() 
{
display = display == "none" ? "block" : "none";
}

请注意,该事件可以在层次结构级别使用,您不需要在任何位置捕获任何引用。

最新更新