I can't seem to solve this without using integers or different class or event names per button (which isn't scalable). I have a custom ripple-effect that I've created in Blazor and need each button on the page to receive a "ripple" class when clicked and then remove that class after a second or two.
@using System.Timers
<button class="primary-btn @buttonClass" @onclick="AddRipple">Action 1</button>
<button class="primary-btn @buttonClass" @onclick="AddRipple">Action 2</button>
@code {
// Set Ripple Class
private string buttonClass = "";
private void AddRipple()
{
buttonClass = "ripple";
StartTimer();
}
private void StartTimer()
{
var timer = new Timer(1000);
timer.Elapsed += RemoveClass;
timer.AutoReset = false;
timer.Start();
}
private void RemoveClass(object source, ElapsedEventArgs e)
{
buttonClass = "";
((Timer)source).Dispose();
InvokeAsync(StateHasChanged);
}
}
I have created and tested this functionality, but it applies the class change to every button on the page when any one button is clicked. How can I make the event fire and apply only the class change on the actual button clicked?
--UPDATE-- I created a custom razor component and that worked beautifully. I should have realized that! DOH! Here is a step by step guide if anyone is new at this like me: https://toxigon.com/building-reusable-components-in-blazor
Your thinking about this the wrong way. Build the functionality into a custom button component. Totally scalable.
Here's a demo MyButton
component that changes colour for 2 seconds. It uses the primitive System.Threading.Timer
to load a one time timer task onto the application timer. When this expires it calls OnTimerExpired
which renders the component. The component implements IDisposable
to clear down any running timers when the component is disposed.
@implements IDisposable
<button class="@_buttonClass" @onclick="AddRipple">@ChildContent</button>
@code {
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public EventCallback OnClick { get; set; }
private IDisposable? _disposer;
private bool _rippling;
private string _buttonClass => _rippling ? "btn btn-danger" : "btn btn-success";
private async Task AddRipple()
{
_rippling = true;
_disposer = new System.Threading.Timer(this.OnTimerExpired, null, TimeSpan.FromSeconds(2), TimeSpan.Zero);
await OnClick.InvokeAsync();
}
private void OnTimerExpired(object? data)
{
_rippling = false;
this.InvokeAsync(StateHasChanged);
}
public void Dispose()
{
_disposer?.Dispose();
}
}
See https://shauncurtis.github.io/Posts/Timers.html for more detailed info on timers.