Code Monkey home page Code Monkey logo

blazortemplater's Introduction

About Me

A real dinosaur on the bleeding edge of web design. Use Visual Studio and VS Code to create ASP.NET and ASP.NET Core applications.

A fan of Blazor since it first appeared and using it real applications already.

๐Ÿ“ฆ Notable GitHub Repos

Libraries

  • BlazorTemplater renders .razor components to HTML - useful for email generation.
  • BlazorFormLayout Blazor component library to make creating Bootstrap v4 forms easier
  • Frankfurter C# API for the Frankfurter currency conversion web service
  • versionReaderTask Azure DevOps build task to extract version values from projects

Demo Apps

โœ๏ธ Blog

Blazor Expert

๐Ÿ“Š GitHub Stats

conficient's GitHub stats

blazortemplater's People

Contributors

conficient avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

blazortemplater's Issues

CSS Isolation

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?

Await for OnInitializedAsync completes

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?

Conditionals being lost in render

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.

Possible issue with awaited data calls in rendered component

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();

Injecting NavigationManager

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

Cannot supply a component of type 'Components.BookComponent' because the current platform does not support the render mode 'Microsoft.AspNetCore.Components.Web.InteractiveServerRenderMode'

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'

Dependency Injection

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?

Can Layouts be supported?

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.

Performance tests / comparisons

Do some performance tests to see how fast the renderer is. Perhaps compare the same template with Razor-View based templating engines.

No support for cascading parameter

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, ArrayRange1 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.RenderedComponent1.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](IDictionary2 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

Rendering a component requires adding Localization service despite the component not injecting it

When trying to use this library to make html from a component I get this error:
image

It pops up when calling Render, here's the code for the rendering and the component:
image
image

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?

Not able to set cascading parameter if its inherited

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.

CS 7036 Error Even without Base Class Inheritance

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; }
}

image

What am I doing wrong?

TIA

Bootstrap

I have custom component and I am using Bootstrap for styling.
The library works fine but without showing bootstrap style!.
Any ideas?

Static site generator

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.

Consider renaming of BlazorTemplater class to different name

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:

  • Templater
  • Renderer
  • RenderHost

Need to:

  • Make breaking change, as 1.1
  • Rename references in the code/tests
  • Amend documentation
  • Amend renderer variable names to templater

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.