'Blazor RenderFragment to String

I'm developing a code block component using .Net 6 Blazor wasm. I need to display the RenderFragment as string and also render the component in my html.

Here is my code block component,

<pre class="language-html">
    <code class="language-html">
        @("Some way to get non rendered html from @ChildContent")
    </code>
</pre>

@ChildContent

@code
{
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

I'm using the above component as,

<CodeBlock>
    <Chat></Chat>
</CodeBlock>

Expected Output:

<Chat></Chat> 
<!-- This is the input I passed inside as RenderFragment and I need the exact Render Fragement Code; 
not the Rendered Html code of the RenderFragment -->

Chat Component

Component gets rendered as expected. But unable to get non rendered html of my RenderFragment

One option is to pass the RenderFragment content as string parameter to CodeBlock component. But this results in duplicate and non readable HTML. Also this becomes difficult to maintain when the ChildContent has multiple lines of code.

<CodeBlock Html="@("<Chat></Chat>")">
    <Chat></Chat>
</CodeBlock>

Any hints/suggestions on how to achieve this?



Solution 1:[1]

This is kind of a tough question. From what I understand you want to be able to render the ChildContent of your component but also be able to view the ChildContent data as plaintext razor code, or HTML if that's what's in your ChildContent, right?

So an option would be to surround your @ChildContent renderer tag with a <div> and assign that div a unique id when the parent component is initialized. Then you create another variable, let's call it private string RawContent { get; set; }. Then write a javascript function that takes an id value and gets the element by id from the DOM and returns the element's innerHTML.

I created a test project to try it and it works. Here are the snippets that are relevant.

In your Component.razor file:

@inject IJSRuntime JS
<pre class="language-html">
    <code class="language-html">
        @RawContent
    </code>
</pre>

<div id="@ElementId">
    @ChildContent
</div>
@code
{
    [Parameter]
    public RenderFragment ChildContent { get; set; }
    private ElementReference DivItem { get; set; }
    private string RawContent { get; set; }
    private Guid ElementId { get; set; } = Guid.NewGuid();

    protected async override Task OnInitializedAsync()
    {
        base.OnInitializedAsync();

        RawContent = await JS.InvokeAsync<string>("GetElementHtmlText", ElementId.ToString());
    }
}

Then in your index.html file add this in side of a <script> tag, or to one of your javascript files:

async function GetElementHtmlText(elementID) {
            await setTimeout(() => {  }, 1);
            console.log(elementID);
            var element = document.getElementById(elementID);
            console.log(element);
            var result = element.innerHTML;
            console.log(result);
            return result;
        }

Then anywhere you wish to use this component you will render the HTML markup and have the Raw Text available as well. However this function will return all of the html markup raw text, meaning that you get the razor tags and the empty comment elements that Blazor inserts as well (ie. <!--!-->). But as long as you know that you can work around them.

I didn't do this but you could modify this to call the JSInterop function inside the RawContent's getter instead of just when the component initializes.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 JG222