'Blazor attribute splatting issues with CssBuilder package
I am using CssBuilder to build the CSS as described in CssBuilder docs. When i add this it in turn breaks my passed click handlers. I either get click handlers or i get CSS, not both.
ComponentUIBase
using Microsoft.AspNetCore.Components;
using System;
using System.Collections.Generic;
namespace BlazorServer.UI
{
public class UIComponentBase : ComponentBase
{
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, Object>();
}
}
Custom Button.razor component - When @attributes exists, CSS breaks and onclick works, when @attributes is removed, CSS works and onclick breaks
@inherits UIComponentBase
<button class="@BaseCss" @attributes="AdditionalAttributes">
@ChildContent
</button>
Custom Button.cs code-behind
using BlazorComponentUtilities;
using Microsoft.AspNetCore.Components;
namespace BlazorServer.UI.Buttons
{
public partial class Button
{
[Parameter]
public string Color { get; set; } = "btn-primary";
[Parameter]
public bool Rounded { get; set; } = false;
public string BaseCss =>
new CssBuilder("btn")
.AddClass(Color)
.AddClass("rounded-0", when: !Rounded)
.AddClass("rounded-2", when: Rounded)
.AddClass("shadow-none")
.AddClassFromAttributes(AdditionalAttributes)
.Build();
}
}
Parent component
<Card Color="bg-info">
<CardHeader>Card Header</CardHeader>
<CardBody>
<CardTitle>Card title</CardTitle>
<CardTitle SubTitle>Card Subtitle</CardTitle>
<CardText>Card text goes here...</CardText>
</CardBody>
<CardFooter>
<Spacer />
<Button Color="bg-danger">Cancel</Button>
<Button class="ms-2" Color="bg-primary" @onclick="Test">Ok</Button>
</CardFooter>
</Card>
@code {
private async void Test()
{
await JsRuntime.InvokeVoidAsync("alert", "Warning!11"); // Alert
}
}
Solution 1:[1]
You need to remove the class from the splatter attributes.
I have a similar setup to you. Here's some code.
My UIComponentBase:
public abstract class UIComponentBase : ComponentBase
{
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object> UserAttributes { get; set; } = new Dictionary<string, object>();
protected virtual List<string> UnwantedAttributes { get; set; } = new List<string>();
protected Dictionary<string, object> SplatterAttributes
{
get
{
var list = new Dictionary<string, object>();
foreach (var item in UserAttributes)
{
if (!UnwantedAttributes.Any(item1 => item1.Equals(item.Key)))
list.Add(item.Key, item.Value);
}
return list;
}
}
}
My Base class - a default div:
public class UIComponent : UIComponentBase
{
[Parameter] public bool Show { get; set; } = true;
[Parameter] public bool Disabled { get; set; } = false;
[Parameter] public string? Tag { get; set; }
[Parameter] public EventCallback<MouseEventArgs> ClickEvent { get; set; }
protected virtual List<string> CssClasses { get; private set; } = new List<string>();
protected virtual string HtmlTag => this.Tag ?? "div";
protected override List<string> UnwantedAttributes { get; set; } = new List<string>() { "class" };
protected string CssClass
=> CSSBuilder.Class()
.AddClass(CssClasses)
.AddClassFromAttributes(this.UserAttributes)
.Build();
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (this.Show)
{
builder.OpenElement(0, this.HtmlTag);
builder.AddMultipleAttributes(1, this.SplatterAttributes);
if (!string.IsNullOrWhiteSpace(this.CssClass))
builder.AddAttribute(2, "class", this.CssClass);
if (Disabled)
builder.AddAttribute(3, "disabled");
if (ClickEvent.HasDelegate)
builder.AddAttribute(4, "onclick", EventCallback.Factory.Create<MouseEventArgs>(this, ClickEvent));
builder.AddContent(5, ChildContent);
builder.CloseElement();
}
}
}
And then my button:
public class UIButton : UIComponent
{
public UIButton()
=> this.CssClasses.Add("btn mr-1");
protected override string HtmlTag => "button";
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (this.Show)
{
builder.OpenElement(0, this.HtmlTag);
builder.AddAttribute(1, "class", this.CssClass);
builder.AddMultipleAttributes(2, this.SplatterAttributes);
if (!UserAttributes.ContainsKey("type"))
builder.AddAttribute(3, "type", "button");
if (Disabled)
builder.AddAttribute(4, "disabled");
if (ClickEvent.HasDelegate)
builder.AddAttribute(5, "onclick", EventCallback.Factory.Create<MouseEventArgs>(this, ClickEvent));
builder.AddContent(6, ChildContent);
builder.CloseElement();
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
