1

I have a simple Blazor component that take some inputs (some lists of object and few strings) and formats them into simple HTML for display, (tables generated from the lists of objects, simple text, etc.).

This HTML is a report that is intended to be both displayed to users in the app and also emailed to various people (via SendGrid). For compatibility, we are keeping the email HTML as simple as possible.

The component works fine, however I am not sure how to translate a component's markup portion into a simple string of escaped HTML so that I can pass the string to SendGrid and fire off an email.

I am aware of MarkupStrings, but I have only used them in reverse--to write a string containing HTML tags that will be properly displayed in my app. I can't find any suggestions for doing the conversion the way that I need it done.

Is there any simple way to have a component write all of its markup into a string so that I can email it out?

Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?

2

3 Answers 3

1

Is there any simple way to have a component write all of its markup into a string so that I can email it out?

No, your C# code has no simple way to do this - you could use JS Interop to get the rendered HTML from the dom, but nothing built in for it.

Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?

That is a possibility - I can't comment on the value of that to you, but it is a technique that could work if the component you are rendering is static,

Sign up to request clarification or add additional context in comments.

Comments

1

The simplest way to do that is to employ JSInterop to retrieve the Html markup for the component, produced by the browser. Let's say You've defined a child component, and you want to retrieve its html source. You can do that like this:

Define the child...

SelectGender.razor

<div id="selectGender"> <h1>Select Gender</h1> <select> @foreach (var gender in genders) { <option>@gender</option> } </select> </div> @code { private List<string> genders = new List<string> { "Male", "Female", "Other" }; } 

Usage

@page "/" @inject IJSRuntime JSRuntime <div>@((MarkupString) html)</div> <SelectGender /> <button @onclick="GetHtml">Get Html</button> @code{ private string html; protected async Task GetHtml() { html = await JSRuntime.InvokeAsync<string>("myJsFunctions.getHtml"); } } 

_Host.cshtml

<script> window.myJsFunctions = { getHtml: function () { return document.getElementById("selectGender").innerHTML; } }; </script> 

3 Comments

Thanks, enet, this definitely does the trick. To prevent my component in question from being rendered to the screen (which isn't always necessary in my case), I wrapped it in two divs, like: div style="visibility:hidden"><div id="emailBody"><ReportEmailComponent /></div></div>.
Note: The purpose of this answer is to demonstrate how to get the Html produced by the component. That is what you were looking for, right ? Displaying it in a div, as I did, was done only to show you that it works. However, you don't have to do what you did... You'll need a parent component, and a child component whose Html representation you'll read in the parent component, and then pass it to the code that send your email...
Used this solution for years, but replaced with a new answer now that we have HtmlRenderer in .Net 8 and 9.
1

I'm adding an updated answer to this using tools from later versions of .Net (8 and 9). The previous solution from @enet served well, but the new approach is done completely in C# without needing to rely on JS or browser rendering at all. Much more streamlined.

It relies on HtmlRenderer, which takes Razor component (componentType in this example) and a dictionary of all parameters. It also needs access to both your service provider and logger factory.

This is the invoking method I use throughout my apps, usable both as a <T> generic and with Type as a parameter (with some simplifications for this example).

public static async Task<string> RenderTemplate<T>(Dictionary<string, object?> parameters, IServiceProvider serviceProvider) where T : IComponent, IEmailTemplateComponent { return await RenderTemplate(typeof(T), parameters, serviceProvider); } public static async Task<string> RenderTemplate(Type componentType, Dictionary<string, object?> parameters, IServiceProvider serviceProvider) { if (!typeof(IComponent).IsAssignableFrom(componentType)) { throw new ArgumentException($"Type {componentType.Name} must implement IComponent.", nameof(componentType)); } ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); await using var htmlRenderer = new HtmlRenderer(serviceProvider, loggerFactory); return await htmlRenderer.Dispatcher.InvokeAsync(async () => { var parameterView = ParameterView.FromDictionary(parameters); var output = await htmlRenderer.RenderComponentAsync(componentType, parameterView); string htmlString = output.ToHtmlString(); return htmlString; }); } 

Here is an example method call:

string renderedEmail = await ComponentRenderer.RenderTemplate<InspectionEmailTemplate>( new Dictionary<string, object?>() { { nameof(InspectionEmailTemplate.Project), project }, { nameof(InspectionEmailTemplate.Inspection), inspection}, { nameof(InspectionEmailTemplate.SubmittingUserFullName), args.SubmittingUser}, { nameof(InspectionEmailTemplate.comment), args.comment} }, _serviceProvider); 

See docs for more details:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.htmlrenderer?view=aspnetcore-9.0

https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-components-outside-of-aspnetcore?view=aspnetcore-9.0

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.