如何在RenderFragment中定义带有@context的模板化组件

5

我有一个模板组件(工具提示),它有一个参数,并将该参数作为上下文传递给子内容。该参数是ElementReference的包装器。其目的是在子元素引用被设置后,将子元素的引用传回到工具提示中。 我想要做的是将该工具提示组件的特定实例存储在多个位置中可重用的RenderFragment中。 但是我遇到了错误:The name 'context' does not exists in the current context


这是原始问题,但它被证明是过于简单了。请转到第二个分隔线,其中示例更类似于我的情况。

这里有一个示例(不是那个工具提示,而是具有完全相同问题的简化代码)。
模板组件RenderFragTempl

@inherits ComponentBase

<span id="@(Ref)">    
    @IntChildContent(Ref)
</span>

@code {
    [Parameter] public RenderFragment<int> IntChildContent { get; set; }

    [Parameter] public int Ref { get; set; }
} 

Index.razor中的调用:

@page "/"
@using BlazorServer.Pages.StackOverflow

<RenderFragTempl Ref="10">
    <IntChildContent>
        <p>@context</p>
    </IntChildContent>
</RenderFragTempl>
<br />
@test

@code {
    //compiler error CS0103: The name 'context' does not exist in the current context
    private readonly RenderFragment test = @<RenderFragTempl Ref="10001">
        <IntChildContent>
            <p>@context</p>
        </IntChildContent>
    </RenderFragTempl>;

}


编辑:1
我有一个提示框组件,它有一个子元素。每当鼠标悬停在该子元素上时,提示框就会显示出来。但是提示框没有用任何东西包裹子元素。所以如果你查看源代码,你只会看到不带提示框任何指示的子内容。当鼠标悬停在子元素上时,提示框容器将被添加到页面底部,并位于该子元素正上方。这在Blazor中实现起来非常棘手,因为提示框需要引用子内容。但是引用是在填充参数之后建立的。因此,使用特殊的包装器来实现,Tooltip被构建为模板化组件。

因此,我使用的ElementReference的包装器(MatBlazor提供):

    public class TargetForwardRef
    {
        private ElementReference _current;
        public ElementReference Current
        {
            get => _current;
            set
            {
                Set(value);
                //this is just for debugging purpose
                Console.WriteLine($"Ref: {value.Id ?? "null"}"); 
            }
        }
        public void Set(ElementReference value) => _current = value;
    }

我的简化版 Tooltip 作为 RenderFragTempl(仅包含重要部分)

@inherits ComponentBase

<span>            
    @UnboundChildContent(RefBack)
</span>

@code {
    [Parameter] public RenderFragment<TargetForwardRef> UnboundChildContent { get; set; }

    [Parameter] public TargetForwardRef RefBack { get; set; } = new TargetForwardRef();

} 

我的index.razor


@page "/"
@* 
//this is working, will printout to console `<span>` reference id, you will be 
//able to find it once you go to source; I added this here for reference 
*@
<RenderFragTempl>
    <UnboundChildContent>
        <span @ref="@context.Current">Span content</span>
    </UnboundChildContent>
</RenderFragTempl>
<br/>
@test

@code {
    //compiler error CS0103: The name 'context' does not exist in the current context
    private readonly RenderFragment test = 
    @<RenderFragTempl>
        <UnboundChildContent>
            <span @ref="@context.Current">Hover to show tooltip</span >
        </UnboundChildContent>
    </RenderFragTempl>;

}


为了回答这个问题——为什么我试图使用RenderFragment——想象一下我有一系列的卡片——假设是150张。卡片组件接受一个类型为IList<RenderFragment>的参数,用于在卡片上呈现按钮。我想把我的带有工具提示的图标传递给那张卡片。我需要访问工具提示的上下文。
我尝试将上下文的名称更改为其他名称,但我得到了同样的错误(除了错误信息中出现了新的上下文名称)。
2个回答

4

为什么要定义一个模板化组件并像这样使用它?

然而,下面的代码示例展示了如何在不定义模板化组件的情况下获得相同所需的结果:

@test(12)

@code {
    
    private RenderFragment<int> test => (value) => (__builder) =>
    {
        <span id="@(value)">
            @value
        </span>
    };

}

但如果您坚持,这里是演示如何渲染组件的代码:
@test((10001, 15))

@code 
{
    private RenderFragment<(int, int)> test => (value) => (__builder) =>
    {
        <RenderFragTempl Ref="@value.Item1">
            <IntChildContent>
                <p>@value.Item2</p>
            </IntChildContent>
        </RenderFragTempl>;
   
    };

 }

更新:

以下代码描述了如何使用内部变量上下文呈现模板化组件。您可以根据需要进行改进。

注意:我没有阅读您的更新... 我只是按照原始问题的要求执行,但这次使用上下文。

@test(121)

@code {
    
    private RenderFragment<int> test => (value) => (__builder) =>
        {
        __builder.OpenComponent<TemplatedComponent>(0);
        __builder.AddAttribute(1, "Ref", value);
        __builder.AddAttribute(2, "IntChildContent", 
            (RenderFragment<int>)((context) => (__builder2) =>
            {
                __builder2.OpenElement(3, "p");
                __builder2.AddContent(4, context);
                __builder2.CloseElement();
            }
            ));
            __builder.CloseComponent();

        };

}

请注意,当您调用RenderFragment委托时,您将向其传递一个值,该值分配给Ref参数,并以内部上下文变量的形式传递。

我有一种感觉我把它过于简单化了。正如我所说 - 我有一个需要引用其子元素的工具提示(它不会用任何元素包装子内容)。因此,基本上重要的是获取工具提示模板的上下文以供子内容使用。有一种情况是整个页面将被填充使用相同的工具提示(具有相同的提示)。我真的需要访问那个上下文...我会修改我的问题 - 希望它能更好地反映我的需求。 - mikeyy
抱歉,但新的更改需要一个新的问题。请接受我的答案,并创建一个新的问题,我会在那里尽力帮助... - enet
1
问题仍然是一样的。我仍在尝试使用上下文。问题的核心没有改变,只是简化了(虽然比原来更复杂)示例。非常感谢您的回答,但它并没有回答我的直接问题。您提供了一个解决我过于简化的示例的方法,但没有回答我关于如何使用上下文的问题。再次强调,我真的很感激您的意见。 - mikeyy
请参见我回答中的更新部分。 - enet
谢谢!实际上我昨天自己想出了同样的解决方案。甚至在这里发布了 https://github.com/ant-design-blazor/ant-design-pro-blazor/pull/68 - 我真的希望能得到更简洁的解决方案,可能不需要直接使用RenderTreeBuilder。我将在github.com/dotnet/aspnetcore上提出同样的问题,也许有些我们不知道的东西。如果我得到相同的答案(或类似的答案),我会将您的答案标记为已接受。再次感谢您的帮助! - mikeyy
恢复了我的答案...很高兴能够接受它。 - enet

2

我在https://github.com/dotnet/aspnetcore/issues/29671上提出了这个问题,得到了一个答案。虽然不完全符合我的要求,但似乎是官方的:

    private readonly RenderFragment<TargetForwardRef> test => context => 
    @<RenderFragTempl>
        <UnboundChildContent>
            <span @ref="@context.Current">Hover to show tooltip</span >
        </UnboundChildContent>
    </RenderFragTempl>;


"我真的需要访问那个上下文", "我仍在尝试使用这个上下文": 你的上下文不是由渲染引擎产生的内部上下文,它只是传递给Razor模板的一个参数。 - enet

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