Skip to content

Can't pass IJSObjectReference in calls from JS to .NET #26049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JinShil opened this issue Sep 18, 2020 · 18 comments
Closed

Can't pass IJSObjectReference in calls from JS to .NET #26049

JinShil opened this issue Sep 18, 2020 · 18 comments
Labels
affected-most This issue impacts most of the customers area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-jsinterop This issue is related to JSInterop in Blazor severity-major This label is used by an internal tool

Comments

@JinShil
Copy link

JinShil commented Sep 18, 2020

Trying to return a DOM Event object as an IJSObjectReference throws an exception:

image

  1. What is an "Unexcepted JSON property"? Did you mean "Unexpected"?
  2. I expect the DOM event object to be passed as an IJSObjectReference to the JSInvokable OnClick function below.

To Reproduce

Bug.csproj

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0-rc.2.20460.25" />
	  <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0-rc.2.20460.25" PrivateAssets="all" />
	  <PackageReference Include="System.Net.Http.Json" Version="6.0.0-alpha.1.20420.3" />
  </ItemGroup>

</Project>

Javascript to add to index.html

window.interop = {
    AddEventListener(element, eventName, dotNetObj, dotNetMethod) {
        element.addEventListener(eventName, (e) => {
            dotNetObj.invokeMethod(dotNetMethod, e);
        });
    }
}

Index.razor

@page "/"

@inject IJSRuntime JS

<div @ref="_element" style="width: 200px; height: 200px; background-color: blue;"></div>

@code {
    ElementReference _element;

    [JSInvokable]
    public void OnClick(IJSObjectReference jsReference)
    {
        Console.WriteLine("Clicked");
    }

    protected override void OnAfterRender(bool firstRender)
    {
        base.OnAfterRender(firstRender);

        JS.InvokeVoidAsync("interop.AddEventListener", _element, "click", DotNetObjectReference.Create(this), nameof(OnClick));
    }
}
  1. Create a Blazor WASM project with the code above.
  2. Run it
  3. Click on the blue rectangle

Further technical details

dotnet --version
5.0.100-rc.1.20454.5
 dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.100-rc.1.20454.5
 Commit:    53a032d031

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19041
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.100-rc.1.20454.5\

Host (useful for support):
  Version: 5.0.0-rc.1.20451.14
  Commit:  38017c3935

.NET SDKs installed:
  2.1.4 [C:\Program Files\dotnet\sdk]
  2.1.100 [C:\Program Files\dotnet\sdk]
  2.1.101 [C:\Program Files\dotnet\sdk]
  2.1.102 [C:\Program Files\dotnet\sdk]
  2.1.103 [C:\Program Files\dotnet\sdk]
  2.1.104 [C:\Program Files\dotnet\sdk]
  2.1.201 [C:\Program Files\dotnet\sdk]
  2.1.202 [C:\Program Files\dotnet\sdk]
  2.1.301 [C:\Program Files\dotnet\sdk]
  2.1.400 [C:\Program Files\dotnet\sdk]
  2.1.401 [C:\Program Files\dotnet\sdk]
  2.1.402 [C:\Program Files\dotnet\sdk]
  2.1.403 [C:\Program Files\dotnet\sdk]
  2.1.500 [C:\Program Files\dotnet\sdk]
  2.1.502 [C:\Program Files\dotnet\sdk]
  2.1.503 [C:\Program Files\dotnet\sdk]
  2.1.504 [C:\Program Files\dotnet\sdk]
  2.1.505 [C:\Program Files\dotnet\sdk]
  2.1.507 [C:\Program Files\dotnet\sdk]
  2.1.600 [C:\Program Files\dotnet\sdk]
  2.1.601 [C:\Program Files\dotnet\sdk]
  2.1.602 [C:\Program Files\dotnet\sdk]
  2.1.604 [C:\Program Files\dotnet\sdk]
  2.1.700 [C:\Program Files\dotnet\sdk]
  2.1.801 [C:\Program Files\dotnet\sdk]
  2.2.103 [C:\Program Files\dotnet\sdk]
  3.0.100-preview9-014004 [C:\Program Files\dotnet\sdk]
  3.0.100 [C:\Program Files\dotnet\sdk]
  3.1.100 [C:\Program Files\dotnet\sdk]
  3.1.200 [C:\Program Files\dotnet\sdk]
  3.1.301 [C:\Program Files\dotnet\sdk]
  3.1.402 [C:\Program Files\dotnet\sdk]
  5.0.100-rc.1.20452.10 [C:\Program Files\dotnet\sdk]
  5.0.100-rc.1.20454.5 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.All 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.All 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.0-preview9.19424.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.0-rc.1.20451.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.3-servicing-26724-03 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.1.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 2.2.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0-preview.7.20364.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.0-rc.1.20451.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.0.0-preview9-19423-09 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.7 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.0-rc.1.20452.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
