conficient / BlazorTemplater

A library that generates HTML (e.g. for emails) from Razor Components
Apache License 2.0
146 stars 16 forks source link

Possible issue with awaited data calls in rendered component #23

Closed aterbo closed 2 years ago

aterbo commented 2 years ago

Hello,

I have a component with an inject data service that makes some awaited calls to the Db to gather data and render it. When running it through BlazorTemplater, it seems to be missing all of the data that is being pulled in during the awaited call. Everything else pulls in fine.

The ComponentRenderer works great if I modify my component to make a "dummy component" that takes all of the data in as parameters without any awaited calls in the lifecycle. I can still have synchronous code that is executed and the output is correct.

Am I missing something, or is there something special that needs to be done to have the renderer handle async data calls?

Here is my calling code:

            htmlString = new ComponentRenderer<IssueReportEmail>()
                            .AddService(ApplicationDataService)
                            .Set(c => c.Issues, issues)
                            .Set(c => c.ReviewDate, review.Reviewed)
                            .Set(c => c.EmailList, emailList)
                            .Render();
conficient commented 2 years ago

Hmm, yes had not thought about async operations in the render process.. I'll have to do some investigation

conficient commented 2 years ago

Looking at how this would work. bUnit has something to handle this - will see if I can adapt this

aterbo commented 2 years ago

Good deal, thank you for looking into it and working on a solution. If there's something I can do to help you out, let me know.

conficient commented 2 years ago

I had a look at this, but wasn't sure there was an easy way to handle async method calls within the templater.

It would require a system to flag when async events had completed to then handle a re-render, which you'd have to code for and handle. The handling of async in Blazor components in a Web UI context is logical (indeed, required!), and for bUnit it also makes sense as you'd want to test handling of async events and how this affects re-renders.

For a one-off HTML renderer it makes no sense: we don't want to re-render the component several times, we want to render it once and get the HTML. I think the solution for BlazorTemplater is that any async work that is required to render the component should be done before the render is executed and just the results passed in.

In your example, it would mean not passing in the service, but getting the data first, and passing the result to the component to render with, e.g.

// make the async call to get the required data
 var data = await ApplicationDataService.GetSomeDataAsync();
// render the component, pass the actual data
 htmlString = new ComponentRenderer<IssueReportEmail>()
                            .Set(c => c.Data, data)
                            .Set(c => c.Issues, issues)
                            .Set(c => c.ReviewDate, review.Reviewed)
                            .Set(c => c.EmailList, emailList)
                            .Render();

Hope that makes sense and you're able to use this workaround.