Practical ASP.NET
Suppressing Events in Blazor and ASP.NET Core Version 3.1
ASP.NET Core Version 3.1 adds some new features for managing events in Blazor. You may think you'll never need them, but there may be a time when you'll be glad to know about at least one of them.
ASP.NET Core Version 3.1 came out in December of 2019 and added some new features for managing events in Blazor. I'll be the first to admit that you might lead a long and happy life without using these features…but you never know. In fact, I'll claim at the end of this column that there's at least one scenario when you'll need this information.
Suppressing the Browser
First new feature: Many HTML DOM-related events have a default action associated with them -- the keypress event, for example, adds a character to a textbox (when the event happens in a textbox, of course). If you want, you can prevent those default actions from happening by using Blazor's new preventDefault directive with your method's name.
This code, for example, prevents the user from adding any characters to the textbox defined by this element:
<input type="text" value="" @onkeypress:preventDefault />
If you want to tie your own code to the event, you can still do that -- just repeat the event name and tie it to a method as you've done in the past. This example suppresses the default action for the event and calls a method named AuditKeyStrokes:
<input type="text" value="" @onkeypress="AuditKeyStrokes" @onkeypress:preventDefault />
Suppressing Bubbling: Static
Second: When an event is fired by an element in your UI, that event not only fires for that element but also bubbles up through all the elements the original element is nested inside of. This means that if, for example, you have one method that you want to apply to the click event of several different elements, you could just wrap those elements inside a div element and grab the onclick event in that div element.
This is a feature more likely to be useful in UI manipulation than when writing business logic. However, this example demonstrates how the feature might be applied to support business logic (though I think it matters which button the user clicks and I'm not sure how you'd determine that):
<div @onclick="CreateAnyKindOfOrder">
<button>Create Order</button>
<input type="button" value="Create Backorder" />
</div>
More reasonably, event propagation gives you the ability to factor common code into separate methods, making each method more focused. This code, for example, puts the audit function that applies to both types of orders in one method and the code for the two types of orders in the individual buttons:
<div @onclick="auditOrder">
<button @onclick="createBackOrder">Create Back Order</button>
<input type="button" @onclick="createSalesOrder" value="Create Sales Order" />
</div>
If you do this, however, it's not hard to imagine a scenario when you'd have a button whose event you don't want to bubble up to the enclosing div element's method -- canceling an order, for example. When that's the case, in the element where you want the event to stop, you reference the event and set its stopPropagation directive to true.
Incorporating a cancelOrder method that doesn't propagate into my example looks like this:
<div @onclick="auditOrder">
<button @onclick="createBackOrder">Create Back Order</button>
<input type="button" @onclick="createSalesOrder" value="Create Sales Order" />
<button @onclick="cancelOrder" @onclick:stopPropagation="true">Create Back Order</button>
</div>
Suppressing Bubbling: Dynamic
That's how to always stop propagation. However, you may want to stop propagation dynamically -- when one of the “inner” methods abends, for example. You can start to handle that by setting the stopPropagation directive to a Boolean field in your component. To get the default propagating behavior, you'd initialize that field to false.
This example stages me to dynamically stop propagation for both my createBackOrder and createSalesOrder elements while having the cancelElement always stop propagation:
<div @onclick="auditOrder">
<button @onclick="createBackOrder"
@onclick:stopPropagation="noPropagation">Create Back Order</button>
<input type="button" @onclick="createSalesOrder"
@onclick:stopPropagation="noPropagation" value="Create Sales Order" />
<button @onclick="cancelOrder" @onclick:stopPropagation="true">Create Back Order</button>
</div>
@code
{
private bool noPropagation = false;
Now, inside my methods, I can set noPropagation to false whenever anything goes wrong. As an example, my createBackOrder code might look like this:
private void createBackOrder()
{
try
{
//…code to create a back order
}
catch (Exception ex)
{
noPropagation = true;
//…code to handle error
}
}
And I do recognize that these are niche solutions that you might never need. However, I wouldn't be surprised if, some day, you find some element high up in your component's hierarchy firing an event that you didn't expect. When you investigate, it will turn out that the element with the unexpected event shares that event with some element nested deeply inside of it. Because of event propagation the event you want in the nested element is propagating up to the element where you don't want it.
That's the very moment that you'll be glad that you know how to stop events from propagating. You're welcome.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.