0

我有一个基础组件PetTemplate和一个PetDog继承并使用PetTemplate. PetTemplate有一个名为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();
    }
}

宠物狗剃须刀

@inherits PetTemplate

<PetTemplate Name="Dog">
    <div>Someone's best friend!</div>
</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();
    }
}
4

2 回答 2

2

当你使用

<PetDog @ref="dog" />

@code {
    PetDog dog;

    void ShowPetDetails()
    {
        dog.ToggleDisplay();
    }
}

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

宠物模板.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());
    }
}

宠物狗剃须刀

@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。参数属性应该是自动属性;也就是说,有一个像这样的 get 和 set 访问器:[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,不应修改。如果您希望对参数值进行一些操作,请将其复制到局部变量中,然后执行您的操作。

于 2021-11-14T22:39:46.973 回答
0

正如@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. 在您的 PetDog 中,在引发内部点击时简单地调用切换方法

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";
}

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

于 2021-11-15T07:33:59.803 回答