DKX.Signals.Blazor 1.0.0-rc.10

Prefix Reserved
This is a prerelease version of DKX.Signals.Blazor.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package DKX.Signals.Blazor --version 1.0.0-rc.10
                    
NuGet\Install-Package DKX.Signals.Blazor -Version 1.0.0-rc.10
                    
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="DKX.Signals.Blazor" Version="1.0.0-rc.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DKX.Signals.Blazor" Version="1.0.0-rc.10" />
                    
Directory.Packages.props
<PackageReference Include="DKX.Signals.Blazor" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DKX.Signals.Blazor --version 1.0.0-rc.10
                    
#r "nuget: DKX.Signals.Blazor, 1.0.0-rc.10"
                    
#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.
#addin nuget:?package=DKX.Signals.Blazor&version=1.0.0-rc.10&prerelease
                    
Install DKX.Signals.Blazor as a Cake Addin
#tool nuget:?package=DKX.Signals.Blazor&version=1.0.0-rc.10&prerelease
                    
Install DKX.Signals.Blazor as a Cake Tool

DKX.Signals.Blazor NuGet Version

<a href="https://www.buymeacoffee.com/david_kudera" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-violet.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>

Integration of DKX.Signals with Blazor.

Installation

dotnet add package DKX.Signals
dotnet add package DKX.Signals.Blazor
dotnet add package DKX.Signals.Blazor.SourceGenerators

Before continuing, please read the README of the main project.

Example

@inherits DKX.Signals.Blazor.SignalsComponentBase

<p>Current count: @_counter.Get()</p>
<button @onclick="IncrementCount">Click me</button>

And the code-behind class:

using DKX.Signals;
using DKX.Signals.Blazor;
using DKX.Signals.Blazor.Attributes;

public sealed partial class Counter : SignalsComponentBase
{
    [Watch]
    private readonly IWritableSignal<int> _counter;

    public Counter()
    {
        _counter = Scope.Signal(0);
    }

    private void IncrementCount()
    {
        _counter.Update(static x => x + 1);
    }
}

Limitations

Source Generators

Read more here: https://github.com/dotnet/roslyn/issues/57239

Because source generators can't access code generated by other source generators, we have to use the code-behind approach while also specifying the @inherits directive in the Razor file and in the code-behind class. We use this as a marker to find all components in our source generator.

Error BL0007

Read more here: https://github.com/dotnet/aspnetcore/issues/51372

This error can be safely ignored by creating .editorconfig file in the root of your project with the following content:

[*.cs]
dotnet_diagnostic.BL0007.severity = none           # Component parameter '*' should be auto property

JetBrains Rider and [EditorBrowsable]

Read more here: https://youtrack.jetbrains.com/issue/RSRP-330352/EditorBrowsable-is-ignored-by-default

There is some generated code that should not be visible in the editor. This is done by using the EditorBrowsable attribute. To hide this code in JetBrains Rider, see this comment in the issue above: https://youtrack.jetbrains.com/issue/RSRP-330352/EditorBrowsable-is-ignored-by-default#focus=Comments-27-6905957.0-0

Sealed components

Components that want to use the SignalsComponentBase class must be sealed.

SignalsComponentBase

SignalsComponentBase is a base class for Blazor components that use DKX.Signals. It provides the following features:

  • Dispose pattern
  • DisposeRequested property with cancellation token
  • Disables all re-renders by Blazor and only allows re-renders when some signal is changed
  • Default Scope property
  • Factory methods for creating signals
  • Support for turning Blazor input parameters into signals
  • Automatically disposing of effects

Scope

Each component has its own scope accessible with the Scope property. The scope is created when the component is instantiated. You don't have to use this scope, but it's there if you need it.

[Watch] attribute

The [Watch] attribute is used to mark a signal that should be watched for changes. When the signal changes, the component will re-render. Changes to any other signal will not cause the component to re-render.

This attribute can be used on any field or property of these types:

  • ISignal<T>
  • IWritableSignal<T>
  • IResource<T>

Inputs

