Reputation: 141
I'm trying to get the generate the pdf in server-side Blazor. I use DinkToPdf as an external library to convert HTML string to pdf. But I'm having trouble of converting the blazor component to HTML string.
There is a way to render Razor templates to a string by using the Razor ViewEngine. From this web http://fizzylogic.nl/2017/08/03/how-to-generate-pdf-documents-in-asp-net-core/
[HttpGet]
public async Task<IActionResult> CreatePDF()
{
var globalSettings = new GlobalSettings
{
ColorMode = ColorMode.Color,
Orientation = Orientation.Portrait,
PaperSize = PaperKind.A4,
Margins = new MarginSettings { Top = 10 },
DocumentTitle = "PDF Report",
};
var objectSettings = new ObjectSettings
{
PagesCount = true,
HtmlContent = "<h>Hello World</h>",
WebSettings = { DefaultEncoding = "utf-8"},
HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true },
FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" }
};
var pdf = new HtmlToPdfDocument()
{
GlobalSettings = globalSettings,
Objects = { objectSettings }
};
var file = _converter.Convert(pdf);
return File(file,"application/pdf");
}
I need to modify the ObjectSettings.HtmlContent to be my blazor component html string.
Upvotes: 7
Views: 3742
Reputation: 353
I had to dig through some of the source code and this is what I found that works for static content (RenderMode.Static
) (I haven't tested this with script tags or layout hierarchies or nested components).
Depending on where you are trying to do this (i.e. within a request vs background task), you will either need to supply the current HttpContext
or create your own.
public static async Task<string> RenderAsync<TComponent>(IHtmlHelper helper, HttpContext httpContext, object parameters)
{
if (helper is IViewContextAware viewContextAware)
{
viewContextAware.Contextualize(new ViewContext()
{
HttpContext = httpContext
}
}
var content = await helper.RenderComponentAsync<TComponent>(RenderMode.Static, parameters);
var writer = new StringWriter();
content.WriteTo(writer, HtmlEncoder.Default);
return writer.ToString();
}
public static HttpContext CreateDefaultContext(IServiceProvider serviceProvider)
{
return new DefaultHttpContext
{
RequestServices = serviceProvider,
Request =
{
Scheme = "http",
Host = new HostString("localhost"),
PathBase = "/base",
Path = "/path",
QueryString = QueryString.FromUriComponent("?query=value")
}
}
}
CreateDefaultContext
is loosely based off of this sample found in Microsoft's tests
Sample.razor
:
<h3>@Data.Title</h3>
Hello, @Data.FirstName @Data.LastName!
@code {
[Parameter]
public Test Data { get; set; }
public class Test
{
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
Demo:
var helper = ServiceProvider.GetService<IHtmlHelper>();
var context = CreateDefaultContext(ServiceProvider);
var html = await RenderAsync<Sample>(helper, context, new
{
Data = new Sample.Test()
{
Title = "Stack Overflow Test",
FirstName = "user",
LastName = "489566"
}
});
html value:
<h3>Stack Overflow Test</h3>
Hello, user 489566!
Upvotes: 0
Reputation: 23264
Are you trying to convert content the user can see? If so, you could add @ref='ContentToRender'
to the component you want to render, Blazor will then assign that reference after the component is rendered:
@inject IJSRuntime JSRuntime
<div @ref=ContentToRender>
... Your content here...
</div>
@code {
ElementReference ContentToRender;
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
string html = await JSRuntime.InvokeAsync<string>("BlazorUniversity.getInnerHTML", ContentToRender);
}
};
}
Where the JS would look something like this
var BlazorUniversity = BlazorUniversity || {};
BlazorUniversity.getInnerHTML = function(element) {
return element.innerHTML;
};
Don't forget to include the JS in your main index.html or _Host.cshtml page.
For more info see Passing HTML Element References on Blazor University.
Note that you will not be able to access the content until at least after the first render event (firstRender == true).
Upvotes: 2
Reputation: 1
This works for me:
...... HtmlContent = TemplateGenerator.GetHTMLString(), ......
public static string GetHTMLString()
{
IRaumNodeProvider RaumNodeProvider = new
RaumNodeProvider(Globals.Connectionstring);
var reservierungen = RaumNodeProvider.GetReservierungen();
var sb = new StringBuilder();
sb.Append(@"
<html>
<head>
</head>
<body>
<div class='header'><h1>Reservierungsliste</h1></div>
<table align='center'>
<tr>
<th>Id </th>
<th>Raum </th>
<th>Datum </th>
<th>Zeit </th>
<th>Beleger </th>
<th>Belegung</th>
<th>EmailMsg</th>
</tr>");
foreach (var res in reservierungen)
{
sb.AppendFormat(@"<tr>
<td>{0}</td>
<td>{1}</td>
<td>{2}</td>
<td>{3}</td>
<td>{4}</td>
<td>{5}</td>
<td>{6}</td>
</tr>",
res.Res_Id
, res.RaumName
, res.BelegungsDatumTxt
, res.Zeit
, res.Belegung_durch
, res.Belegung
, res.EmailMsg
);
}
sb.Append(@"
</table>
</body>
</html>");
return sb.ToString();
}
Upvotes: 0