@JinShil JinShil changed the title Can't return DOM Event object to IJSObjectReference Can't return DOM Event object to C# as an IJSObjectReference Sep 18, 2020
@JinShil JinShil changed the title Can't return DOM Event object to C# as an IJSObjectReference Can't pass a DOM Event object to C# as an IJSObjectReference Sep 18, 2020
@pranavkm pranavkm added the area-blazor Includes: Blazor, Razor Components label Sep 18, 2020
@captainsafia captainsafia added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Sep 22, 2020
@captainsafia captainsafia added this to the Backlog milestone Sep 22, 2020
@ghost
Copy link

ghost commented Sep 22, 2020

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@SteveSandersonMS SteveSandersonMS changed the title Can't pass a DOM Event object to C# as an IJSObjectReference Can't pass IJSObjectReference in calls from JS to .NET Oct 7, 2020
@SteveSandersonMS SteveSandersonMS added affected-most This issue impacts most of the customers severity-major This label is used by an internal tool labels Oct 7, 2020 — with ASP.NET Core Issue Ranking
@javiercn javiercn added the feature-blazor-jsinterop This issue is related to JSInterop in Blazor label Apr 20, 2021
@user72356
Copy link

Same issue here when trying to send objects from JS to .Net in a callback situation. I would like to receive the objects as IJSObjectReference on the .Net side and not have JSON attempt to serialize them. Is there an inverse to attachReviver so that I may influence the serialization process from JS to .Net? Is it attachDispatcher??

@Jasonizback
Copy link

Jasonizback commented Sep 14, 2021 via email

@user72356
Copy link

You've been hacked! Evil laughter

@TanayParikh
Copy link
Contributor

  • What is an "Unexcepted JSON property"? Did you mean "Unexpected"?

Yes, this was a typo and has been fixed for 6.0

@KristofferStrube
Copy link
Contributor

The progress on solving this problem is also kept track of in issue #31151

@ghost
Copy link

ghost commented Oct 21, 2022

Thanks for contacting us.

We're moving this issue to the .NET 8 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@SteveSandersonMS
Copy link
Member

This is a bug in the application code. The JavaScript code should look like this:

dotNetObj.invokeMethod(dotNetMethod, DotNet.createJSObjectReference(e));

That is, you must explicitly construct the JS object reference via DotNet.createJSObjectReference for calls originating from JS, otherwise the system has no way to know that's how you want to serialize the parameter value.

@guardrex Do you know if this is covered in docs anywhere? I searched through https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-7.0 and the createJSObjectReference API doesn't appear to be mentioned. This might be something we missed.

@SteveSandersonMS
Copy link
Member

Yes, this was a typo and has been fixed for 6.0

@TanayParikh Was it definitely fixed? I was still able to repro this message on 7.0:

image

@guardrex
Copy link
Contributor

guardrex commented Nov 9, 2022

@SteveSandersonMS ... Correct ... not covered ... so I'll open an issue and hopefully reach it by the end of next week. I have about a week of .NET 7 wrap-up things to do first.

@TanayParikh
Copy link
Contributor

Was it definitely fixed? I was still able to repro this message on 7.0:

That was in reference to a correction I'd made in one of my streaming/byte[] interop PRs. But just took another look and there are a lot of places we say excepted instead of expected.

#44989

@guardrex
Copy link
Contributor

guardrex commented Nov 11, 2022

@SteveSandersonMS @javiercn ... In @JinShil's example (in the context of creating a doc example to demonstrate this), why doesn't the event marshal back to .NET as a MouseEventArgs?

