248 lines
11 KiB
Markdown
248 lines
11 KiB
Markdown
|
---
|
||
|
title: ASP.NET Core Blazor event handling
|
||
|
author: guardrex
|
||
|
description: Learn about Blazor's event handling scenarios, including event argument types, event callbacks, and managing default browser events.
|
||
|
monikerRange: '>= aspnetcore-3.1'
|
||
|
ms.author: riande
|
||
|
ms.custom: mvc
|
||
|
ms.date: 02/12/2020
|
||
|
no-loc: [Blazor, SignalR]
|
||
|
uid: blazor/event-handling
|
||
|
---
|
||
|
# ASP.NET Core Blazor event handling
|
||
|
|
||
|
By [Luke Latham](https://github.com/guardrex) and [Daniel Roth](https://github.com/danroth27)
|
||
|
|
||
|
Razor components provide event handling features. For an HTML element attribute named `on{EVENT}` (for example, `onclick` and `onsubmit`) with a delegate-typed value, Razor components treats the attribute's value as an event handler. The attribute's name is always formatted [`@on{EVENT}`](xref:mvc/views/razor#onevent).
|
||
|
|
||
|
The following code calls the `UpdateHeading` method when the button is selected in the UI:
|
||
|
|
||
|
```razor
|
||
|
<button class="btn btn-primary" @onclick="UpdateHeading">
|
||
|
Update heading
|
||
|
</button>
|
||
|
|
||
|
@code {
|
||
|
private void UpdateHeading(MouseEventArgs e)
|
||
|
{
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The following code calls the `CheckChanged` method when the check box is changed in the UI:
|
||
|
|
||
|
```razor
|
||
|
<input type="checkbox" class="form-check-input" @onchange="CheckChanged" />
|
||
|
|
||
|
@code {
|
||
|
private void CheckChanged()
|
||
|
{
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Event handlers can also be asynchronous and return a <xref:System.Threading.Tasks.Task>. There's no need to manually call [StateHasChanged](xref:blazor/lifecycle#state-changes). Exceptions are logged when they occur.
|
||
|
|
||
|
In the following example, `UpdateHeading` is called asynchronously when the button is selected:
|
||
|
|
||
|
```razor
|
||
|
<button class="btn btn-primary" @onclick="UpdateHeading">
|
||
|
Update heading
|
||
|
</button>
|
||
|
|
||
|
@code {
|
||
|
private async Task UpdateHeading(MouseEventArgs e)
|
||
|
{
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Event argument types
|
||
|
|
||
|
For some events, event argument types are permitted. If access to one of these event types isn't necessary, it isn't required in the method call.
|
||
|
|
||
|
Supported `EventArgs` are shown in the following table.
|
||
|
|
||
|
| Event | Class | DOM events and notes |
|
||
|
| ---------------- | -------------------- | -------------------- |
|
||
|
| Clipboard | `ClipboardEventArgs` | `oncut`, `oncopy`, `onpaste` |
|
||
|
| Drag | `DragEventArgs` | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend`<br><br>`DataTransfer` and `DataTransferItem` hold dragged item data. |
|
||
|
| Error | `ErrorEventArgs` | `onerror` |
|
||
|
| Event | `EventArgs` | *General*<br>`onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onended`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll`<br><br>*Clipboard*<br>`onbeforecut`, `onbeforecopy`, `onbeforepaste`<br><br>*Input*<br>`oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit`<br><br>*Media*<br>`oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `onvolumechange`, `onwaiting` |
|
||
|
| Focus | `FocusEventArgs` | `onfocus`, `onblur`, `onfocusin`, `onfocusout`<br><br>Doesn't include support for `relatedTarget`. |
|
||
|
| Input | `ChangeEventArgs` | `onchange`, `oninput` |
|
||
|
| Keyboard | `KeyboardEventArgs` | `onkeydown`, `onkeypress`, `onkeyup` |
|
||
|
| Mouse | `MouseEventArgs` | `onclick`, `oncontextmenu`, `ondblclick`, `onmousedown`, `onmouseup`, `onmouseover`, `onmousemove`, `onmouseout` |
|
||
|
| Mouse pointer | `PointerEventArgs` | `onpointerdown`, `onpointerup`, `onpointercancel`, `onpointermove`, `onpointerover`, `onpointerout`, `onpointerenter`, `onpointerleave`, `ongotpointercapture`, `onlostpointercapture` |
|
||
|
| Mouse wheel | `WheelEventArgs` | `onwheel`, `onmousewheel` |
|
||
|
| Progress | `ProgressEventArgs` | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` |
|
||
|
| Touch | `TouchEventArgs` | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel`<br><br>`TouchPoint` represents a single contact point on a touch-sensitive device. |
|
||
|
|
||
|
For more information, see the following resources:
|
||
|
|
||
|
* [EventArgs classes in the ASP.NET Core reference source (dotnet/aspnetcore release/3.1 branch)](https://github.com/dotnet/aspnetcore/tree/release/3.1/src/Components/Web/src/Web).
|
||
|
* [MDN web docs: GlobalEventHandlers](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers) – Includes information on which HTML elements support each DOM event.
|
||
|
|
||
|
## Lambda expressions
|
||
|
|
||
|
[Lambda expressions](/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions) can also be used:
|
||
|
|
||
|
```razor
|
||
|
<button @onclick="@(e => Console.WriteLine("Hello, world!"))">Say hello</button>
|
||
|
```
|
||
|
|
||
|
It's often convenient to close over additional values, such as when iterating over a set of elements. The following example creates three buttons, each of which calls `UpdateHeading` passing an event argument (`MouseEventArgs`) and its button number (`buttonNumber`) when selected in the UI:
|
||
|
|
||
|
```razor
|
||
|
<h2>@_message</h2>
|
||
|
|
||
|
@for (var i = 1; i < 4; i++)
|
||
|
{
|
||
|
var buttonNumber = i;
|
||
|
|
||
|
<button class="btn btn-primary"
|
||
|
@onclick="@(e => UpdateHeading(e, buttonNumber))">
|
||
|
Button #@i
|
||
|
</button>
|
||
|
}
|
||
|
|
||
|
@code {
|
||
|
private string _message = "Select a button to learn its position.";
|
||
|
|
||
|
private void UpdateHeading(MouseEventArgs e, int buttonNumber)
|
||
|
{
|
||
|
_message = $"You selected Button #{buttonNumber} at " +
|
||
|
$"mouse position: {e.ClientX} X {e.ClientY}.";
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
> [!NOTE]
|
||
|
> Do **not** use the loop variable (`i`) in a `for` loop directly in a lambda expression. Otherwise the same variable is used by all lambda expressions causing `i`'s value to be the same in all lambdas. Always capture its value in a local variable (`buttonNumber` in the preceding example) and then use it.
|
||
|
|
||
|
## EventCallback
|
||
|
|
||
|
A common scenario with nested components is the desire to run a parent component's method when a child component event occurs—for example, when an `onclick` event occurs in the child. To expose events across components, use an `EventCallback`. A parent component can assign a callback method to a child component's `EventCallback`.
|
||
|
|
||
|
The `ChildComponent` in the sample app (*Components/ChildComponent.razor*) demonstrates how a button's `onclick` handler is set up to receive an `EventCallback` delegate from the sample's `ParentComponent`. The `EventCallback` is typed with `MouseEventArgs`, which is appropriate for an `onclick` event from a peripheral device:
|
||
|
|
||
|
[!code-razor[](common/samples/3.x/BlazorWebAssemblySample/Components/ChildComponent.razor?highlight=5-7,17-18)]
|
||
|
|
||
|
The `ParentComponent` sets the child's `EventCallback<T>` (`OnClickCallback`) to its `ShowMessage` method.
|
||
|
|
||
|
*Pages/ParentComponent.razor*:
|
||
|
|
||
|
```razor
|
||
|
@page "/ParentComponent"
|
||
|
|
||
|
<h1>Parent-child example</h1>
|
||
|
|
||
|
<ChildComponent Title="Panel Title from Parent"
|
||
|
OnClickCallback="@ShowMessage">
|
||
|
Content of the child component is supplied
|
||
|
by the parent component.
|
||
|
</ChildComponent>
|
||
|
|
||
|
<p><b>@_messageText</b></p>
|
||
|
|
||
|
@code {
|
||
|
private string _messageText;
|
||
|
|
||
|
private void ShowMessage(MouseEventArgs e)
|
||
|
{
|
||
|
_messageText = $"Blaze a new trail with Blazor! ({e.ScreenX}, {e.ScreenY})";
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
When the button is selected in the `ChildComponent`:
|
||
|
|
||
|
* The `ParentComponent`'s `ShowMessage` method is called. `_messageText` is updated and displayed in the `ParentComponent`.
|
||
|
* A call to [StateHasChanged](xref:blazor/lifecycle#state-changes) isn't required in the callback's method (`ShowMessage`). `StateHasChanged` is called automatically to rerender the `ParentComponent`, just as child events trigger component rerendering in event handlers that execute within the child.
|
||
|
|
||
|
`EventCallback` and `EventCallback<T>` permit asynchronous delegates. `EventCallback<T>` is strongly typed and requires a specific argument type. `EventCallback` is weakly typed and allows any argument type.
|
||
|
|
||
|
```razor
|
||
|
<ChildComponent
|
||
|
OnClickCallback="@(async () => { await Task.Yield(); _messageText = "Blaze It!"; })" />
|
||
|
```
|
||
|
|
||
|
Invoke an `EventCallback` or `EventCallback<T>` with `InvokeAsync` and await the <xref:System.Threading.Tasks.Task>:
|
||
|
|
||
|
```csharp
|
||
|
await callback.InvokeAsync(arg);
|
||
|
```
|
||
|
|
||
|
Use `EventCallback` and `EventCallback<T>` for event handling and binding component parameters.
|
||
|
|
||
|
Prefer the strongly typed `EventCallback<T>` over `EventCallback`. `EventCallback<T>` provides better error feedback to users of the component. Similar to other UI event handlers, specifying the event parameter is optional. Use `EventCallback` when there's no value passed to the callback.
|
||
|
|
||
|
## Prevent default actions
|
||
|
|
||
|
Use the [`@on{EVENT}:preventDefault`](xref:mvc/views/razor#oneventpreventdefault) directive attribute to prevent the default action for an event.
|
||
|
|
||
|
When a key is selected on an input device and the element focus is on a text box, a browser normally displays the key's character in the text box. In the following example, the default behavior is prevented by specifying the `@onkeypress:preventDefault` directive attribute. The counter increments, and the **+** key isn't captured into the `<input>` element's value:
|
||
|
|
||
|
```razor
|
||
|
<input value="@_count" @onkeypress="KeyHandler" @onkeypress:preventDefault />
|
||
|
|
||
|
@code {
|
||
|
private int _count = 0;
|
||
|
|
||
|
private void KeyHandler(KeyboardEventArgs e)
|
||
|
{
|
||
|
if (e.Key == "+")
|
||
|
{
|
||
|
_count++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Specifying the `@on{EVENT}:preventDefault` attribute without a value is equivalent to `@on{EVENT}:preventDefault="true"`.
|
||
|
|
||
|
The value of the attribute can also be an expression. In the following example, `_shouldPreventDefault` is a `bool` field set to either `true` or `false`:
|
||
|
|
||
|
```razor
|
||
|
<input @onkeypress:preventDefault="_shouldPreventDefault" />
|
||
|
```
|
||
|
|
||
|
An event handler isn't required to prevent the default action. The event handler and prevent default action scenarios can be used independently.
|
||
|
|
||
|
## Stop event propagation
|
||
|
|
||
|
Use the [`@on{EVENT}:stopPropagation`](xref:mvc/views/razor#oneventstoppropagation) directive attribute to stop event propagation.
|
||
|
|
||
|
In the following example, selecting the check box prevents click events from the second child `<div>` from propagating to the parent `<div>`:
|
||
|
|
||
|
```razor
|
||
|
<label>
|
||
|
<input @bind="_stopPropagation" type="checkbox" />
|
||
|
Stop Propagation
|
||
|
</label>
|
||
|
|
||
|
<div @onclick="OnSelectParentDiv">
|
||
|
<h3>Parent div</h3>
|
||
|
|
||
|
<div @onclick="OnSelectChildDiv">
|
||
|
Child div that doesn't stop propagation when selected.
|
||
|
</div>
|
||
|
|
||
|
<div @onclick="OnSelectChildDiv" @onclick:stopPropagation="_stopPropagation">
|
||
|
Child div that stops propagation when selected.
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
@code {
|
||
|
private bool _stopPropagation = false;
|
||
|
|
||
|
private void OnSelectParentDiv() =>
|
||
|
Console.WriteLine($"The parent div was selected. {DateTime.Now}");
|
||
|
private void OnSelectChildDiv() =>
|
||
|
Console.WriteLine($"A child div was selected. {DateTime.Now}");
|
||
|
}
|
||
|
```
|