如何像Vue一样从父层随机向Blazor组件添加CSS属性?

14

由于我想设计一些可重用的Blazor组件,希望它们具有以下功能:

假设我有一个自定义组件"MyComponent",在使用它时,我可以添加任何CSS属性:

<MyComponent Class="custom-css1 custom-css2">
    some child content...
</MyComponent>

在MyComponent中,我通常会将一些常见的CSS属性固定到顶部包装器上,就像这样:

Translated to Chinese:

在MyComponent中,我通常会将一些常见的CSS属性固定到顶部包装器上,就像这样:

<div class="fixed-css1 fixed-css2">
    some child content...
</div>

这意味着我需要将两个CSS属性的部分组合在一起,以使最终的HTML像这样:

<div class="fixed-css1 fixed-css2 custom-css1 custom-css2">
    some child content...
</div>

所以我想我应该有这个模式:

<div class="@Classes">
    some child content...
</div>

@functions
{

    [Parameter]
    private string Class { get; set; } = "";

    private string fixedClass = "fixed-css1 fixed-css2";

    private string Classes
    {
        get
        {
            return $"{fixedClass} {Class}";
        }
    }
}
为了减少冗余代码,我可以创建一个基类,其包含一个受保护的 Class 属性,并让每个组件都继承自它,但我仍然无法避免在每个组件中编写相同的组合代码。 我希望有一些解决方案可以直接在我的基类中添加这些自定义 CSS,我想我可以通过覆盖 ComponentBase 类的 BuildRenderTree 方法来实现这一点:
protected override void BuildRenderTree(RenderTreeBuilder builder)
        {
            base.BuildRenderTree(builder);
        }

不幸的是,我已经尝试了所有手动构建的方法,但不知道如何完成。我不知道如何获取我的HTML元素(例如“div”)并向其添加其他CSS属性。

所有这些都是关于像 Vue 那样轻松实现的功能。在Vue代码中,我们肯定可以向组件添加任何属性,并将它们传递给组件中的第一个元素。

能否有人帮助我完成这个目标或给我一些建议?


1
我可以问一下,为什么在基类上使用您已经识别的模式不能解决您的问题?您只需要使用@Classes即可应用组合CSS。这实际上正是Blazor团队用来允许在表单输入控件上使用自定义CSS类的方法。 - Chris Sainty
抱歉,也许我没有清楚地描述我的观点。我希望将这种模式应用于所有组件,这意味着我必须一遍又一遍地重复相同的组合代码(除了Class属性),这很丑陋。 - SillyJumper
我不认为“随机”是一个好的词选择。我觉得你的意思应该是“任意”。 - Jesse
5个回答

8

我认为你的方法很好,只需要稍微抽象一下就能使其更易读,在多个组件之间轻松管理。

这就是我创建这个简单帮助函数库的原因。它与你在代码中所做的完全相同,但提供了一个API以保持一致性。

https://www.nuget.org/packages/BlazorComponentUtilities/


谢谢,我会仔细研究这个建议。 - SillyJumper
太好了!这正是我想要的,而且更多,感谢您的贡献。 - SillyJumper
这个辅助工具设计得非常好,甚至可以处理“属性扩展”。+1 - Yogi

3
据我所知,目前Blazor没有内置处理CSS的方法,并且Blazor的最佳实践和模式尚未提供,因此您可以通过JSInterop等方式来处理CSS。
以下是我认为对您非常有用的库链接: https://github.com/chanan/BlazorStyled 希望这能帮到您...

谢谢,这个包让我有了另一种思考方式,很有帮助。 - SillyJumper

3

试试这个,我已经使用过了,它有效并且简单易懂。

<div class="fixed-css1 fixed-css2 @(Class)">
    some child content...
</div>

我不确定这是否是一种不好的做法,它会导致类属性中的 IntelliSense 无法正常工作,但这很容易解决,只需将其添加到属性的最后和末尾即可。如果您需要使用 IntelliSense 进行更改,请在 @(class) 前面添加 "",进行更改后再删除 ""。如果组件上未设置 Class 参数,则可能会在类字符串中留下一个空格,例如 <div class="fixed-css1 fixed-css2 ">(末尾有空格)。

谢谢,我已经按照您提供的方式解决了这个问题。祝您有个愉快的一天。 - SillyJumper

2

假设:

  • 您想在父元素中添加类
  • 您想在子元素中添加类
  • 您希望所有类都在最终标记中合并
  • 您想使用 class... 而不是 ClassClassesCssCssClass 或其他任何东西!

ChildComponent.razor:

<div @attributes=_additionalAttributes></div>

@code {

  [Parameter(CaptureUnmatchedValues = true)]
  public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }
    = new Dictionary<string, object>();

  private IReadOnlyDictionary<string, object>? _additionalAttributes;

  protected override void OnParametersSet()
  {
    base.OnParametersSet();

    var parentClasses = AdditionalAttributes.GetValueOrDefault("class", "");
    var classes       = $"foo {parentClasses}".Trim();

    _additionalAttributes = 
      AdditionalAttributes.Where(x => x.Key != "class")
      .Append(KeyValuePair.Create("class", (object)classes))
      .ToDictionary(x => x.Key, x => x.Value);
  }

}

ParentComponent.razor 现在彻底清理干净,使用常规名称 class

<ChildComponent class="bar baz" />

@code {
}

这将呈现:

<div class="foo bar baz"></div>

我在仓库上添加了一个请求,希望将此功能包含在“ComponentBase”中;如果您需要此功能,请为该问题投票。它还展示了另一种实现方式。 - lonix

2
你可以用两种方法来实现这个功能。
第一种方法,你可以像下面这样使用字典从父组件传递到子组件。在此示例中,我已添加了required属性。 子组件:
<input id="firstName" @attributes="InputAttributes" />

@code {
    [Parameter]
    public Dictionary<string, object> InputAttributes { get; set; } = 
        new Dictionary<string, object>() 
        {
            { "placeholder", "Child Component Placeholder" }
        };
}

父组件:

<ChildComponent InputAttributes="attributesFromParent">
</ChildComponent>

@code {
    public Dictionary<string, object> attributesFromParent { get; set; } = 
        new Dictionary<string, object>() 
        {
            { "required", "required" },
            { "placeholder", "Parent Component Placeholder" }
        };
}

第二种方法是将 CaptureUnmatchedValues 设置为 true,并直接从父组件传递 maxlength 属性到子组件中。请参考以下代码实现:

子组件:

<input id="firstName" @attributes="InputAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; } = 
        new Dictionary<string, object>() 
        {
            { "placeholder", "Child Component Placeholder" }
        };
}

父组件:

<ChildComponent InputAttributes="attributesFromParent" maxlength="15">
</ChildComponent>

@code {
    public Dictionary<string, object> attributesFromParent { get; set; } = 
        new Dictionary<string, object>() 
        {
            { "required", "required" },
            { "placeholder", "Parent Component Placeholder" }
        };
}

参考资料: https://youtu.be/gClG243kn1ohttps://www.pragimtech.com/blog/blazor/blazor-arbitrary-attributes/

这是一篇关于IT技术的文章,介绍了Blazor任意属性的内容。你可以通过上述链接进行了解。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接