# 简介

一个自动化框架，可帮助您更轻松、更快速地构建 Blazor 组件库。

<div align="left"><img src="https://img.shields.io/github/v/release/AchievedOwner/ComponentBuilder" alt="Latest Version"> <img src="https://img.shields.io/badge/.net-6-blue" alt=".net6"> <img src="https://img.shields.io/badge/.net-7-blue" alt=".net7"></div>

## :sparkles: 特性

* 组件的自动化参数构建
* 易于定制和个性化组件构建
* 易于构建灵活的动态组件结构
* 易于代码和 javascript 之间的交互
* 组件构建自动化的模块化实现
* RenderTreeBuilder 的强大扩展
* 其他自动化...

## :rainbow: 组件定义

* 在 `Button.razor`

```cshtml
@inherits BlazorComponentBase

<button @attributes="AdditionalAttributes">
    @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][HtmlEvent("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set; }

    [Parameter][HtmlAttribute]public string? Title { get; set; }
    
    public enum Color
    {
	    Primary,
	    Secondary,
	    [CssClass("info")]Information,
    }
}
```

* 在 `Button.cs` 类

```csharp
[HtmlTag("button")]
[CssClass("btn")]
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][HtmlEvent("onclick")]public EventCallback<ClickEventArgs> OnClick { get; set; 

    [Parameter][HtmlAttribute]public string? Title { get; set; }
}

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

* 使用和对比

```html
<!--razor-->
<Button Color="Color.Primary">Submit</Button>
<!--html-->
<button class="btn btn-primary">Submit</button>

<!--razor-->
<Button Active Tooltip="active button" Color="Color.Information" Title="click me">Active Button</Button>
<!--html-->
<button class="btn btn-info active" data-tooltip="active button" title="click me">Active Button</button>
```

## :key: C# 和 Javascript 交互

* 导入模块

```javascript
//in app.js
export function display(){
 // ...your code
}
```

```csharp
[Inject]IJSRuntime JS { get; set; }

var js = await JS.Value.ImportAsync("./app.js");
js.display(); // same as function name
```

* 执行 js 字符串

```csharp
JS.Value.EvaluateAsync(window => {
    window.console.log("log")
});

JS.Value.EvaludateAsync(@"
    console.log(\"log\");
")
```

## :information\_source: 个性化 CSS/Style/Attributes

* Logical CSS

```csharp
protected override void BuildCssClass(ICssClassBuilder builder)
{
    if(builder.Contains("annotation-enter"))
    {
        builder.Remove("annotation-exist");
    }
    else
    {
        builder.Append("annotation-enter").Append("annotation-exist");
    }
}
```

* Logical Attributes

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

## :palm\_tree: RenderTreeBuilder 的扩展

* 创建元素

```csharp
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
    builder.Open("div")
            .Class("my-class", (IsActive, "active"), (!string.IsNullOrEmpty(Name), "text-block"))
            .Style((Size.HasValue, $"font-size:{Size}px"))
            .Content("hello world")
           .Close();

    builder.CreateElement(10, "span", "hello", attributes: new { @class = "title-span"});

}
```

* 创建组件

```csharp
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
    builder.Open<Button>()
            .Class("my-class", (IsActive, "active"), (!string.IsNullOrEmpty(Name), "text-block"))
            .Style((Size.HasValue, $"font-size:{Size}px"))
            .Content(ChildContent)
           .Close();

    builder.CreateComponent<NavLink>(0, "Home", new { NavLinkMatch = NavLinkMatch.All, ActiveCssClass = "nav-active" })
}
```

## :children\_crossing: 关联组件

### 在 .razor 文件

* `List.razor` 作为父组件

```cshtml
<ul @attributes="AdditionalAttributes">
    <CascadingValue Value="this">
        @ChildContent
    </CascadingValue>
</ul>
```

* `ListItem.razor` 作为子组件

```cshtml
<li @attributes="AdditionalAttributes">@ChildContent</li>

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

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

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

### 在 RenderTreeBuilder 中

* `List` 组件类

```csharp
[ParentComponent] //be cascading parameter for this component
[HtmlTag("ul")]
public class List : BlazorComponentBase, IHasChildContent
{

}
```

* `ListItem` 组件类

```csharp
[ChildComponent(typeof(List))] //Strong association with List
[HtmlTag("li")]
public class ListItem : BlazorComponentBase, IHasChildContent
{
    [CascadingParameter]public List CascadedList { get; set; }

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

### 组件的使用

```html
<List>
    <ListItem>...</ListItem>
</List>

<ListItem /> <!--ListItem 组件不在 List 组件中将抛出异常-->

```

## :six\_pointed\_star: HtmlHelper

* 在 `.razor` 中

```cshtml
<div class="@GetCssClass">
...
</div>

@code{
    string GetCssClass => HtmlHelper.Class.Append("btn-primary").Append("active", Actived).ToString();
}
```

* 应用于 RenderTreeBuilder 时

```csharp
builder.CreateElement(0, "span", attributes: 
    new { 
            @class = HtmlHelper.Class
                                .Append("btn-primary")
                                .Append("active", Actived),
            style = HtmlHelper.Style.Append($"width:{Width}px"),
            onclick = HtmlHelper.Event.Create<MouseEventArgs>(this, e=>{ //...click... });
        });
```

## :crossed\_swords: 拦截器

您可以拦截组件的生命周期

* 定义拦截器

```csharp
public class LogInterceptor : ComponentInterceptorBase
{
    private readonly ILogger<LogInterceptor> _logger;
    public LogInterceptor(ILogger<LogInterceptor> logger)
    {
        _logger = logger;
    }

    //在 SetParameterAsync 方法中执行
    public override void InterceptSetParameters(IBlazorComponent component, ParameterView parameters)
    {
        foreach(var item in parameters)
        {
            _logger.LogDebug($"Key:{item.Name}, Value:{item.Value}");
        }
    }
}
```

* 注册拦截器

```csharp
builder.Services.AddComponentBuilder(configure => {
    configure.Interceptors.Add(new LogInterceptor());
})
```

## :blue\_book: 安装指南

* 从 `Nuget.org` 安装

```powershell
Install-Package ComponentBuilder
```

* 注册服务

```csharp
builder.Services.AddComponentBuilder();
```

## :pencil: 组件库解决方案模板

使用 `ComponentBuilder.Templates` 生成组件库解决方案和在线演示站点

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

更多信息见 templates


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://playermaker.gitbook.io/componentbuilder/chinese/jian-jie.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
