BlazorComponentUtilities 1.5.0
See the version list below for details.
dotnet add package BlazorComponentUtilities --version 1.5.0
NuGet\Install-Package BlazorComponentUtilities -Version 1.5.0
<PackageReference Include="BlazorComponentUtilities" Version="1.5.0" />
paket add BlazorComponentUtilities --version 1.5.0
#r "nuget: BlazorComponentUtilities, 1.5.0"
// Install BlazorComponentUtilities as a Cake Addin #addin nuget:?package=BlazorComponentUtilities&version=1.5.0 // Install BlazorComponentUtilities as a Cake Tool #tool nuget:?package=BlazorComponentUtilities&version=1.5.0
CSS & Style Builder Pattern
Read the full API docs in the Wiki section
When building complex components for ASP.NET Razor Components there are often CSS classes generated by multiple factors. Styles can often be added by system & component state, public parameters, and static base CSS classes. Combining many CSS classes together doesn't scale well, and when components get large it can be tough to reason about. The CSS builder pattern fixes this problem by offering a easy to repeat, easy to read pattern to use across components in a project. A clean code approach to conditional CSS in Razor Components.
The problem
Given the component below, the Tab's CSS class is set by multiple sources.
The component has paramater UserCss
which can be set at externally.
A base CSS class bl-nav-link
which does not change.
Two state based CSS classes bl-active
and bl-disabled
which are dependent on if the Tab is active or disabled.
Rendering for the CSS class is spread across multiple concerns and is hard to maintain.
Component
<Tab UserCss="my-class" Disabled=true >...</Tab>
Component Source Markup
<li class="bl-nav-item">
<a onclick=@Activate class="bl-nav-link @ActiveCssClass @DisabledCssClass" role="button">
@Title
</a>
</li>
Component Source Logic
[Parameter] string UserCss = String.Empty;
protected string ActiveCssClass => IsActive ? "bl-active" : String.Empty;
protected string DisabledCssClass => Disabled ? "bl-disabled" : String.Empty;
The Solution
Using the CSS Builder pattern we can clean up the code and move everything to a single concern.
Refactoring the component, we move all of the CSS code to the components logic.
In the OnParameterSet
lifecycle method, we condense all of the CSS responsibilities into a single variable ClassToRender
(this name variable is optional).
Next, using the CssBuilder
we define what classes should be added to the builder and when they should be rendered.
The pattern is as follows AddClass
the CSS class and the condition when it should appear AddClass(value, when: bool)
.
If the value is static, we can discard the when
parameter.
Finally, Build is called. All CSS classes are then combined into a single string, properly spaced and trimmed.
The refactored code becomes:
Component Source Markup (refactor)
<li class="bl-nav-item">
<a onclick=@Activate class="@ClassToRender" role="button">
@Title
</a>
</li>
Component Source Logic (refactor)
protected override void OnParametersSet()
{
ClassToRender = new CssBuilder(UserCss)
.AddClass("bl-nav-link")
.AddClass("bl-active", when: IsActive)
.AddClass("bl-disabled", when: Disabled)
.Build();
}
Dynamic values
If you encounter a dynamic value where some string is added to a CSS class name, we can easily handle this as well.
Consider the CSS namespace bl-
must be prefixed to a value SomeValue
supplied by the component state or user input.
This is easily handled with string interpolation ${var}
.
protected override void OnParametersSet()
{
ClassToRender = new CssBuilder(UserCss)
.AddClass("bl-nav-link")
.AddClass("bl-active", when: IsActive)
.AddClass("bl-disabled", when: Disabled)
.AddClass("bl-${SomeValue}", when: IsConditionMet)
.Build();
}
Attribute Splatting
If you encounter dynamic values due to attribute splatting, we can easily handle this as well. Consider the senario where we need to merge a splatted CSS attribute while preserving default values.
<MyComponent class="custom-value">
<div class="my-base custom-value">
<div @attributes="AdditionalAttributes" class="@CssClass">
@code {
[Parameter(CaptureUnmatchedValues = true)]
public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, Object>();
CssBuilder CssClass => new CssBuilder("my-base").AddClassFromAttributes(attributes);
}
Func When
Func<bool> is also accepted as an arguement for the when
parameter. This allows either inline functions or named functions to be called directly.
protected override void OnParametersSet()
{
ClassToRender = new CssBuilder(UserCss)
.AddClass("bl-nav-link")
.AddClass("bl-foo", when: ()=>
!string.IsNullOrEmpty(Foo) ||
!Disabled ||
IsActive)
.AddClass("bl-active", when: IsActive)
.AddClass("bl-disabled", when: Disabled)
.Build();
}
Named function, example.
bool HasMeaningfulName() => !string.IsNullOrEmpty(Foo) || !Disabled || IsActive);
protected override void OnParametersSet()
{
ClassToRender = new CssBuilder(UserCss)
.AddClass("bl-nav-link")
.AddClass("bl-foo", when: HasMeaningfulName)
.AddClass("bl-active", when: IsActive)
.AddClass("bl-disabled", when: Disabled)
.Build();
}
Removing Unused Attributes
When using dynamic attributes may result in empty attribute. When Blazor renders an attribute that has an empty string value, it will result in an empty attribute tag. However, if the value is null the attribute will be excluded. When an empty attribute is expected, the extension method NullIfEmpty can be used to clean up the resulting markup. Note: This method is only necessary when no default value is supplied. Ex: new CssBuilder() or CssBuilder().Empty(). Forgetting to call NullIfEmpty should not have any impact on the UI.
<div class="">
<div>
<div class="@CssClass">
@code {
//string CssClass => new CssBuilder().AddClassFromAttributes(attributes).Build();
string CssClass => new CssBuilder().AddClassFromAttributes(attributes).NullIfEmpty();
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Runtime (>= 4.3.1)
NuGet packages (25)
Showing the top 5 NuGet packages that depend on BlazorComponentUtilities:
Package | Downloads |
---|---|
BlazorStrap
A bootstrap 5 component library for Blazor |
|
BlazorPro.Spinkit
A collection of loading indicators animated with CSS for Blazor. Spinkit also includes the SpinLoader component with templates for handling null values during async operations. Spinkit includes CSS from the Spinkit project by Tobias Ahlin https://tobiasahlin.com/spinkit/ |
|
BlazorStrap.V5
Bootstrap 5.x component library for BlazorStrap |
|
Blazicons
Provides support for displaying SVG based icons in Blazor projects. |
|
BlazorStrap.V4
Bootstrap 4.x component library for BlazorStrap |
GitHub repositories
This package is not used by any popular GitHub repositories.
1.5.0
Added Func<string> value overload for StyleBuilder
1.4.0
Added ValueBuilder for complex styles
Updated /// docs
1.3.0
Added utlitiy mehtod for Attribute Splatting.
Added utility method for returning null for empty strings. This is useful for splatting scenarios.
Added utility method for starting off new builders.