ComponentBuilder 5.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package ComponentBuilder --version 5.0.0
NuGet\Install-Package ComponentBuilder -Version 5.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ComponentBuilder" Version="5.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ComponentBuilder --version 5.0.0
#r "nuget: ComponentBuilder, 5.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ComponentBuilder as a Cake Addin
#addin nuget:?package=ComponentBuilder&version=5.0.0

// Install ComponentBuilder as a Cake Tool
#tool nuget:?package=ComponentBuilder&version=5.0.0

ComponentBuilder

The best automation framework for RCL(Razor Component Library) to help you easy and quickly building your own Razor Component Library

中文介绍 | Quick Start | Document

Latest Version

v4.x supports .net6 .net7

v5.x supports .net8

✨ Features

  • OOP mindset creating component
  • Attribute first, easy define CSS from parameters
  • Easy to associate with components via Attributes
  • Cusomization CSS and attributes of component by coding logic
  • Support Pre-definition for components with simular parameters
  • New lifecycle definition of Component with interceptor design pattern
  • Renderer pipeline pattern to regonize dynamic render of components

🌈 Quick Start

Only change ComponentBase to BlazorComponetBase for derived component class

  • Sample to create a button component with C# class:
[HtmlTag("button")] //define HTML element tag
[CssClass("btn")] //define component necessary CSS class
public class Button : BlazorComponentBase, IHasChildContent, IHasOnClick
{
	[Parameter][CssClass("active")]public bool Active { get; set; } 	
	[Parameter][CssClass("btn-")]public Color? Color { get; set; } 
	[Parameter]public RenderFragment? ChildContent { get; set; }
	[Parameter][HtmlData("tooltip")]public string? Tooltip { get; set; }
	[Parameter][HtmlAttribute("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set; 
	[Parameter][HtmlAttribute]public string? Title { get; set; }
}

public enum Color
{
	Primary,
	Secondary,
	[CssClass("info")]Information,
}

OR you also can define most part of automation features in razor file:

@inherits BlazorComponentBase



<button @attributes="@GetAttributes()"> 
	@ChildContent
</button>

@code{
	[CssClass("btn")]
	public Button()
	{
	}

	[Parameter][CssClass("active")]public bool Active { get; set; } 	
	[Parameter][CssClass("btn-")]public Color? Color { get; set; } 
	[Parameter]public RenderFragment? ChildContent { get; set; } 
	[Parameter][HtmlData("tooltip")]public string? Tooltip { get; set; }
	[Parameter][HtmlAttribute("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set; } 
	[Parameter][HtmlAttribute]public string? Title { get; set; }
	
	public enum Color
	{
		Primary,
		Secondary,
		[CssClass("info")]Information,
	}
}
  • Use component

<Button Color="Color.Primary">Submit</Button>

<button class="btn btn-primary">Submit</button>


<Button Active Tooltip="active button" Color="Color.Information" Title="click me">Active Button</Button>

<button class="btn btn-info active" data-tooltip="active button" title="click me">Active Button</button>

ℹ️ Logical CSS/Style/Attributes

  • Using different logic for groups creates code with OOP mindset
protected override void BuildCssClass(ICssClassBuilder builder)
{
	if(builder.Contains("annotation-enter"))
	{
		builder.Remove("annotation-exist");
	}
	else
	{
		builder.Append("annotation-enter").Append("annotation-exist");
	}
}

protected override void BuildStyle(IStyleBuilder builder)
{
	if(Height.HasValue)
	{
		builder.Append($"height:{Height}px");
	}
}

protected override void BuildAttributes(IDictionary<string, object> attributes)
{	
	if(attrbutes.ContainKey("data-toggle"))
	{
		attributes["data-toggle"] = "collapse";
	}
}

🚸 Parent/Child component

Easy to create parent/child pair components using ParentComponentAttribute and ChildComponentAttribute<TParent>

  • For List component class
[ParentComponent] //auto creating the cascading parameter for current 
[HtmlTag("ul")]
public class List : BlazorComponentBase, IHasChildContent
{

}
  • For ListItem component class
[ChildComponent<List>] //Strong association with List
[HtmlTag("li")]
public class ListItem : BlazorComponentBase, IHasChildContent
{
	[CascadingParameter]public List? CascadedList { get; set; }//Auto getting the instance of cascading parameter

	[Parameter] public RenderFragment? ChildContent { get; set; }
}

Use in blazor

<List>
	<ListItem>...</ListItem>
</List>

<ListItem /> 

In .razor file

In razor file component, you should create cascading parameter by yourself

List.razor:

<ul @attributes="@GetAttributes()">
	<CascadingValue Value="this">
		@ChildContent
	</CascadingValue>
</ul>

ListItem.razor:

<li @attributes="GetAttributes()">@ChildContent</li>

@code{
	[ChildComponent<List>]
	public ListItem()
	{
	}

	[CascadingParameter] public List? CascadedList { get; set; }

	[Parameter] public RenderFragment? ChildContent { get; set; }
}

😄 Other extensions

  • Extensions for RenderTreeBuilder

It's very useful for dynamic component creating using OOP mindset

builder.CreateElement(0, "div","any text", new { @class="main" });		
//<div class="main">any text</div>

builder.CreateComponent<MyComponent>(attributes: new { Visible = true }); 
//<MyComponent Visible />

builder.CreateCascadingValue<T>(value); 
//<CascadingValue Value="this"></CascadingValue>
  • FluentRenderTreeBuilder

Write RenderTreeBuilder as fluent API

//import namespace
using ComponentBuilder.FluentRenderTree;

builder.Element("p", "default-class")		// create <p> element with default class
		.Class("hover", Hoverable)			// append class if Hoverable parameter is true
		.Attribute("disabled", Disabled)	// add HTML attribute if Disabled is true
		.Data("trigger", "string")			// add data-trigger="string" HTML attribute if String parameter not empty
		.Callback<MouseEventArgs>("onmouseover", this, e => MyHandler())	// add event named 'onmouseover' with a event handler code
		.Content("content text")			// add inner text for this element
	.Close()

//HTML element generate like:
<p class="default-class hover" data-trigger="string" disabled>content text</p>

// normally in razor file:
<p class="default-class @(Hoverable?"hover":"")" disabled="@Disabled" data-trigger="string" @onmouseover="@(e => MyHandler())">content text</p>


builder.Component<MyComponent>()
		.Parameter(m => m.Disabled, true)
		.Parameter(m => Size, 5)
		.ChildContent("My name is hello world")
	.Close();

  • Create dynamic Class/Style/Callback
//import namespace
using ComponentBuilder.JSInterop

//create dynamic css class string
HtmlHelper.Class.Append("class1").Append("disabled", Disabled).ToString();

//create dynamic style string
HtmlHelper.Style.Append($"width:{Width}px").Append($"height:{Height}px", Height.HasValue).ToString();

//create dynamic EventCallback
HtmlHelper.Callback.Create(this, ()=>{ //action for callback });
  • ComponentBuilder.JSInterop

Interactive with C# and JS

export function sayHello(){
	//...
}

export function getClient(){
	//..
	return name;
}


@inject IJSRuntime JS

var module = JS.ImportAsync("./module.js");	//Import js module

await module.Module.InvokeVoidAsync("sayHello");
var name = await module.Module.InvokeAsync<string>("getClient");

⚔️ Interceptors

You can intercept the lifecycle of component

  • Define an interceptor
public class LogInterceptor : ComponentInterceptorBase
{
	private readonly ILogger<LogInterceptor> _logger;
	public LogInterceptor(ILogger<LogInterceptor> logger)
	{
		_logger = logger;
	}

	//Run in SetParameterAsync method is called
	public override void InterceptSetParameters(IBlazorComponent component, ParameterView parameters)
	{
		foreach(var item in parameters)
		{
			_logger.LogDebug($"Key:{item.Name}, Value:{item.Value}");
		}
	}
}
  • Register interceptor
builder.Services.AddComponentBuilder(configure => {
	configure.Interceptors.Add<LogInterceptor>();
})

BlazorComponentBase Lifecycle

Why interceptors?

Follow SOLID pricipal when designing a component. So you no need break the lifecycle or using override any protected method such as OnParameterSet to create new HTML attribute whatever you want.

♻️ Renderer Pipeline

Recognize special case to render specified component

public class NavLinkComponentRender : IComponentRender
{
	public bool Render(IBlazorComponent component, RenderTreeBuilder builder)
	{
		if ( component is IHasNavLink navLink )
		{
			builder.OpenComponent<NavLink>(0);
			builder.AddAttribute(1, nameof(NavLink.Match), navLink.Match);
			builder.AddAttribute(2, nameof(NavLink.ActiveClass), navLink.ActiveCssClass);
			builder.AddAttribute(3, nameof(NavLink.ChildContent), navLink.ChildContent);
			builder.AddMultipleAttributes(4, component.GetAttributes());
			builder.CloseComponent();
			return false;
		}
		return true;
	}
}
  • Register renderer in configuration
builder.Services.AddComponentBuilder(configure => {
	configure.Renderers.Add<NavLinkComponentRenderer>();
});

📘 Installation Guide

  • Install from Nuget.org
Install-Package ComponentBuilder
  • Register service
builder.Services.AddComponentBuilder();

//configure costomization such as Interceptors
builder.Services.AddComponentBuilder(configure => {
	//...
})

Read document for more informations

📝 Component Library Solution Template

Use ComponentBuilder.Templates to generate a razor component library solution and online demo site

dotnet new install ComponentBuilder.Templates
dotnet new blazor-sln -n {YourRazorLibraryName}

More information see templates

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on ComponentBuilder:

Package Downloads
TDesign

基于腾讯 TDesign 的 Blazor 企业级组件库。腾讯 TDesign 官方地址:https://tdesign.tencent.com/

BlamanticUI

The css framework from Semantic-UI for blazor without jQuery.

ComponentBuilder.FluentRenderTree

用链式编程的方式简化 RenderTreeBuilder 的操作。 示例: builder.Element("div").Content("hello").Close(); builder.Component<Button>().Content("Button").Close(); builder.Div(Id is not null).Content(content => content.Component<Icon>().Attribute(m => m.Name, "user").Close()).Close();

ComponentBuilder.Interceptors.Diagnostics.Console

在控制台中用于组件生命周期诊断的拦截器,该拦截器可以用于调试阶段的生命周期运行的输出。

ComponentBuilder.Resolvers.FluentClass

组件参数支持 IFluentClassProvider 自动解析成 CSS 类。 [Parameter]public IFluentClassProvider Parameter{ get; set; } <Component Parameter="Provider.Is3.FromTop.HasSmall" />

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on ComponentBuilder:

Repository Stars
tdesign-blazor/TDesignBlazor
基于腾讯 TDesign 的 Blazor 组件库
Version Downloads Last updated
5.1.0 95 3/7/2024
5.0.0 161 12/21/2023
5.0.0-beta-1 112 11/16/2023
4.1.2 891 7/17/2023
4.1.1 165 7/17/2023
4.1.0 692 6/4/2023
4.0.0 439 5/30/2023
4.0.0-beta-3 221 5/22/2023
4.0.0-beta-2 108 5/19/2023
4.0.0-beta-1 115 5/17/2023
3.1.4 504 3/17/2023
3.1.3 186 3/16/2023
3.1.0 203 3/10/2023
3.0.0 705 2/24/2023
3.0.0-beta-0217 86 2/16/2023
3.0.0-beta-0206 166 2/6/2023
3.0.0-beta-0130 101 1/30/2023
3.0.0-beta-0114 100 1/13/2023
2.3.0 286 12/28/2022
2.2.0 279 12/13/2022
2.1.0 1,413 11/24/2022
2.0.0 341 10/28/2022
1.5.0.4 353 10/18/2022
1.5.0.3 374 10/13/2022
1.5.0.2 1,190 10/5/2022
1.5.0.1 380 10/3/2022
1.5.0 387 10/1/2022
1.4.1.1 645 9/19/2022
1.4.1 392 9/16/2022
1.4.0 402 9/15/2022
1.3.0 394 8/29/2022
1.2.1 548 7/12/2022
1.2.0 398 7/11/2022
1.1.0 538 5/22/2022
1.0.0 585 3/23/2022
0.7.0 424 3/11/2022
0.6.0 418 2/9/2022
0.5.0 260 1/6/2022
0.4.0 269 12/23/2021
0.3.0 276 12/16/2021
0.2.0 284 12/7/2021
0.1.0 6,132 11/24/2021