Skip to content

Commit 937742b

Browse files
authored
[Pre4] New JavaScript interop features (#35220)
1 parent dd56c6e commit 937742b

File tree

3 files changed

+212
-2
lines changed

3 files changed

+212
-2
lines changed

aspnetcore/blazor/javascript-interoperability/call-javascript-from-dotnet.md

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,107 @@ IJSRuntime JS { get; set; }
453453

454454
[!INCLUDE[](~/blazor/includes/js-interop/synchronous-js-interop-call-js.md)]
455455

456+
:::moniker range=">= aspnetcore-10.0"
457+
458+
<!-- UPDATE 10.0 - API Browser cross-links in the next
459+
two H2 sections. -->
460+
461+
## Create an instance of a JS object using a constructor function
462+
463+
Create an instance of a JS object using a constructor function and get the <xref:Microsoft.JSInterop.IJSObjectReference>/<xref:Microsoft.JSInterop.IJSInProcessObjectReference> .NET handle for referencing the instance with the following API:
464+
465+
* `InvokeNewAsync` (asynchronous)
466+
* `InvokeNew` (synchronous)
467+
468+
Examples in this section demonstrate the API calls with the following `TestClass` with a constructor function (`constructor(text)`):
469+
470+
```javascript
471+
window.TestClass = class {
472+
constructor(text) {
473+
this.text = text;
474+
}
475+
476+
getTextLength() {
477+
return this.text.length;
478+
}
479+
}
480+
```
481+
482+
### Asynchronous `InvokeNewAsync`
483+
484+
Use `InvokeNewAsync(string identifier, object?[]? args)` on <xref:Microsoft.JSInterop.IJSRuntime> and <xref:Microsoft.JSInterop.IJSObjectReference> to invoke the specified JS constructor function asynchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an <xref:Microsoft.JSInterop.IJSObjectReference>.
485+
486+
```csharp
487+
var classRef = await JSRuntime.InvokeNewAsync("TestClass", "Blazor!");
488+
var text = await classRef.GetValueAsync<string>("text");
489+
var textLength = await classRef.InvokeAsync<int>("getTextLength");
490+
```
491+
492+
An overload is available that takes a <xref:System.Threading.CancellationToken> argument or <xref:System.TimeSpan> timeout argument.
493+
494+
### Synchronous `InvokeNew`
495+
496+
Use `InvokeNew(string identifier, object?[]? args)` on <xref:Microsoft.JSInterop.IJSInProcessRuntime> and <xref:Microsoft.JSInterop.IJSInProcessObjectReference> to invoke the specified JS constructor function synchronously. The function is invoked with the `new` operator. In the following example, `TestClass` contains a constructor function, and `classRef` is an <xref:Microsoft.JSInterop.IJSInProcessObjectReference>:
497+
498+
```csharp
499+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
500+
var classRef = inProcRuntime.InvokeNew("TestClass", "Blazor!");
501+
var text = await classRef.GetValueAsync<string>("text");
502+
var textLength = await classRef.InvokeAsync<int>("getTextLength");
503+
```
504+
505+
An overload is available that takes a <xref:System.Threading.CancellationToken> argument or <xref:System.TimeSpan> timeout argument.
506+
507+
## Read or modify the value of a JS object property
508+
509+
Read or modify the value of a JS object property, both data and accessor properties, with the following API:
510+
511+
* `GetValueAsync`/`SetValueAsync` (asynchronous)
512+
* `GetValue`/`SetValue` (synchronous)
513+
514+
Examples in this section demonstrate the API calls with the following JS object (`testObject`):
515+
516+
```javascript
517+
window.testObject = {
518+
num: 10
519+
}
520+
```
521+
522+
### Asynchronous `GetValueAsync` and `SetValueAsync`
523+
524+
Use `GetValueAsync<TValue>(string identifier)` to read the value of the specified JS property asynchronously. A <xref:Microsoft.JSInterop.JSException> is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataPropertyAsync`:
525+
526+
```csharp
527+
var valueFromDataPropertyAsync =
528+
await JSRuntime.GetValueAsync<int>("testObject.num");
529+
```
530+
531+
Use `SetValueAsync<TValue>(string identifier, TValue value)` to update the value of the specified JS property asynchronously. If the property isn't defined on the target object, the property is created. A <xref:Microsoft.JSInterop.JSException> is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`:
532+
533+
```csharp
534+
await JSRuntime.SetValueAsync("testObject.num", 20);
535+
await JSRuntime.SetValueAsync("testObject.num2", 30);
536+
```
537+
538+
### Synchronous `GetValue` and `SetValue`
539+
540+
Use `GetValue<TValue>(string identifier)` to read the value of the specified JS property synchronously. A <xref:Microsoft.JSInterop.JSException> is thrown if the property doesn't exist or is a `set`-only property. In the following example, the value of `testObject.num` (10) is stored in `valueFromDataProperty`:
541+
542+
```csharp
543+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
544+
var valueFromDataProperty = inProcRuntime.GetValue<int>("testObject.num");
545+
```
546+
547+
Use `SetValue<TValue>(string identifier, TValue value)` to update the value of the specified JS property synchronously. The property can't be a `get`-only property. If the property isn't defined on the target object, the property is created. A <xref:Microsoft.JSInterop.JSException> is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `testObject.num` is set to 20, and `num2` is created with a value of 30 on `testObject`:
548+
549+
```csharp
550+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
551+
inProcRuntime.SetValue("testObject.num", 20);
552+
inProcRuntime.SetValue("testObject.num2", 30);
553+
```
554+
555+
:::moniker-end
556+
456557
## JavaScript location
457558

458559
Load JavaScript (JS) code using any of approaches described by the [article on JavaScript location](xref:blazor/js-interop/javascript-location):
@@ -519,12 +620,35 @@ For browser compatibility, see [Can I use: JavaScript modules: dynamic import](h
519620

520621
In server-side scenarios, JS interop calls can't be issued after Blazor's SignalR circuit is disconnected. Without a circuit during component disposal or at any other time that a circuit doesn't exist, the following method calls fail and log a message that the circuit is disconnected as a <xref:Microsoft.JSInterop.JSDisconnectedException>:
521622

623+
:::moniker-end
624+
625+
:::moniker range=">= aspnetcore-10.0"
626+
627+
<!-- UPDATE 10.0 - API Browser cross-links -->
628+
522629
* JS interop method calls
523630
* <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType>
524631
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync%2A?displayProperty=nameWithType>
525-
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>)
632+
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>
633+
* `InvokeNewAsync`
634+
* `GetValueAsync`
635+
* `SetValueAsync`
526636
* `Dispose`/`DisposeAsync` calls on any <xref:Microsoft.JSInterop.IJSObjectReference>.
527637

638+
:::moniker-end
639+
640+
:::moniker range=">= aspnetcore-5.0 < aspnetcore-10.0"
641+
642+
* JS interop method calls
643+
* <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType>
644+
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync%2A?displayProperty=nameWithType>
645+
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>
646+
* `Dispose`/`DisposeAsync` calls on any <xref:Microsoft.JSInterop.IJSObjectReference>.
647+
648+
:::moniker-end
649+
650+
:::moniker range=">= aspnetcore-5.0"
651+
528652
In order to avoid logging <xref:Microsoft.JSInterop.JSDisconnectedException> or to log custom information in server-side Blazor, catch the exception in a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement.
529653

530654
For the following component disposal example:
@@ -627,7 +751,7 @@ In the preceding example:
627751

628752
Dynamically importing a module requires a network request, so it can only be achieved asynchronously by calling <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A>.
629753

630-
`IJSInProcessObjectReference` represents a reference to a JS object whose functions can be invoked synchronously in client-side components. For more information, see the [Synchronous JS interop in client-side components](#synchronous-js-interop-in-client-side-components) section.
754+
<xref:Microsoft.JSInterop.IJSInProcessObjectReference> represents a reference to a JS object whose functions can be invoked synchronously in client-side components. For more information, see the [Synchronous JS interop in client-side components](#synchronous-js-interop-in-client-side-components) section.
631755

632756
> [!NOTE]
633757
> When the external JS file is supplied by a [Razor class library](xref:blazor/components/class-libraries), specify the module's JS file using its stable static web asset path: `./_content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)}`:

aspnetcore/blazor/javascript-interoperability/index.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,31 @@ Don't assume that observing `document.body`, instead of `target.parentNode`, is
328328

329329
JavaScript (JS) interop calls can't be issued after Blazor's SignalR circuit is disconnected. Without a circuit during component disposal or at any other time that a circuit doesn't exist, the following method calls fail and log a message that the circuit is disconnected as a <xref:Microsoft.JSInterop.JSDisconnectedException>:
330330

331+
:::moniker range=">= aspnetcore-10.0"
332+
333+
<!-- UPDATE 10.0 - API Browser cross-links -->
334+
331335
* JS interop method calls
332336
* <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType>
333337
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync%2A?displayProperty=nameWithType>
334338
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>
339+
* `InvokeNewAsync`
340+
* `GetValueAsync`
341+
* `SetValueAsync`
335342
* `Dispose`/`DisposeAsync` calls on any <xref:Microsoft.JSInterop.IJSObjectReference>.
336343

344+
:::moniker-end
345+
346+
:::moniker range="< aspnetcore-10.0"
347+
348+
* JS interop method calls
349+
* <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType>
350+
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync%2A?displayProperty=nameWithType>
351+
* <xref:Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync%2A?displayProperty=nameWithType>
352+
* `Dispose`/`DisposeAsync` calls on any <xref:Microsoft.JSInterop.IJSObjectReference>.
353+
354+
:::moniker-end
355+
337356
In order to avoid logging <xref:Microsoft.JSInterop.JSDisconnectedException> or to log custom information, catch the exception in a [`try-catch`](/dotnet/csharp/language-reference/keywords/try-catch) statement.
338357

339358
For the following component disposal example:

aspnetcore/release-notes/aspnetcore-10/includes/blazor.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,70 @@ else
280280
```
281281

282282
For more information, see <xref:blazor/components/prerender?view=aspnetcore-10.0#persist-prerendered-state>. Additional API implementation notes, which are subject to change at any time, are available in [[Blazor] Support for declaratively persisting component and services state (`dotnet/aspnetcore` #60634)](https://github.com/dotnet/aspnetcore/pull/60634).
283+
284+
<!-- PREVIEW 4
285+
286+
### New JavaScript interop features
287+
288+
Blazor adds support for the following JS interop features:
289+
290+
* Create an instance of a JS object using a constructor function and get the <xref:Microsoft.JSInterop.IJSObjectReference>/<xref:Microsoft.JSInterop.IJSInProcessObjectReference> .NET handle for referencing the instance.
291+
* Read or modify the value of a JS object property, both data and accessor properties.
292+
293+
The following asynchronous methods are available on <xref:Microsoft.JSInterop.IJSRuntime> and <xref:Microsoft.JSInterop.IJSObjectReference> with the same scoping behavior as the existing <xref:Microsoft.JSInterop.IJSRuntime.InvokeAsync%2A?displayProperty=nameWithType> method:
294+
295+
* `InvokeNewAsync(string identifier, object?[]? args)`: Invokes the specified JS constructor function asynchronously. The function is invoked with the `new` operator. In the following example, `jsInterop.TestClass` is a class with a constructor function, and `classRef` is an <xref:Microsoft.JSInterop.IJSObjectReference>:
296+
297+
```csharp
298+
var classRef = await JSRuntime.InvokeNewAsync("jsInterop.TestClass", "Blazor!");
299+
var text = await classRef.GetValueAsync<string>("text");
300+
var textLength = await classRef.InvokeAsync<int>("getTextLength");
301+
```
302+
303+
* `GetValueAsync<TValue>(string identifier)`: Reads the value of the specified JS property asynchronously. The property can't be a `set`-only property. A <xref:Microsoft.JSInterop.JSException> is thrown if the property doesn't exist. The following example returns a value from a data property:
304+
305+
```csharp
306+
var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>(
307+
"jsInterop.testObject.num");
308+
```
309+
310+
* `SetValueAsync<TValue>(string identifier, TValue value)`: Updates the value of the specified JS property asynchronously. The property can't be a `get`-only property. If the property isn't defined on the target object, the property is created. A <xref:Microsoft.JSInterop.JSException> is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `num` is created on `testObject` with a value of 30 if it doesn't exist:
311+
312+
```csharp
313+
await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
314+
```
315+
316+
Overloads are available for each of the preceding methods that take a <xref:System.Threading.CancellationToken> argument or <xref:System.TimeSpan> timeout argument.
317+
318+
The following synchronous methods are available on <xref:Microsoft.JSInterop.IJSInProcessRuntime> and <xref:Microsoft.JSInterop.IJSInProcessObjectReference> with the same scoping behavior as the existing <xref:Microsoft.JSInterop.IJSInProcessObjectReference.Invoke%2A?displayProperty=nameWithType> method:
319+
320+
* `InvokeNew(string identifier, object?[]? args)`: Invokes the specified JS constructor function synchronously. The function is invoked with the `new` operator. In the following example, `jsInterop.TestClass` is a class with a constructor function, and `classRef` is an <xref:Microsoft.JSInterop.IJSInProcessObjectReference>:
321+
322+
```csharp
323+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
324+
var classRef = inProcRuntime.InvokeNew("jsInterop.TestClass", "Blazor!");
325+
var text = await classRef.GetValueAsync<string>("text");
326+
var textLength = await classRef.InvokeAsync<int>("getTextLength");
327+
```
328+
329+
* `GetValue<TValue>(string identifier)`: Reads the value of the specified JS property synchronously. The property can't be a `set`-only property. A <xref:Microsoft.JSInterop.JSException> is thrown if the property doesn't exist. The following example returns a value from a data property:
330+
331+
```csharp
332+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
333+
var valueFromDataProperty = inProcRuntime.GetValue<int>(
334+
"jsInterop.testObject.num");
335+
```
336+
337+
* `SetValue<TValue>(string identifier, TValue value)`: Updates the value of the specified JS property synchronously. The property can't be a `get`-only property. If the property isn't defined on the target object, the property is created. A <xref:Microsoft.JSInterop.JSException> is thrown if the property exists but isn't writable or when a new property can't be added to the object. In the following example, `num` is created on `testObject` with a value of 20 if it doesn't exist:
338+
339+
```csharp
340+
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
341+
inProcRuntime.SetValue("jsInterop.testObject.num", 20);
342+
```
343+
344+
For more information, see the following sections of the *Call JavaScript functions from .NET methods* article:
345+
346+
* [Create an instance of a JS object using a constructor function](xref:blazor/js-interop/call-javascript-from-dotnet#create-an-instance-of-a-js-object-using-a-constructor-function)
347+
* [Read or modify the value of a JS object property](xref:blazor/js-interop/call-javascript-from-dotnet#read-or-modify-the-value-of-a-js-object-property)
348+
349+
-->

0 commit comments

Comments
 (0)