2
votes

I have a templated component (a tooltip) that has a parameter and passes that parameter in context to child content. That parameter is a wrapper for ElementReference. The purpose of this is to get back to the tooltip the child's reference, once it is set. What I want to do is to store a particular instance of that tooltip component in a reusable RenderFragment in several places. But I get the error The name 'context' does not exists in the current context.


This is original question, but it proved to be oversimplified. Please go to second separation line, with sample more resembling my situation.

Here is a sample (not that tooltip, but simplified code that has exactly the same problem).
Templated component RenderFragTempl:

@inherits ComponentBase

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

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

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

and the call in 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>;

}


EDIT: 1
I have a tooltip component that has a child content. Tooltip will be shown whenever the mouse hovers over that child content. But tooltip does not wrap the child content with anything. So if you check out the source, you will only see the child content without any indication of the tooltip. The moment mouse cursor hovers over the child content, the tooltip container is added to the bottom of the page and is position right over that child content. This is quite problematic to achieve in blazor because the tooltip needs to have the reference to the child content. But references are established after parameters are filled. So a special wrapper is used to achieve that and Tooltip is built as a templated component.

So the wrapper for ElementReference I use (curtesy of 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;
    }

My simplified Tooltip as RenderFragTempl (just the important bits)

@inherits ComponentBase

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

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

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

} 

And my 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>;

}

To answer the question - why I am trying to use RenderFragment - imagine I have a collection of cards - let's say 150. The card component accepts as a parameter IList<RenderFragment> for rendering buttons on a card. I want to pass my icon with tooltip to that card. I need to have access to Tooltip's context.

I tried renaming context to something else, but I get the same error (except in the error there is new context name).

2

2 Answers

1
votes

Why define a templated component and use it like that ?

However, here's code sample that demonstrates how to get the same required result without defining a templated component:

@test(12)

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

}

But if you insist, here's the code that demonstrate how to render your component:

@test((10001, 15))

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

 }

Update:

The following code describes how to render the templated component with the internal variable context. You can improve on it as you like.

Note: I did not read your update... I just do what you requested on the original question, but this time using context.

@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();

        };

}

Note that when you invoke the RenderFragment delegate you pass it a value which is assigned to the Ref parameter, and is passed in the form the the internal context variable

1
votes

I asked this question in https://github.com/dotnet/aspnetcore/issues/29671 and I got an answer. This is not exactly what I wanted, but that seems to be official:

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