具有继承性的 Blazor 模板组件
Blazor templating component with inheritance
我有一个基本组件 PetTemplate
和第二个 PetDog
继承并使用 PetTemplate
的模板。 PetTemplate
有一个名为 ToggleDisplay
的方法。我的目标是当我单击 Index
页面上的按钮时调用 PetDog.ToggleDisplay
方法和 show/hide 页面上的 PetDog 详细信息。
下面示例代码中的“内部”按钮有效,但“外部”按钮无效。如何从页面或父组件正确调用 ToggleDisplay
方法?
Index.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 组件的引用,然后尝试在您没有引用的对象(PetTemplate 的实例)上调用派生方法 dog.ToggleDisplay()。为了使其工作,您必须获得对父组件 (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);
}
}
Index.razor
@page "/"
<button @onclick="ShowPetDetails">Show Details (Outside)</button>
<PetDog @ref="dog" />
@code {
PetDog dog;
void ShowPetDetails()
{
dog.PetTemplateProp.ToggleDisplay();
}
}
注意:尽管 Razor 组件是 C# 类,但您不能将它们视为普通组件 类。他们的行为不同。例如,您不能在组件外部定义变量实例并设置其参数等。充其量,您可以捕获对组件的引用并在组件实例上调用 public 方法,就像在当前示例中所做的那样。简而言之,组件对象不同于普通的 类.
同样重要的是要记住,每个组件都是一个单独的岛,可以独立于其父项和子项进行渲染。
But just wondering how can I change a component parameter value from outside of it, that inherited/uses a template. I tried the methods in the documentation or the resources I found, but it didn't work for my case
您不应该(这是一个警告)并且可能不能(现在可能是一个错误)在组件之外更改组件参数的值。例如,您不能捕获对组件的引用并为其参数赋值 属性:
<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 功能时,这是一种更简洁的方法:
- 在基础组件中声明一个事件,该事件在 UI 状态从组件内部更改时引发。还让控制状态的变量成为参数。在你的情况下,像
PetTemplate.razor
:
[Parameter]
public EventCallback OnToggleRequested {get;set;}
[Parameter]
public string Display {get;set;}
protected async Task RaiseToggle()
{
await OnToggleRequested.InvokeAsync();
}
- 在您的 PetDog 中,在引发内部点击时简单地调用切换方法
PetDog.razor
:
<button @onclick="RaiseToggle">Show Details (Inside)</button>
- 在您的容器中(在本例中为 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";
}
请注意,事件可以在层次结构级别使用,您不需要在任何地方捕获任何引用。
我有一个基本组件 PetTemplate
和第二个 PetDog
继承并使用 PetTemplate
的模板。 PetTemplate
有一个名为 ToggleDisplay
的方法。我的目标是当我单击 Index
页面上的按钮时调用 PetDog.ToggleDisplay
方法和 show/hide 页面上的 PetDog 详细信息。
下面示例代码中的“内部”按钮有效,但“外部”按钮无效。如何从页面或父组件正确调用 ToggleDisplay
方法?
Index.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 组件的引用,然后尝试在您没有引用的对象(PetTemplate 的实例)上调用派生方法 dog.ToggleDisplay()。为了使其工作,您必须获得对父组件 (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);
}
}
Index.razor
@page "/"
<button @onclick="ShowPetDetails">Show Details (Outside)</button>
<PetDog @ref="dog" />
@code {
PetDog dog;
void ShowPetDetails()
{
dog.PetTemplateProp.ToggleDisplay();
}
}
注意:尽管 Razor 组件是 C# 类,但您不能将它们视为普通组件 类。他们的行为不同。例如,您不能在组件外部定义变量实例并设置其参数等。充其量,您可以捕获对组件的引用并在组件实例上调用 public 方法,就像在当前示例中所做的那样。简而言之,组件对象不同于普通的 类.
同样重要的是要记住,每个组件都是一个单独的岛,可以独立于其父项和子项进行渲染。
But just wondering how can I change a component parameter value from outside of it, that inherited/uses a template. I tried the methods in the documentation or the resources I found, but it didn't work for my case
您不应该(这是一个警告)并且可能不能(现在可能是一个错误)在组件之外更改组件参数的值。例如,您不能捕获对组件的引用并为其参数赋值 属性:
<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 功能时,这是一种更简洁的方法:
- 在基础组件中声明一个事件,该事件在 UI 状态从组件内部更改时引发。还让控制状态的变量成为参数。在你的情况下,像
PetTemplate.razor
:
[Parameter]
public EventCallback OnToggleRequested {get;set;}
[Parameter]
public string Display {get;set;}
protected async Task RaiseToggle()
{
await OnToggleRequested.InvokeAsync();
}
- 在您的 PetDog 中,在引发内部点击时简单地调用切换方法
PetDog.razor
:
<button @onclick="RaiseToggle">Show Details (Inside)</button>
- 在您的容器中(在本例中为 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";
}
请注意,事件可以在层次结构级别使用,您不需要在任何地方捕获任何引用。