[JSInvokable]
public void OnClick(MouseEventArgs args)
{
    Console.WriteLine($"Mouse coordinates: {args.ClientX}:{args.ClientY}");
}

... because I think that the click event makes the Event (e) a MouseEvent. When I read e.clientX and e.clientY, I do get mouse coordinates. However on the .NET side, I'm getting ...

Mouse coordinates: 0:0

No 💥 ... but no values.

If it turns out that it should marshal back as a IJSObjectReference in this case (i.e., a click event on a <div>), what would be good to do with objRef to complete the demo for this?

[JSInvokable]
public void OnClick(IJSObjectReference objRef)
{
   // ??????????
}

@SteveSandersonMS
Copy link
Member

In @JinShil's example (in the context of creating a doc example to demonstrate this), why doesn't the event marshal back to .NET as a MouseEventArgs?

Because it isn't one. It's a JavaScript MouseEvent object, which not the same type as a .NET MouseEventArgs (even though they are clearly related to the same concept). For real Blazor event handlers, Blazor contains internal code that explicitly marshals between the JS event types and the .NET event types, passing the specific subset of data that makes sense to serialize. But this logic isn't a real Blazor event handler; it's just a JS interop call, so Blazor will just do normal JSON serialization without any other special-case marshalling.

@SteveSandersonMS
Copy link
Member

If it turns out that it should marshal back as a IJSObjectReference in this case (i.e., a click event on a

), what would be good to do with objRef to complete the demo for this?

I wouldn't have an example based on event handlers, because Blazor has a proper native way to deal with events, and it's not this.

Instead I'd simply show an example of sending some arbitrary object from JS to .NET as an IJSObjectReference. For example,

// Passes a reference to the non-serializable 'window' object to .NET code, which receives it as an IJSObjectReference
DotNet.invokeMethodAsync('MyApp', 'ReceiveWindowObject', DotNet.createJSObjectReference(window));

@guardrex
Copy link
Contributor

guardrex commented Nov 11, 2022

Very well ... one final question about an API remark for createJSStreamReference, which I'm also placing in the doc ...

@param streamReference The ArrayBufferView or Blob used to create the JavaScript stream reference.

export function createJSStreamReference(streamReference: ArrayBuffer | ArrayBufferView | Blob | any)

I can't cross-link "ArrayBufferView" at MDN (or in the ECMAScript spec either). I see it described around the Net as a "superclass" for accessing the bytes of ArrayBuffer ... or an "abstract type" for various arrays, but MDN doesn't have a dedicated page to link for it. I was angling the text to read something like this ...

Call DotNet.createJSStreamReference(streamReference) to construct a JS stream reference so that it can be passed to .NET, where streamReference is an ArrayBuffer, ArrayBufferView, or Blob used to create the JS stream reference.

No example on that one. I was just going to place it under the new content for createJSObjectReference in a section titled Create JavaScript object and data references that can be passed to .NET.

Is an "ArrayBufferView" a DataView of an ArrayBuffer and/or a TypedArray of an ArrayBuffer?

Or ... should I just leave "ArrayBufferView" code-fenced without an MDN cross-link?

@SteveSandersonMS
Copy link
Member

Is an "ArrayBufferView" a DataView of an ArrayBuffer and/or a TypedArray of an ArrayBuffer?

I think ArrayBufferView is just the abstract base class for all the different kinds of typed array. So I'd probably say something like:

Call DotNet.createJSStreamReference(streamReference) to construct a JS stream reference so that it can be passed to .NET, where streamReference is an ArrayBuffer, Blob, or any typed array such as Uint8Array or Float32Array used to create the JS stream reference.

TBH the parameter name streamReference is really strange. It should probably be called dataSource or something like that.

@guardrex
Copy link
Contributor

Ok, I'm good. I'll wrap this up now. If you want me to ping u on the PR, let me know, but I don't think it's necessary based on how closely the text will come off of your remarks (and the example).

@SteveSandersonMS
Copy link
Member

Thanks. Closing this issue now then.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
affected-most This issue impacts most of the customers area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-jsinterop This issue is related to JSInterop in Blazor severity-major This label is used by an internal tool
Projects
None yet
Development

No branches or pull requests