Browse Source

Enable getting IJSObjectReference to a JS function in Blazor interop (#61453)

Ondřej Roztočil 11 months ago
parent
commit
fc4c09e4d3

+ 4 - 0
src/Components/test/E2ETest/Tests/InteropTest.cs

@@ -107,6 +107,8 @@ public class InteropTest : ServerTestBase<ToggleExecutionModeServerFixture<Progr
             ["invokeNewWithClassConstructorAsync.dataProperty"] = "abraka",
             ["invokeNewWithClassConstructorAsync.function"] = "6",
             ["invokeNewWithNonConstructorAsync"] = "Success",
+            // Function reference tests
+            ["changeFunctionViaObjectReferenceAsync"] = "42"
         };
 
         var expectedSyncValues = new Dictionary<string, string>
@@ -171,6 +173,8 @@ public class InteropTest : ServerTestBase<ToggleExecutionModeServerFixture<Progr
             ["invokeNewWithClassConstructor.dataProperty"] = "abraka",
             ["invokeNewWithClassConstructor.function"] = "6",
             ["invokeNewWithNonConstructor"] = "Success",
+            // Function reference tests
+            ["changeFunctionViaObjectReference"] = "42"
         };
 
         // Include the sync assertions only when running under WebAssembly

+ 12 - 2
src/Components/test/testassets/BasicTestApp/DotNetToJSInterop.razor

@@ -92,6 +92,7 @@
 <div style="margin-top: 2em">
     <button @onclick="CreateInstanceByConstructorFunction">Call constructor function InvokeNewAsync</button>
     <button @onclick="CreateInstanceByClassConstructor">Call class constructor with InvokeNewAsync</button>
+    <button @onclick="ChangeInstanceMethodWithFunctionReference">Change instance method with function reference</button>
     <span>@InstanceMessage</span>
 </div>
 
@@ -146,13 +147,13 @@
 
     private async Task CreateInstanceByConstructorFunction()
     {
-        var dogRef = await JSRuntime.InvokeNewAsync("Dog", ["Igor"]);
+        var dogRef = await JSRuntime.InvokeNewAsync("Dog", "A dog");
         InstanceMessage = await dogRef.InvokeAsync<string>("bark");
     }
 
     private async Task CreateInstanceByClassConstructor()
     {
-        var catRef = await JSRuntime.InvokeNewAsync("Cat", ["Whiskers"]);
+        var catRef = await JSRuntime.InvokeNewAsync("Cat", "A cat");
         InstanceMessage = await catRef.InvokeAsync<string>("meow");
     }
 
@@ -166,6 +167,15 @@
         await JSRuntime.SetValueAsync<int>("testObject.getOnlyProperty", 123);
     }
 
+    private async Task ChangeInstanceMethodWithFunctionReference()
+    {
+        var dogRef = await JSRuntime.InvokeNewAsync("Dog", "A dog");
+        var dogFuncRef = await dogRef.GetValueAsync<IJSObjectReference>("bark");
+        var catRef = await JSRuntime.InvokeNewAsync("Cat", "A cat");
+        await catRef.SetValueAsync("meow", dogFuncRef);
+        InstanceMessage = await catRef.InvokeAsync<string>("meow");
+    }
+
     class TestObjectModel
     {
         public int Num { get; set; }

+ 25 - 0
src/Components/test/testassets/BasicTestApp/InteropComponent.razor

@@ -301,6 +301,13 @@
             InvokeNewTests();
         }
 
+        await FunctionReferenceAsyncTests();
+
+        if (shouldSupportSyncInterop)
+        {
+            FunctionReferenceTests();
+        }
+
         Invocations = invocations;
         DoneWithInterop = true;
     }
@@ -601,6 +608,24 @@
         }
     }
 
+    private async Task FunctionReferenceAsyncTests()
+    {
+        var funcRef = await JSRuntime.GetValueAsync<IJSObjectReference>("jsInteropTests.nonConstructorFunction");
+        var testClassRef = await JSRuntime.InvokeNewAsync("jsInteropTests.TestClass", "abraka");
+        await testClassRef.SetValueAsync("getTextLength", funcRef);
+        ReturnValues["changeFunctionViaObjectReferenceAsync"] = (await testClassRef.InvokeAsync<int>("getTextLength")).ToString();
+    }
+
+    private void FunctionReferenceTests()
+    {
+        var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
+
+        var funcRef = inProcRuntime.GetValue<IJSObjectReference>("jsInteropTests.nonConstructorFunction");
+        var testClassRef = inProcRuntime.InvokeNew("jsInteropTests.TestClass", "abraka");
+        testClassRef.SetValue("getTextLength", funcRef);
+        ReturnValues["changeFunctionViaObjectReference"] = testClassRef.Invoke<int>("getTextLength").ToString();
+    }
+
     public class PassDotNetObjectByRefArgs
     {
         public string StringValue { get; set; }

+ 1 - 1
src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts

@@ -155,7 +155,7 @@ export module DotNet {
    * @throws Error if the given value is not an Object.
    */
   export function createJSObjectReference(jsObject: any): any {
-      if (jsObject && typeof jsObject === "object") {
+      if (jsObject && (typeof jsObject === "object" || jsObject instanceof Function)) {
           cachedJSObjectsById[nextJsObjectId] = new JSObject(jsObject);
 
           const result = {