Any input parameter of the component can be turned into a signal. All you have to do is add the Input attribute to the parameter and mark it as public and partial.

The source generator will generate a new private property with name Input{ParameterName}. This property is a signal that will be updated when the parameter changes.

Of course attributes [Input] and [Watch] can be used together.

using DKX.Signals;
using DKX.Signals.Blazor;
using DKX.Signals.Blazor.Attributes;

public sealed partial class Counter : SignalsComponentBase
{
    [Watch]
    private readonly IWritableSignal<int> _counter;

    [Watch]
    private readonly ISignal<int> _result;

    public Counter()
    {
        _counter = Scope.Signal(0);

        // InputMultiplier is automatically generated signal based on the Multiplier parameter
        _result = Scope.Computed(new { _counter, InputMultiplier }, static deps => deps._counter.Get() * deps.InputMultiplier.Get());
    }

    [Parameter, Input]
    public partial int Multiplier { get; set; } = 1;

    private void IncrementCount()
    {
        _counter.Update(static x => x + 1);
    }
}

Effects

Effects in Blazor works a bit differently than in the main project:

  • Effects are automatically disposed of when the component is disposed.
  • You can't create effects in the constructor. You have to create them in the OnInitialized method or later.

AsyncEffect

There is a special type of effect that is accessible only in components. It allows you to use async methods in the effect. That is useful when you want to, for example, fire an event when a signal changes.

public sealed partial class Counter : SignalsComponentBase
{
    [Watch]
    private readonly IWritableSignal<int> _counter;

    public Counter()
    {
        _counter = Scope.Signal(0);
    }

    [Parameter]
    public EventCallback<int> OnCountChanged { get; set; }

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

        // Effect is automatically disposed of and can't be created in the constructor
        Scope.AsyncEffect(
            new { _counter },
            async deps =>
            {
                await OnCountChanged.InvokeAsync(deps._counter.Get());
            }
        );
    }

    private void IncrementCount()
    {
        _counter.Update(x => x + 1);
    }
}

Resources

Resource in Blazor have two differences compared to the main project:

  • They are automatically disposed of when the component is disposed.
  • They stay in idle state when created in the constructor until the component is initialized.

Store

Store is most useful when you want to share signals between components. You can use [Parameter], [CascadingParameter] or any other way to pass the store to the component.

using DKX.Signals;
using DKX.Signals.Blazor;
using DKX.Signals.Blazor.Attributes;

public sealed class MyStore : IStore
{
    public IScope Scope { get; } = ScopeBuilder.Default.Create();
    
    public readonly ISignal<int> Counter;
    
    public MyStore()
    {
        Counter = Scope.Signal(0);
    }
    
    public void Increment()
    {
        Counter.Update(static x => x + 1);
    }
}

public sealed partial class GlobalCounter : SignalsComponentBase
{
    [CascadingParameter]
    public required MyStore Store { get; set; }
    
    // Expose the store signal to the component
    [Watch] // <-- This attribute is required to watch re-render GlobalCounter when Store.Counter changes
    public ISignal<int> Counter => Store.Counter;

    private void IncrementCount()
    {
        Store.Increment();
    }
}

Customize component scope

It's possible to provide customized ScopeBuilder to the component. This is useful when you want to for example enable logging.

public sealed partial class Counter : SignalsComponentBase
{
    // ...
    
    // Or register the prepared custom ScopeBuilder in DI and use it here
    public Counter(ILoggerFactory loggerFactory)
        : base(ScopeBuilder.Default.WithLoggerFactory(loggerFactory))
    {
    }
    
    // ...
}
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0-rc.11 116 5/8/2025
1.0.0-rc.10 116 5/8/2025
1.0.0-rc.9 112 5/7/2025
1.0.0-rc.8 113 5/7/2025
1.0.0-rc.7 112 5/6/2025
1.0.0-rc.6 115 5/4/2025
1.0.0-rc.5 39 5/3/2025
1.0.0-rc.4 50 5/3/2025
1.0.0-rc.3 44 5/3/2025
1.0.0-rc.2 75 5/2/2025
1.0.0-rc.1 114 5/1/2025