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:
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/building-reusable-components-in-blazor
Share Improve this question edited Mar 14 at 13:46 jottin asked Mar 14 at 0:59 jottinjottin 4312 silver badges13 bronze badges2 Answers
Reset to default 1Your 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.
From my understanding you need a list to control the state of each button class which uses independent timer.
@page "/"
<style>
.ripple{
color:red;
}
</style>
@using System.Timers
<button class="primary-btn @buttonClass[1]" @onclick="()=>AddRipple(1)">Action 1</button>
<button class="primary-btn @buttonClass[2]" @onclick="()=>AddRipple(2)">Action 2</button>
@code {
//create list of 3 items to match index 1 and 2.
private List<string> buttonClass = new List<string>(new string[3]);
private void AddRipple(int i)
{
buttonClass[i] = "ripple";
StartTimer(i);
}
private void StartTimer(int i)
{
var timer = new Timer(1000);
timer.Elapsed += (sender,e)=> RemoveClass(sender,e,i);
timer.AutoReset = false;
timer.Start();
}
private void RemoveClass(object source, ElapsedEventArgs e,int i)
{
buttonClass[i] = "";
((Timer)source).Dispose();
InvokeAsync(StateHasChanged);
}
}