Description
Hi,
I've recently been looking at using blazor to render Markdown content as part of a blog.
Using the ChildContent which contains the markdown syntax and at the same time render any sub components inside the markdown.
It turns out this is possible (and pretty awesome)
But the code is a little sketchy in certain places
The question I've got is, is there a better way to do this in so far as the RenderTreeBuilder Array?
Such as a function call which sets rather than Adds a frame.
That way I could just ChildContent(destbuilder);
Then override the frames I want with a set function to turn them from markdown syntax into html.
I came across this but it didn't seem of much help
#12415
Perhaps there's a different / better way I should be doing this, or perhaps some API method I'm not familiar with.
Markdown.cs
using Markdig;
using Ganss.XSS;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
namespace BlogFace.Components.Markdown {
/// <summary> Blazor component for rendering markdown using Markdig. </summary>
public class Markdown : ComponentBase {
/// <summary> The Markdig pipeline. </summary>
/// <value> The Markdig pipeline. </value>
[Parameter]
public MarkdownPipeline MdPipeLine { get; set; }
/// <summary> The child content. </summary>
/// <value> The child content. </value>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary> Service used to sanitize the html. </summary>
/// <value> The HTML sanitizer. </value>
[Inject]
public IHtmlSanitizer HtmlSanitizer { get; set; }
/// <summary> On initial Setup </summary>
protected override void OnInitialized() {
base.OnInitialized();
if (MdPipeLine == null) {
MdPipeLine = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
}
}
/// <summary> Convert markdown string to html. </summary>
/// <param name="value"> The string value to render. </param>
/// <returns> The string converted to html. </returns>
protected string ConvertMarkdownToString(string value) {
if (!string.IsNullOrWhiteSpace(value)) {
// Convert markdown string to HTML
var html = Markdig.Markdown.ToHtml(value, MdPipeLine);
// Sanitize HTML before rendering
var sanitizedHtml = HtmlSanitizer.Sanitize(html);
// Return sanitized HTML
return sanitizedHtml;
}
return "";
}
/// <summary> Builds the render tree. </summary>
/// <param name="destbuilder"> The destbuilder. </param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "BL0006:Do not use RenderTree types", Justification = "<Pending>")]
protected override void BuildRenderTree(RenderTreeBuilder destbuilder) {
base.BuildRenderTree(destbuilder);
// We use this to extract the text from the ChildContent so we can pass it to Markdig
var srcbuilder = new RenderTreeBuilder();
ChildContent(srcbuilder);
// Get the Frames
var frames = srcbuilder.GetFrames();
var framearr = frames.Array;
var framecount = frames.Count;
// Loop over the frames rendered from ChildContent
for (int i = 0; i < framecount; i++) {
var frame = framearr[i];
// Assume Markup is Markdown syntax and render it via Markdig
if (frame.FrameType == RenderTreeFrameType.Markup) {
var src_content = frame.MarkupContent;
destbuilder.AddMarkupContent(i, ConvertMarkdownToString(src_content));
} else {
// Add other sub components
var destframes = destbuilder.GetFrames();
// This part is kind of sketchy but it works
// Is there a better way to do this?
// make sure the array counter is incremented internally.
destbuilder.AddMarkupContent(i, "");
// move the reference over from the ChildContent
destframes.Array[i] = framearr[i];
}
}
}
}
}
An Example of usage below
Note TestComponent1 can be anything
it's just an example of embedding a blazor component in the middle of the markdown text.
Works fine with code blocks too.
Example1.razor
<Markdown>
# Test Header1
## Test Header2
This is some more content, hello world
<TestComponent1>
</TestComponent1>
# Test Header3
## Test Header4
This is some more content, hello world
</Markdown>