Skip to content

Commit e47b371

Browse files
committed
Added replacers to the JSInterop
1 parent bb2d778 commit e47b371

File tree

5 files changed

+66
-5
lines changed

5 files changed

+66
-5
lines changed

src/Components/Web.JS/src/Rendering/ElementReferenceCapture.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,34 @@ function getCaptureIdAttributeName(referenceCaptureId: string) {
1616
return `_bl_${referenceCaptureId}`;
1717
}
1818

19+
function getCaptureIdFromElement(element: Element): string | null {
20+
for (let i = 0; i < element.attributes.length; i++) {
21+
const attr = element.attributes[i];
22+
if (attr.name.startsWith('_bl_')) {
23+
return attr.name.substring(4);
24+
}
25+
}
26+
return null;
27+
}
28+
1929
// Support receiving ElementRef instances as args in interop calls
2030
const elementRefKey = '__internalId'; // Keep in sync with ElementRef.cs
2131
DotNet.attachReviver((key, value) => {
2232
if (value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, elementRefKey) && typeof value[elementRefKey] === 'string') {
33+
console.log("attachReviver: ", value);
2334
return getElementByCaptureId(value[elementRefKey]);
2435
} else {
2536
return value;
2637
}
2738
});
39+
40+
// Support return of the ElementRef from JS to .NET
41+
DotNet.attachReplacer((key, value) => {
42+
if (value instanceof Element) {
43+
const captureId = getCaptureIdFromElement(value);
44+
if (captureId) {
45+
return { [elementRefKey]: captureId };
46+
}
47+
}
48+
return value;
49+
});

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public void CanInvokeInteropMethods()
7878
["testDtoAsync"] = "Same",
7979
["returnPrimitiveAsync"] = "123",
8080
["returnArrayAsync"] = "first,second",
81+
["elementReference"] = "Success",
8182
["jsObjectReference.identity"] = "Invoked from JSObjectReference",
8283
["jsObjectReference.nested.add"] = "5",
8384
["addViaJSObjectReference"] = "5",

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
<h2>@nameof(JSObjectReferenceInvokeNonFunctionException)</h2>
5252
<p id="@nameof(JSObjectReferenceInvokeNonFunctionException)">@JSObjectReferenceInvokeNonFunctionException?.Message</p>
5353
</div>
54+
55+
<p @ref="element">Element reference.</p>
56+
5457
@if (DoneWithInterop)
5558
{
5659
<p id="done-with-interop">Done with interop.</p>
@@ -70,6 +73,8 @@
7073

7174
public bool DoneWithInterop { get; set; }
7275

76+
public ElementReference element;
77+
7378
public async Task InvokeInteropAsync()
7479
{
7580
var shouldSupportSyncInterop = RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
@@ -167,6 +172,19 @@
167172
ReturnValues["invokeAsyncThrowsSerializingCircularStructure"] = $"Failure: {ex.Message}";
168173
}
169174

175+
try
176+
{
177+
var elementReference = await JSRuntime.InvokeAsync<ElementReference>("returnElementReference", element);
178+
ReturnValues["elementReference"] = "Success";
179+
}
180+
catch (JSException ex)
181+
{
182+
ReturnValues["elementReference"] = $"Failure: {ex.Message}";
183+
}
184+
catch (Exception ex)
185+
{
186+
ReturnValues["elementReference"] = $"Failure: {ex.Message}";
187+
}
170188

171189
var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
172190
ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync<string>("identity", "Invoked from JSObjectReference");

src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ window.jsInteropTests = {
254254
receiveDotNetObjectByRefAsync: receiveDotNetObjectByRefAsync,
255255
receiveDotNetStreamReference: receiveDotNetStreamReference,
256256
receiveDotNetStreamWrapperReference: receiveDotNetStreamWrapperReference,
257+
returnElementReference: returnElementReference,
257258
TestClass: TestClass,
258259
nonConstructorFunction: () => { return 42; },
259260
testObject: testObject,
@@ -373,6 +374,10 @@ function returnJSObjectReference() {
373374
};
374375
}
375376

377+
function returnElementReference(element) {
378+
return element;
379+
}
380+
376381
function addViaJSObjectReference(jsObjectReference, a, b) {
377382
return jsObjectReference.nested.add(a, b);
378383
}

src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
export module DotNet {
77
export type JsonReviver = ((key: any, value: any) => any);
88
const jsonRevivers: JsonReviver[] = [];
9+
export type JsonReplacer = ((key: any, value: any) => any);
10+
const jsonReplacers: JsonReplacer[] = [];
911

1012
const jsObjectIdKey = "__jsObjectId";
1113
const dotNetObjectRefKey = "__dotNetObject";
@@ -118,6 +120,14 @@ export module DotNet {
118120
jsonRevivers.push(reviver);
119121
}
120122

123+
/**
124+
* Adds a JSON replacer callback that will be used when serializing arguments sent to .NET.
125+
* @param replacer The replacer to add.
126+
*/
127+
export function attachReplacer(replacer: JsonReplacer) {
128+
jsonReplacers.push(replacer);
129+
}
130+
121131
/**
122132
* Invokes the specified .NET public method synchronously. Not all hosting scenarios support
123133
* synchronous invocation, so if possible use invokeMethodAsync instead.
@@ -808,16 +818,21 @@ export module DotNet {
808818
}
809819

810820
function argReplacer(key: string, value: any) {
811-
if (value instanceof DotNetObject) {
812-
return value.serializeAsArg();
813-
} else if (value instanceof Uint8Array) {
821+
const processedValue = jsonReplacers.reduce(
822+
(currentValue, replacer) => replacer(key, currentValue),
823+
value
824+
);
825+
826+
if (processedValue instanceof DotNetObject) {
827+
return processedValue.serializeAsArg();
828+
} else if (processedValue instanceof Uint8Array) {
814829
const dotNetCallDispatcher = currentCallDispatcher!.getDotNetCallDispatcher();
815-
dotNetCallDispatcher!.sendByteArray(nextByteArrayIndex, value);
830+
dotNetCallDispatcher!.sendByteArray(nextByteArrayIndex, processedValue);
816831
const jsonValue = { [byteArrayRefKey]: nextByteArrayIndex };
817832
nextByteArrayIndex++;
818833
return jsonValue;
819834
}
820835

821-
return value;
836+
return processedValue;
822837
}
823838
}

0 commit comments

Comments
 (0)