Skip to content

Conversation

dariatiurina
Copy link
Contributor

Added replacers to the JSInterop

Description

This pull request introduces support for returning ElementReference objects in JavaScript interop calls between .NET and JS. It does so by adding a mechanism to serialize DOM elements as ElementReference objects, and updates the test infrastructure to validate this new capability. The changes include updates to both the JS runtime and the .NET test components.

Changes:

  • Added a new attachReplacer API and corresponding infrastructure in Microsoft.JSInterop.ts to allow custom serialization of arguments sent from JS to .NET, enabling support for returning DOM elements as ElementReference objects.
  • Implemented logic in ElementReferenceCapture.ts to extract the capture ID from DOM elements and serialize them for interop calls, including support for both receiving and returning ElementReference instances.
  • Added a new test and UI element in InteropComponent.razor and InteropTest.cs to validate round-tripping of ElementReference between .NET and JS, including updates to the test logic and result reporting.

Fixes #23369

@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Sep 4, 2025
@dariatiurina dariatiurina marked this pull request as ready for review September 4, 2025 11:45
@Copilot Copilot AI review requested due to automatic review settings September 4, 2025 11:45
@dariatiurina dariatiurina requested a review from a team as a code owner September 4, 2025 11:45
@dariatiurina dariatiurina self-assigned this Sep 4, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces bidirectional support for ElementReference objects in JavaScript interop calls between .NET and JavaScript. The primary purpose is to enable DOM elements to be serialized and returned as ElementReference objects from JavaScript to .NET, complementing the existing support for passing ElementReference from .NET to JavaScript.

Key changes:

  • Added a new attachReplacer API to enable custom serialization of arguments sent from JavaScript to .NET
  • Implemented logic to serialize DOM elements as ElementReference objects using capture IDs
  • Added comprehensive test coverage to validate round-tripping of ElementReference between .NET and JavaScript

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Microsoft.JSInterop.ts Added attachReplacer API and integrated replacer logic into argument serialization
jsinteroptests.js Added returnElementReference test function to support element reference testing
InteropComponent.razor Added element reference and test logic for round-trip validation
InteropTest.cs Updated test expectations to include element reference success case
ElementReferenceCapture.ts Implemented replacer logic to serialize DOM elements as ElementReference objects

Copy link
Member

@ilonatommy ilonatommy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flow looks logical, I don't have more suggestions for improvements beyond cleaning console.log.

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, suggested a few tweaks

Comment on lines 40 to 48
DotNet.attachReplacer((key, value) => {
if (value instanceof Element) {
const captureId = getCaptureIdFromElement(value);
if (captureId) {
return { [elementRefKey]: captureId };
}
}
return value;
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding some public API that I don't know we want (anything in DotNet is public API)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing it like this, could we "hijack" JSObjectReference to achieve this?

By that, I mean using the same key as

const jsObjectIdKey = "__jsObjectId";
const dotNetObjectRefKey = "__dotNetObject";
const byteArrayRefKey = "__byte[]";
const dotNetStreamRefKey = "__dotNetStream";
const jsStreamReferenceLengthKey = "__jsStreamReferenceLength";

And then on the C# side (if necessary) have a custom converter for ElementReference, or have it look for the JSObjectIdKey too if there's already one.

Copy link
Member

@oroztocil oroztocil Sep 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more about adding an extensibility mechanism to processing values sent from JS to .NET that is symmetrical to "JSON revivers" used in the other direction (when handling arguments coming from .NET to JS). I am also not sure if we want to add that but we should evaluate it in these terms.

Alternatively, if we only care about supporting this use case with ElementReference, we can reuse the JSObjectReference ID representation and then special-case handling of Element instances in createJSCallResult in Microsoft.JSInterop.ts (or some other related function, I haven't look at it in detail).

@dariatiurina dariatiurina force-pushed the 23369-jsinterop-return-elem branch from 32accf1 to 32847a5 Compare September 5, 2025 07:27
@dariatiurina dariatiurina merged commit 65cebb7 into dotnet:main Sep 5, 2025
30 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the 11.0-preview1 milestone Sep 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Returning an HTML element from a JS interop call throws an exception.
5 participants