conficient / blazortemplater Goto Github PK
View Code? Open in Web Editor NEWA library that generates HTML (e.g. for emails) from Razor Components
License: Apache License 2.0
A library that generates HTML (e.g. for emails) from Razor Components
License: Apache License 2.0
In our Blazor components, we frequently use dependency injection using the @Inject or [Inject] attributes in C# code. When creating an HTML string, we get the following error in the console:
Unhandled exception rendering component: Cannot provide a value for property 'AppState' on type 'Invoice2.Client.Pages.InvoiceView'. There is no registered service of type 'Invoice2.Shared.AppState'.
System.InvalidOperationException: Cannot provide a value for property 'AppState' on type 'Invoice2.Client.Pages.InvoiceView'. There is no registered service of type 'Invoice2.Shared.AppState'.
Otherwise the component works just fine. Is there a way around this?
The NuGet package should have a readme rather than just a simple description.
Instructions here: https://docs.microsoft.com/en-gb/nuget/nuget-org/package-readme-on-nuget-org
When you say CSS isolation is not supported, does that mean if I am using CSS isolation in my component, it will not work with the templater? So how should I provide the required CSS?
No key found when push runs
Thanks to @SQL-MisterMagoo
when i call component by this way i have this error
in TestComponent.razor i call BookComponent to get html
var sensorHtml = new ComponentRenderer() .Render();
BookComponent.razor
@rendermode InteractiveServer
other codes......
but i have this error
Cannot supply a component of type 'Components.BookComponent' because the current platform does not support the render mode 'Microsoft.AspNetCore.Components.Web.InteractiveServerRenderMode'
I'm trying to render a .razor file (email template) that contains conditionals like
<!--[if mso]>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td width="50%" valign="top">
<![endif]-->
but they don't survive through the render. Is this something that can be fixed or is there a flag to set that already does this? Any help would be great.
I have custom component and I am using Bootstrap for styling.
The library works fine but without showing bootstrap style!.
Any ideas?
Testing the library in an application I found it awkward having the renderer class name the same as the namespace. I wasn't able to add a using
for the namespace and then use the BlazorTemplater
class. I think it's recommended against in C# guidelines somewhere, so will rename.
Possible alternatives:
Need to:
renderer
variable names to templater
If a component have a baseClass that it inherits from with the @inherits from a .cs file with a cascadeParameter, then its not possible to set that parameter.
It seams that when the parameters are fetched in GetParameterName that it gets the CascadingParameterAttribute instead of ParameterAttribute.
Hello,
I am unable to figure out why I am getting a CS7036 on even the most basic model when using the Fluent Set.
Here is my model:
public class ConfirmUserEmailViewModel
{
public string Name { get; set; }
public string ConfirmUrl { get; set; }
}
Here is my razor:
<p>Welcome @Model.Name,</p>
<p>Please confirm your email by <a href='@Model.ConfirmUrl'>clicking here</a>.</p>
<p>If you prefer you can copy and paste this link into your browser of choice.</p>
<br />
<p>@Model.ConfirmUrl</p>
<br />
@code {
[Parameter] public ConfirmUserEmailViewModel Model { get; set; }
}
What am I doing wrong?
TIA
Hello, I've a scenario where my template receives a service via dependency injection
this service in turn performs an async operation.
the idea is to call the async operation in the OnInitializedAsync method of the page/component/template which will populate the asynchronously received data that the page will finally display once ready
I've seen that BlazorTemplater correctly invokes the OnInitializedAsync method (as well as all the other Async lifecycle method) but unfortunately the "render" phase appears to occur immediately without waiting the async methods to complete; So my data are not contained in the produced html.
Looks like there are no method to "RenderAsync" or am I wrong?
Do you have any ideas/plans on how to achieve this?
Basically what I would like to have is a RenderAsync Method that returns only when all the "Async" lifecycle methods of the component have completed and therefore provides the "final" html populated with all the values obtained from these async services.
-Of course I could invoke the service externally, obtain the values and then pass as parameter to the page, but I would prefer the other way because that allows me to reuse the template as normal page in my application (providing me to preview it to the user for example, before transforming it in a HTML string that I then transform in a PDF or I can email: preview is a cool feature!)
Any idea?
Need to get the current AB repo and create a pull request to add this.
When trying to use this library to make html from a component I get this error:
It pops up when calling Render, here's the code for the rendering and the component:
I've gotten rid of the error by adding Localizer as a service, but that begs the question as to why that component is trying to use Localizer in the first place?
My first theory was that it was using the MainLayout, but that doesn't contain any reference to Localizer. The only other place I can think of that it would be requesting this service is in _Imports.razor, but why would this component be pulling from there?
So it seems there is support for cascading value but not cascading parameters.
I modified the method GetParameterName in ComponentRenderer to also get CascadingAttribute
private static string GetParameterName<TValue>(Expression<Func<TComponent, TValue>> parameterSelector)
{
if (parameterSelector is null)
throw new ArgumentNullException(nameof(parameterSelector));
if (parameterSelector.Body is not MemberExpression memberExpression ||
memberExpression.Member is not PropertyInfo propInfoCandidate)
throw new ArgumentException($"The parameter selector '{parameterSelector}' does not resolve to a public property on the component '{typeof(TComponent)}'.", nameof(parameterSelector));
var propertyInfo = propInfoCandidate.DeclaringType != TComponentType
? TComponentType.GetProperty(propInfoCandidate.Name, propInfoCandidate.PropertyType)
: propInfoCandidate;
//Changed ParameterAttribute to Attribute, as CascadingParameter inherits from Attribute not ParameterAttribute
var paramAttr = propertyInfo?.GetCustomAttribute<Attribute>(inherit: true);
if (propertyInfo is null || paramAttr is null)
throw new ArgumentException($"The parameter selector '{parameterSelector}' does not resolve to a public property on the component '{typeof(TComponent)}' with a [Parameter] or [CascadingParameter] attribute.", nameof(parameterSelector));
return propertyInfo.Name;
}
I also added a test case for the CascadeChild which contains a cascading parameter in ComponentRenderer_Tests, but it throws exception
[TestMethod]
public void ComponentRenderer_CascadingValues_Test_2()
{
const string expected = "<p>The name is Bill</p>";
var info = new CascadeInfo() { Name = "Bill" };
var html = new ComponentRenderer<CascadeChild>()
.Set(c => c.Info, info)
.Render();
// trim leading space and trailing CRLF from output
var actual = html.Trim();
Assert.AreEqual(expected, actual);
}
Exception
System.InvalidOperationException HResult=0x80131509 Message=Object of type 'BlazorTemplater.Library.CascadeChild' has a property matching the name 'Info', but it does not have [ParameterAttribute] applied. Source=Microsoft.AspNetCore.Components StackTrace: at Microsoft.AspNetCore.Components.Reflection.ComponentProperties.ThrowForSettingCascadingParameterWithNonCascadingValue(Type targetType, String parameterName) at Microsoft.AspNetCore.Components.Reflection.ComponentProperties.SetProperties(ParameterView& parameters, Object target) at Microsoft.AspNetCore.Components.ParameterView.SetParameterProperties(Object target) at Microsoft.AspNetCore.Components.ComponentBase.SetParametersAsync(ParameterView parameters) at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange
1 oldTree, ArrayRange1 newTree) at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment) at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry) at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue() --- End of stack trace from previous location --- at BlazorTemplater.HtmlRenderer.AssertNoSynchronousErrors() in C:\VSTS\BlazorTemplater\BlazorTemplater\HtmlRenderer.cs:line 71 at BlazorTemplater.HtmlRenderer.DispatchAndAssertNoSynchronousErrors(Action callback) in C:\VSTS\BlazorTemplater\BlazorTemplater\HtmlRenderer.cs:line 64 at BlazorTemplater.ContainerComponent.RenderComponentUnderTest(Type componentType, ParameterView parameters) in C:\VSTS\BlazorTemplater\BlazorTemplater\ContainerComponent.cs:line 61 at BlazorTemplater.RenderedComponent
1.SetParametersAndRender(ParameterView parameters) in C:\VSTS\BlazorTemplater\BlazorTemplater\RenderedComponent.cs:line 34 at BlazorTemplater.Templater.RenderComponent(Type componentType, IDictionary2 parameters) in C:\VSTS\BlazorTemplater\BlazorTemplater\Templater.cs:line 149 at BlazorTemplater.Templater.RenderComponent[TComponent](IDictionary
2 parameters) in C:\VSTS\BlazorTemplater\BlazorTemplater\Templater.cs:line 114 at BlazorTemplater.ComponentRenderer1.Render() in C:\VSTS\BlazorTemplater\BlazorTemplater\ComponentRenderer.cs:line 188 at BlazorTemplater.Tests.ComponentRenderer_Tests.ComponentRenderer_CascadingValues_Test_2() in C:\VSTS\BlazorTemplater\BlazorTemplater.Tests\ComponentRenderer_Tests.cs:line 239
Current version supports .NET Standard 2.0 and .NET 5 only, should update to include a .NET 6 version.
Need separate action for PRs that does not push to NUGET
Razor Components support the specify of a Layout templates that wrap component output using a @Body
render fragment.
Layouts can be specified in the component, e.g, @layout MyLayout
- we need to see if this can be supported.
Also are we able to support a layout being passed as a parameter or set in Templater
to apply to components. This might be useful if using different layouts for different brands when creating emails, for example.
Do some performance tests to see how fast the renderer is. Perhaps compare the same template with Razor-View based templating engines.
Need to test if we can render a template, provide a Cascading Value and render correctly.
Hi,
Trying to render a component where @Inject NavigationManager SiteNavigationManager is used to inject the Navigation manager, how should I make this work with BlazorTemplater?
Thank you,
ed
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();
Remember to attribute to PhotoAtomic
This is a great project, is there any intention to make a document publishing tool that can generate a static site?
The html file can be generated by reading the accessible URL route on the page.
Using BlazorTemplater in a .NET 5 ASP.NET core application, encountered this error:
Field not found: 'Microsoft.AspNetCore.Components.RenderTree.RenderTreeFrame.ComponentId'.
This is a known issue when working across the v3.x and v5.x versions.
Need to add support for .NET - similar to this approach.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.