Skip to content

Commit 65051aa

Browse files
Custom event args: support receiving IJSObjectReference (#31591)
* Add failing E2E case * Implement fix * Add E2E test * Update src/Components/test/testassets/BasicTestApp/EventCustomArgsComponent.razor * Update suppressions
1 parent 52eff90 commit 65051aa

File tree

11 files changed

+54
-10
lines changed

11 files changed

+54
-10
lines changed

src/Components/Server/src/Circuits/CircuitHost.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ public async Task DispatchEvent(string eventDescriptorJson, string eventArgsJson
404404
WebEventData webEventData;
405405
try
406406
{
407-
webEventData = WebEventData.Parse(Renderer, eventDescriptorJson, eventArgsJson);
407+
var jsonSerializerOptions = JSRuntime.ReadJsonSerializerOptions();
408+
webEventData = WebEventData.Parse(Renderer, jsonSerializerOptions, eventDescriptorJson, eventArgsJson);
408409
}
409410
catch (Exception ex)
410411
{

src/Components/Server/src/Circuits/RemoteJSRuntime.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public RemoteJSRuntime(IOptions<CircuitOptions> options, ILogger<RemoteJSRuntime
3030
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
3131
}
3232

33+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
34+
3335
internal void Initialize(CircuitClientProxy clientProxy)
3436
{
3537
_clientProxy = clientProxy ?? throw new ArgumentNullException(nameof(clientProxy));

src/Components/Shared/src/WebEventData.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal class WebEventData
1515
{
1616
// This class represents the second half of parsing incoming event data,
1717
// once the event ID (and possibly the type of the eventArgs) becomes known.
18-
public static WebEventData Parse(Renderer renderer, string eventDescriptorJson, string eventArgsJson)
18+
public static WebEventData Parse(Renderer renderer, JsonSerializerOptions jsonSerializerOptions, string eventDescriptorJson, string eventArgsJson)
1919
{
2020
WebEventDescriptor eventDescriptor;
2121
try
@@ -29,13 +29,14 @@ public static WebEventData Parse(Renderer renderer, string eventDescriptorJson,
2929

3030
return Parse(
3131
renderer,
32+
jsonSerializerOptions,
3233
eventDescriptor,
3334
eventArgsJson);
3435
}
3536

36-
public static WebEventData Parse(Renderer renderer, WebEventDescriptor eventDescriptor, string eventArgsJson)
37+
public static WebEventData Parse(Renderer renderer, JsonSerializerOptions jsonSerializerOptions, WebEventDescriptor eventDescriptor, string eventArgsJson)
3738
{
38-
var parsedEventArgs = ParseEventArgsJson(renderer, eventDescriptor.EventHandlerId, eventDescriptor.EventName, eventArgsJson);
39+
var parsedEventArgs = ParseEventArgsJson(renderer, jsonSerializerOptions, eventDescriptor.EventHandlerId, eventDescriptor.EventName, eventArgsJson);
3940
return new WebEventData(
4041
eventDescriptor.BrowserRendererId,
4142
eventDescriptor.EventHandlerId,
@@ -59,7 +60,7 @@ private WebEventData(int browserRendererId, ulong eventHandlerId, EventFieldInfo
5960

6061
public EventArgs EventArgs { get; }
6162

62-
private static EventArgs ParseEventArgsJson(Renderer renderer, ulong eventHandlerId, string eventName, string eventArgsJson)
63+
private static EventArgs ParseEventArgsJson(Renderer renderer, JsonSerializerOptions jsonSerializerOptions, ulong eventHandlerId, string eventName, string eventArgsJson)
6364
{
6465
try
6566
{
@@ -70,7 +71,7 @@ private static EventArgs ParseEventArgsJson(Renderer renderer, ulong eventHandle
7071

7172
// For custom events, the args type is determined from the associated delegate
7273
var eventArgsType = renderer.GetEventArgsType(eventHandlerId);
73-
return (EventArgs)JsonSerializer.Deserialize(eventArgsJson, eventArgsType, JsonSerializerOptionsProvider.Options)!;
74+
return (EventArgs)JsonSerializer.Deserialize(eventArgsJson, eventArgsType, jsonSerializerOptions)!;
7475
}
7576
catch (Exception e)
7677
{

src/Components/WebAssembly/WebAssembly/src/Infrastructure/JSInteropMethods.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public static void NotifyLocationChanged(string uri, bool isInterceptedLink)
3838
public static Task DispatchEvent(WebEventDescriptor eventDescriptor, string eventArgsJson)
3939
{
4040
var renderer = RendererRegistry.Find(eventDescriptor.BrowserRendererId);
41-
var webEvent = WebEventData.Parse(renderer, eventDescriptor, eventArgsJson);
41+
var jsonSerializerOptions = DefaultWebAssemblyJSRuntime.Instance.ReadJsonSerializerOptions();
42+
var webEvent = WebEventData.Parse(renderer, jsonSerializerOptions, eventDescriptor, eventArgsJson);
4243
return renderer.DispatchEventAsync(
4344
webEvent.EventHandlerId,
4445
webEvent.EventFieldInfo,

src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.WarningSuppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<argument>ILLink</argument>
3636
<argument>IL2072</argument>
3737
<property name="Scope">member</property>
38-
<property name="Target">M:Microsoft.AspNetCore.Components.Web.WebEventData.ParseEventArgsJson(Microsoft.AspNetCore.Components.RenderTree.Renderer,System.UInt64,System.String,System.String)</property>
38+
<property name="Target">M:Microsoft.AspNetCore.Components.Web.WebEventData.ParseEventArgsJson(Microsoft.AspNetCore.Components.RenderTree.Renderer,System.Text.Json.JsonSerializerOptions,System.UInt64,System.String,System.String)</property>
3939
</attribute>
4040
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
4141
<argument>ILLink</argument>

src/Components/WebAssembly/WebAssembly/src/Services/DefaultWebAssemblyJSRuntime.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics.CodeAnalysis;
55
using System.Globalization;
6+
using System.Text.Json;
67
using Microsoft.JSInterop.Infrastructure;
78
using Microsoft.JSInterop.WebAssembly;
89

@@ -23,6 +24,8 @@ private DefaultWebAssemblyJSRuntime()
2324
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
2425
}
2526

27+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
28+
2629
// The following methods are invoke via Mono's JS interop mechanism (invoke_method)
2730
public static string? InvokeDotNet(string assemblyName, string methodIdentifier, string dotNetObjectId, string argsJson)
2831
{

src/Components/WebView/WebView/src/IpcReceiver.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ private void EndInvokeJS(PageContext pageContext, long asyncHandle, bool succeed
8888
private Task DispatchBrowserEventAsync(PageContext pageContext, string eventDescriptor, string eventArgs)
8989
{
9090
var renderer = pageContext.Renderer;
91-
var webEventData = WebEventData.Parse(renderer, eventDescriptor, eventArgs);
91+
var jsonSerializerOptions = pageContext.JSRuntime.ReadJsonSerializerOptions();
92+
var webEventData = WebEventData.Parse(renderer, jsonSerializerOptions, eventDescriptor, eventArgs);
9293
return renderer.DispatchEventAsync(
9394
webEventData.EventHandlerId,
9495
webEventData.EventFieldInfo,

src/Components/WebView/WebView/src/Services/WebViewJSRuntime.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public void AttachToWebView(IpcSender ipcSender)
2323
_ipcSender = ipcSender;
2424
}
2525

26+
public JsonSerializerOptions ReadJsonSerializerOptions() => JsonSerializerOptions;
27+
2628
protected override void BeginInvokeJS(long taskId, string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId)
2729
{
2830
_ipcSender.BeginInvokeJS(taskId, identifier, argsJson, resultType, targetInstanceId);

src/Components/test/E2ETest/Tests/EventCustomArgsTest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ public void CanAliasBrowserEvent_WithoutAnyNativeListenerForBrowserEvent()
162162
Browser.True(() => GetLogLines().Contains("Received custom mouseover event"));
163163
}
164164

165+
[Fact]
166+
public void CanRegisterCustomEventAndSupplyIJSObjectReference()
167+
{
168+
Browser.Exists(By.Id("register-sendjsobject")).Click();
169+
Browser.FindElement(By.Id("trigger-sendjsobject-event-directly")).Click();
170+
Browser.Equal("Event with IJSObjectReference received: Hello!", () => GetLogLines().Single());
171+
}
172+
165173
void SendKeysSequentially(IWebElement target, string text)
166174
{
167175
foreach (var c in text)

src/Components/test/testassets/BasicTestApp/EventCustomArgsComponent.razor

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
@ontestevent="@HandleTestEvent"
1010
@onkeydown.testvariant="@HandleCustomKeyDown"
1111
@onkeydown.yetanother="@HandleYetAnotherKeyboardEvent"
12-
@oncustommouseover="@(e => { LogMessage("Received custom mouseover event"); })">
12+
@oncustommouseover="@(e => { LogMessage("Received custom mouseover event"); })"
13+
@onsendjsobject="HandleEventWithIJSObjectReference">
1314
Event target
1415
<div id="test-event-target-child" style="background: #afa; padding: 1em;">
1516
Child
@@ -55,6 +56,17 @@
5556
Register custom mouseover event (which has no corresponding native listener)
5657
</button>
5758

59+
<button id="register-sendjsobject"
60+
onclick="Blazor.registerCustomEventType('sendjsobject', { createEventArgs: event => event.detail })">
61+
Register custom event that sends IJSObjectReference
62+
</button>
63+
64+
<button id="trigger-sendjsobject-event-directly"
65+
onclick="document.getElementById('test-event-target-child').dispatchEvent(new CustomEvent('sendjsobject', { bubbles: true, detail: { jsObject: DotNet.createJSObjectReference({ getMyValue: () => 'Hello!' }) } }))">
66+
Trigger sendjsobject event directly
67+
</button>
68+
69+
5870
<p>
5971
<label>
6072
<input type="checkbox" id="custom-keydown-prevent-default" @bind="customKeyDownPreventDefault" />
@@ -99,4 +111,10 @@
99111
{
100112
LogMessage($"Yet another aliased event received: {eventArgs.YouPressed}");
101113
}
114+
115+
async Task HandleEventWithIJSObjectReference(EventWithIJSObjectReferenceEventArgs eventArgs)
116+
{
117+
var innerValue = await eventArgs.JsObject.InvokeAsync<string>("getMyValue", null);
118+
LogMessage($"Event with IJSObjectReference received: {innerValue}");
119+
}
102120
}

src/Components/test/testassets/BasicTestApp/EventCustomArgsTypes.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System;
22
using Microsoft.AspNetCore.Components;
3+
using Microsoft.JSInterop;
34

45
namespace BasicTestApp.CustomEventTypesNamespace
56
{
67
[EventHandler("ontestevent", typeof(TestEventArgs), true, true)]
78
[EventHandler("onkeydown.testvariant", typeof(TestKeyDownEventArgs), true, true)]
89
[EventHandler("onkeydown.yetanother", typeof(YetAnotherCustomKeyboardEventArgs), true, true)]
910
[EventHandler("oncustommouseover", typeof(EventArgs), true, true)]
11+
[EventHandler("onsendjsobject", typeof(EventWithIJSObjectReferenceEventArgs), true, true)]
1012
public static class EventHandlers
1113
{
1214
}
@@ -25,4 +27,9 @@ class YetAnotherCustomKeyboardEventArgs : EventArgs
2527
{
2628
public string YouPressed { get; set; }
2729
}
30+
31+
class EventWithIJSObjectReferenceEventArgs : EventArgs
32+
{
33+
public IJSObjectReference JsObject { get; set; }
34+
}
2835
}

0 commit comments

Comments
 (0)