Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 10 di questo articolo.
Questo articolo illustra come richiamare funzioni JavaScript (JS) da .NET.
Per informazioni su come chiamare metodi .NET da JS, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor.
Invocare JS funzioni
IJSRuntime viene registrato dal Blazor framework. Per chiamare JS da .NET, inserire l'astrazione IJSRuntime e chiamare uno dei metodi seguenti:
Per i metodi .NET precedenti che richiamano le funzioni JS:
- L'identificatore della funzione (
String) è relativo all'ambito globale (window). Per chiamarewindow.someScope.someFunction, l'identificatore èsomeScope.someFunction. Non è necessario registrare la funzione prima che venga chiamata. - Passare qualsiasi numero di argomenti serializzabili in JSON in
Object[]a una funzione JS. - Il token di annullamento (
CancellationToken) propaga una notifica che le operazioni devono essere annullate. -
TimeSpanrappresenta un limite di tempo per un'operazione JS . - Il
TValuetipo restituito deve anche essere serializzabile JSON.TValuedeve corrispondere al tipo .NET che esegue il mapping migliore al tipo JSON restituito. - Per i metodi
InvokeAsync, viene restituito JSPromise.InvokeAsyncannulla il wrapping diPromisee restituisce il valore atteso dall'oggettoPromise. - Sono supportati i tipi di unione C#.
- L'identificatore della funzione (
String) è relativo all'ambito globale (window). Per chiamarewindow.someScope.someFunction, l'identificatore èsomeScope.someFunction. Non è necessario registrare la funzione prima che venga chiamata. - Passa un numero qualsiasi di argomenti serializzabili in JSON in
Object[]a una funzione JS. - Il token di annullamento (
CancellationToken) propaga una notifica che le operazioni devono essere annullate. -
TimeSpanrappresenta un limite di tempo per un'operazione JS . - Il
TValuetipo restituito deve anche essere serializzabile JSON.TValuedeve corrispondere al tipo .NET che esegue il mapping migliore al tipo JSON restituito. -
JS
Promiseviene restituito per i metodiInvokeAsync.InvokeAsyncannulla il wrapping diPromisee restituisce il valore atteso dall'oggettoPromise.
Per le app Blazor con prerendering abilitato, che è l'impostazione predefinita per le app lato server, non è possibile chiamare JS durante il prerendering. Per altre informazioni, vedere la sezione Prerendering .
L'esempio seguente si basa su TextDecoder, un decodificatore basato su JS. L'esempio illustra come richiamare una JS funzione da un metodo C# che esegue l'offload di un requisito dal codice dello sviluppatore a un'API esistente JS . La JS funzione accetta una matrice di byte da un metodo C#, decodifica la matrice e restituisce il testo al componente per la visualizzazione.
<script>
window.convertArray = (win1251Array) => {
var win1251decoder = new TextDecoder('windows-1251');
var bytes = new Uint8Array(win1251Array);
var decodedArray = win1251decoder.decode(bytes);
return decodedArray;
};
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
Componente seguente:
- Richiama la
convertArrayJS funzione con InvokeAsync quando si seleziona un pulsante (Convert Array). - Dopo aver chiamato la JS funzione, la matrice passata viene convertita in una stringa. La stringa viene restituita al componente per la visualizzazione (
text).
CallJs1.razor:
@page "/call-js-1"
@inject IJSRuntime JS
<PageTitle>Call JS 1</PageTitle>
<h1>Call JS Example 1</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray() =>
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
CallJs1.razor:
@page "/call-js-1"
@inject IJSRuntime JS
<PageTitle>Call JS 1</PageTitle>
<h1>Call JS Example 1</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray() =>
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
CallJsExample1.razor:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
}
}
CallJsExample1.razor:
@page "/call-js-example-1"
@inject IJSRuntime JS
<h1>Call JS <code>convertArray</code> Function</h1>
<p>
<button @onclick="ConvertArray">Convert Array</button>
</p>
<p>
@text
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0472710/">David Krumholtz on IMDB</a>
</p>
@code {
private MarkupString text;
private uint[] quoteArray =
new uint[]
{
60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
105, 118, 101, 114, 115, 101, 10, 10,
};
private async Task ConvertArray()
{
text = new MarkupString(await JS.InvokeAsync<string>("convertArray",
quoteArray));
}
}
API JavaScript limitata ai movimenti dell'utente
Questa sezione si applica ai componenti lato server.
Alcune API JavaScript (JS) del browser possono essere eseguite solo nel contesto di un movimento dell'utente, ad esempio usando .Fullscreen API Queste API non possono essere chiamate tramite il JS meccanismo di interoperabilità nei componenti lato server perché la gestione degli eventi dell'interfaccia utente viene eseguita in modo asincrono e in genere non più nel contesto del movimento dell'utente. L'app deve gestire completamente l'evento dell'interfaccia utente in JavaScript, quindi deve usare onclick anziché l'attributo della direttiva @onclick di Blazor.
Richiamare le funzioni JavaScript senza leggere un valore restituito (InvokeVoidAsync)
Usa InvokeVoidAsync quando:
- .NET non è necessario per leggere il risultato di una chiamata JavaScript (JS).
- JS le funzioni restituiscono void(0)/void 0 o non definiti.
Specificare una displayTickerAlert1JS funzione. La funzione viene chiamata con InvokeVoidAsync e non restituisce un valore:
<script>
window.displayTickerAlert1 = (symbol, price) => {
alert(`${symbol}: $${price}!`);
};
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
Esempio di componente (.razor) (InvokeVoidAsync)
Componente seguente:
- Invoca la funzione
displayTickerAlert1JS con InvokeVoidAsync, con due parametri serializzabili JSON: una stringa instockSymbole un decimale inprice, quando viene selezionato un pulsante (Set Stock). - La JS funzione riceve i due argomenti in parametri rigorosamente tipizzato e visualizza una casella di avviso modale con un messaggio specificato.
CallJs2.razor:
@page "/call-js-2"
@inject IJSRuntime JS
<PageTitle>Call JS 2</PageTitle>
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJs2.razor:
@page "/call-js-2"
@inject IJSRuntime JS
<PageTitle>Call JS 2</PageTitle>
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
CallJsExample2.razor:
@page "/call-js-example-2"
@inject IJSRuntime JS
<h1>Call JS Example 2</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
}
}
Esempio di classe (.cs) (InvokeVoidAsync)
JsInteropClasses1.cs:
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses1(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask TickerChanged(string symbol, decimal price) =>
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses1(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask TickerChanged(string symbol, decimal price) =>
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses1 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses1(IJSRuntime js)
{
this.js = js;
}
public async ValueTask TickerChanged(string symbol, decimal price)
{
await js.InvokeVoidAsync("displayTickerAlert1", symbol, price);
}
public void Dispose()
{
}
}
TickerChanged chiama il displayTickerAlert1 metodo nel componente seguente.
CallJs3.razor:
@page "/call-js-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 3</PageTitle>
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJs3.razor:
@page "/call-js-3"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 3</PageTitle>
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses1? jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private JsInteropClasses1 jsClass;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample3.razor:
@page "/call-js-example-3"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 3</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private JsInteropClasses1 jsClass;
protected override void OnInitialized()
{
jsClass = new JsInteropClasses1(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
await jsClass.TickerChanged(stockSymbol, price);
}
public void Dispose() => jsClass?.Dispose();
}
Richiamare le funzioni JavaScript e leggere un valore restituito (InvokeAsync)
Usare InvokeAsync quando .NET deve leggere il risultato di una chiamata JavaScript (JS).
Specificare una displayTickerAlert2JS funzione. Nell'esempio seguente viene restituita una stringa per la visualizzazione da parte del chiamante:
<script>
window.displayTickerAlert2 = (symbol, price) => {
if (price < 20) {
alert(`${symbol}: $${price}!`);
return "User alerted in the browser.";
} else {
return "User NOT alerted.";
}
};
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
Esempio di componente (.razor) (InvokeAsync)
Componente seguente:
- Invoca la funzione
displayTickerAlert2JS con InvokeAsync, con due parametri serializzabili JSON: una stringa instockSymbole un decimale inprice, quando viene selezionato un pulsante (Set Stock). - La JS funzione riceve i due argomenti in parametri rigorosamente tipizzato e visualizza una casella di avviso modale con un messaggio specificato.
CallJs4.razor:
@page "/call-js-4"
@inject IJSRuntime JS
<PageTitle>Call JS 4</PageTitle>
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJs4.razor:
@page "/call-js-4"
@inject IJSRuntime JS
<PageTitle>Call JS 4</PageTitle>
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private string? result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private string result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
CallJsExample4.razor:
@page "/call-js-example-4"
@inject IJSRuntime JS
<h1>Call JS Example 4</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result != null)
{
<p>@result</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private string result;
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult =
await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
Esempio di classe (.cs) (InvokeAsync)
JsInteropClasses2.cs:
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses2(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask<string> TickerChanged(string symbol, decimal price) =>
await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
namespace BlazorSample;
public class JsInteropClasses2(IJSRuntime js) : IDisposable
{
private readonly IJSRuntime js = js;
public async ValueTask<string> TickerChanged(string symbol, decimal price) =>
await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
// Calling SuppressFinalize(this) prevents derived types that introduce
// a finalizer from needing to re-implement IDisposable.
public void Dispose() => GC.SuppressFinalize(this);
}
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;
public class JsInteropClasses2 : IDisposable
{
private readonly IJSRuntime js;
public JsInteropClasses2(IJSRuntime js)
{
this.js = js;
}
public async ValueTask<string> TickerChanged(string symbol, decimal price)
{
return await js.InvokeAsync<string>("displayTickerAlert2", symbol, price);
}
public void Dispose()
{
}
}
TickerChanged chiama il displayTickerAlert2 metodo e visualizza la stringa restituita nel componente seguente.
CallJs5.razor:
@page "/call-js-5"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 5</PageTitle>
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJs5.razor:
@page "/call-js-5"
@implements IDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 5</PageTitle>
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized() => jsClass = new(JS);
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}" +
$"{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private string? stockSymbol;
private decimal price;
private JsInteropClasses2? jsClass;
private string? result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
if (jsClass is not null)
{
stockSymbol =
$"{(char)('A' + Random.Shared.Next(0, 26))}{(char)('A' + Random.Shared.Next(0, 26))}";
price = Random.Shared.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol is not null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result is not null)
{
<p>@result</p>
}
@code {
private Random r = new();
private string stockSymbol;
private decimal price;
private JsInteropClasses2 jsClass;
private string result;
protected override void OnInitialized()
{
jsClass = new(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
public void Dispose() => jsClass?.Dispose();
}
CallJsExample5.razor:
@page "/call-js-example-5"
@implements IDisposable
@inject IJSRuntime JS
<h1>Call JS Example 5</h1>
<p>
<button @onclick="SetStock">Set Stock</button>
</p>
@if (stockSymbol != null)
{
<p>@stockSymbol price: @price.ToString("c")</p>
}
@if (result != null)
{
<p>@result</p>
}
@code {
private Random r = new Random();
private string stockSymbol;
private decimal price;
private JsInteropClasses2 jsClass;
private string result;
protected override void OnInitialized()
{
jsClass = new JsInteropClasses2(JS);
}
private async Task SetStock()
{
stockSymbol =
$"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
price = r.Next(1, 101);
var interopResult = await jsClass.TickerChanged(stockSymbol, price);
result = $"Result of SetStock call for {stockSymbol} at " +
$"{price.ToString("c")}: {interopResult}";
}
public void Dispose() => jsClass?.Dispose();
}
Scenari di generazione dinamica del contenuto
Per la generazione di contenuto dinamico con BuildRenderTree, usare l'attributo [Inject] :
[Inject]
IJSRuntime JS { get; set; }
Pre-rendering
Questa sezione si applica alle applicazioni lato server che eseguono il prerendering dei componenti Razor. Il prerendering è trattato in Prerender dei componenti di ASP.NET Core Razor.
Questa sezione si applica alle applicazioni lato server che eseguono il prerendering dei componenti Razor. Il prerendering è trattato in Prerender dei componenti di ASP.NET Core Razor.
Nota
La navigazione interna per il routing interattivo in Blazor Web Apps non comporta la richiesta al server di nuovi contenuti della pagina. Di conseguenza, il prerendering non si verifica per le richieste di pagina interne. Se l'app adotta il routing interattivo, esegui un ricaricamento completo della pagina per gli esempi di componenti che illustrano il comportamento di prerendering. Per altre informazioni, vedere ASP.NET Core Blazor persistenza dello stato di prerendering.
Questa sezione si applica alle applicazioni lato server e alle applicazioni ospitate Blazor WebAssembly che eseguono il prerendering dei componenti Razor. La prerendering è descritta in Integrare componenti ASP.NET Core Razor con MVC o Razor Pages.
Durante la pre-esecuzione, la chiamata a JavaScript (JS) non è possibile. L'esempio seguente illustra come usare l'interoperabilità JS come parte della logica di inizializzazione di un componente in modo compatibile con il prerendering.
La funzione seguente scrollElementIntoView :
- Scorre fino all'elemento specificato con
scrollIntoView. - Restituisce il valore della proprietà dell'elemento
topdalgetBoundingClientRectmetodo .
window.scrollElementIntoView = (element) => {
element.scrollIntoView();
return element.getBoundingClientRect().top;
}
Dove IJSRuntime.InvokeAsync chiama la JS funzione nel codice del componente, ElementReference viene usato solo in OnAfterRenderAsync e non in alcun metodo del ciclo di vita precedente perché non è presente alcun elemento DOM HTML fino a quando non viene eseguito il rendering del componente.
StateHasChanged (source di riferimento) viene chiamato per accodare il nuovo rendering del componente con il nuovo stato ottenuto dalla chiamata di interoperabilità JS (per altre informazioni, vedere rendering dei componenti Razor di ASP.NET Core). Non viene creato un ciclo infinito perché StateHasChanged viene chiamato solo quando scrollPosition è null.
PrerenderedInterop.razor:
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<PageTitle>Prerendered Interop</PageTitle>
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
@page "/prerendered-interop"
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Prerendered Interop Example</h1>
<div @ref="divElement" style="margin-top:2000px">
Set value via JS interop call: <strong>@scrollPosition</strong>
</div>
@code {
private ElementReference divElement;
private double? scrollPosition;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && scrollPosition is null)
{
scrollPosition = await JS.InvokeAsync<double>(
"scrollElementIntoView", divElement);
StateHasChanged();
}
}
}
L'esempio precedente inquina il client con una funzione globale. Per un approccio migliore nelle app di produzione, vedere Isolamento JavaScript nei moduli JavaScript.
Interoperabilità sincrona JS nei componenti lato client
Questa sezione si applica solo ai componenti lato client.
JS le chiamate di interoperabilità sono asincrone, indipendentemente dal fatto che il codice chiamato sia sincrono o asincrono. Le chiamate sono asincrone per garantire che i componenti siano compatibili tra le modalità di rendering lato server e lato client. Nel server tutte le JS chiamate di interoperabilità devono essere asincrone perché vengono inviate tramite una connessione di rete.
Se si è certi che il componente viene eseguito solo in WebAssembly, è possibile scegliere di effettuare chiamate di interoperabilità sincrone JS . Questo comporta un sovraccarico leggermente inferiore rispetto all'esecuzione di chiamate asincrone e può comportare un minor numero di cicli di rendering perché non esiste uno stato intermedio in attesa dei risultati.
Per effettuare una chiamata sincrona da .NET a JavaScript in un componente lato client, eseguire il cast di IJSRuntime a IJSInProcessRuntime per effettuare la chiamata di interoperabilità JS:
@inject IJSRuntime JS
...
@code {
protected override void HandleSomeEvent()
{
var jsInProcess = (IJSInProcessRuntime)JS;
var value = jsInProcess.Invoke<string>("javascriptFunctionIdentifier");
}
}
Quando lavori con IJSObjectReference in componenti lato client .NET 5 o versioni successive, puoi invece usare IJSInProcessObjectReference in modo sincrono. IJSInProcessObjectReference implementa IAsyncDisposable/IDisposable e deve essere eliminato per l'operazione di Garbage Collection per evitare una perdita di memoria, come illustrato nell'esempio seguente:
@inject IJSRuntime JS
@implements IDisposable
...
@code {
...
private IJSInProcessObjectReference? module;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var jsInProcess = (IJSInProcessRuntime)JS;
module = await jsInProcess.Invoke<IJSInProcessObjectReference>("import",
"./scripts.js");
var value = module.Invoke<string>("javascriptFunctionIdentifier");
}
}
...
void IDisposable.Dispose()
{
if (module is not null)
{
await module.Dispose();
}
}
}
Nell'esempio precedente, un JSDisconnectedException non viene intercettato durante la rimozione del modulo perché in un'app Blazor WebAssembly non esiste alcun circuito Blazor-SignalR che possa andare perso. Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).
Creare un'istanza di un JS oggetto usando una funzione del costruttore
Creare un'istanza di un JS oggetto usando una funzione del costruttore e ottenere l'handle IJSObjectReference/IJSInProcessObjectReference .NET per fare riferimento all'istanza con l'API seguente:
- InvokeConstructorAsync (asincrono)
- InvokeConstructor (sincrono)
Gli esempi in questa sezione illustrano le chiamate API con quanto segue TestClass con una funzione del costruttore (constructor(text)):
window.TestClass = class {
constructor(text) {
this.text = text;
}
getTextLength() {
return this.text.length;
}
}
InvokeConstructorAsync Asincrono
Usare InvokeConstructorAsync su IJSRuntime e IJSObjectReference per richiamare la funzione del costruttore specificata JS in modo asincrono. La funzione viene richiamata con l'operatore new . Nell'esempio seguente, TestClass contiene una funzione costruttore, ed classRef è un IJSObjectReference.
var classRef = await JSRuntime.InvokeConstructorAsync("TestClass", "Blazor!");
var text = await classRef.GetValueAsync<string>("text");
var textLength = await classRef.InvokeAsync<int>("getTextLength");
È disponibile un overload che accetta un argomento CancellationToken o un argomento di timeout TimeSpan.
InvokeConstructor sincrono
Usare InvokeConstructor su IJSInProcessRuntime e IJSInProcessObjectReference per richiamare la funzione del costruttore specificata JS in modo sincrono. La funzione viene richiamata con l'operatore new . Nell'esempio seguente contiene TestClass una funzione del costruttore ed classRef è un oggetto IJSInProcessObjectReference:
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
var classRef = inProcRuntime.InvokeConstructor("TestClass", "Blazor!");
var text = classRef.GetValue<string>("text");
var textLength = classRef.Invoke<int>("getTextLength");
È disponibile un overload che accetta un argomento CancellationToken o un argomento di timeout TimeSpan.
Leggere o modificare il valore di una JS proprietà dell'oggetto
Leggere o modificare il valore di una JS proprietà dell'oggetto, sia delle proprietà di dati che delle proprietà di accesso, con la seguente API:
- GetValueAsync / SetValueAsync (asincrono)
-
GetValue/SetValue(sincrono)
Esempi in questa sezione illustrano le chiamate API con l'oggetto seguente JS (testObject):
window.testObject = {
num: 10
}
Asincrona GetValueAsync e SetValueAsync
Utilizzare GetValueAsync per leggere il valore della proprietà specificata JS in modo asincrono. Viene lanciato un JSException se la proprietà non esiste o è una proprietà di sola set. Nell'esempio seguente il valore di testObject.num (10) viene archiviato in valueFromDataPropertyAsync:
var valueFromDataPropertyAsync =
await JSRuntime.GetValueAsync<int>("testObject.num");
Utilizzare SetValueAsync per aggiornare il valore della proprietà specificata JS in modo asincrono. Se la proprietà non è definita nell'oggetto di destinazione, viene creata la proprietà . Viene JSException generata un'eccezione se la proprietà esiste ma non è scrivibile o quando non è possibile aggiungere una nuova proprietà all'oggetto . Nell'esempio seguente, testObject.num è impostato su 20 e num2 viene creato con un valore pari a 30 in testObject:
await JSRuntime.SetValueAsync("testObject.num", 20);
await JSRuntime.SetValueAsync("testObject.num2", 30);
Sincrono GetValue e SetValue
Utilizzare GetValue per leggere il valore della proprietà specificata JS in modo sincrono. Viene lanciato un JSException se la proprietà non esiste o è una proprietà di sola set. Nell'esempio seguente il valore di testObject.num (10) viene archiviato in valueFromDataProperty:
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
var valueFromDataProperty = inProcRuntime.GetValue<int>("testObject.num");
Utilizzare SetValue per aggiornare il valore della proprietà specificata JS in modo sincrono. La proprietà non può essere solo una proprietà get. Se la proprietà non è definita nell'oggetto di destinazione, viene creata la proprietà . Viene JSException generata un'eccezione se la proprietà esiste ma non è scrivibile o quando non è possibile aggiungere una nuova proprietà all'oggetto . Nell'esempio seguente, testObject.num è impostato su 20 e num2 viene creato con un valore pari a 30 in testObject:
var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
inProcRuntime.SetValue("testObject.num", 20);
inProcRuntime.SetValue("testObject.num2", 30);
Posizione JavaScript
Carica il codice JavaScript (JS) usando uno degli approcci descritti nell'articolo sulla posizione di JavaScript:
-
Caricare uno script nel markup
<head>(generalmente non consigliato) -
Caricare uno script nel markup
<body> -
Carica uno script da un file JavaScript esterno (
.js) nella stessa directory del componente -
Caricare uno script da un file JavaScript esterno (
.js) - Iniettare uno script prima o dopo che Blazor venga avviato
Per informazioni sull'isolamento degli script nei JS moduli, vedere la sezione Isolamento JavaScript nei moduli JavaScript.
Avviso
Inserire un <script> tag in un file di componente (.razor) solo se il componente deve adottare il rendering statico lato server (SSR statico) perché il <script> tag non può essere aggiornato in modo dinamico.
Avviso
Non inserire un <script> tag in un file di componente (.razor) perché il <script> tag non può essere aggiornato in modo dinamico.
Isolamento di JavaScript nei moduli di JavaScript
Blazor consente l'isolamento di JavaScript (JS) nei moduli JavaScript standard (specifica ECMAScript). Il caricamento del modulo JavaScript funziona allo stesso modo Blazor in quanto funziona per altri tipi di app Web e puoi personalizzare il modo in cui i moduli vengono definiti nell'app. Per una guida su come usare i moduli JavaScript, vedere Documentazione Web MDN: moduli JavaScript.
L'isolamento di JS offre i vantaggi seguenti:
- Il codice JS importato non inquina più lo spazio dei nomi globale.
- Gli utenti di una libreria e dei relativi componenti non sono tenuti a importare il file JS correlato.
L'importazione dinamica con l'operatore import() è supportata con ASP.NET Core e Blazor:
if ({CONDITION}) import("/additionalModule.js");
Nell'esempio precedente il {CONDITION} segnaposto rappresenta un controllo condizionale per determinare se il modulo deve essere caricato.
Per la compatibilità del browser, vedere È possibile usare i moduli JavaScript: importazione dinamica.
Negli scenari sul lato server, non è possibile eseguire chiamate JSdi interoperabilità dopo la disconnessione del circuito di BlazorSignalR Senza un circuito durante l'eliminazione di un componente o in qualsiasi altro momento in cui un circuito non esiste, le chiamate al metodo seguenti hanno esito negativo e registrano un messaggio che indica che il circuito viene disconnesso come JSDisconnectedException:
- JS chiamate di metodo di interoperabilità
-
Dispose
/
DisposeAsyncrichiama qualsiasi IJSObjectReference.
- JS chiamate di metodo di interoperabilità
-
Dispose
/
DisposeAsynceffettua chiamate su qualsiasi IJSObjectReference.
Per evitare di registrare JSDisconnectedException oppure di registrare informazioni personalizzate in Blazor lato server, intercettare l'eccezione in un'istruzione try-catch.
Per l'esempio di eliminazione dei componenti seguente:
- Il componente lato server implementa IAsyncDisposable.
-
moduleè un IJSObjectReference di un modulo JS. - JSDisconnectedException viene intercettata e non viene registrata nei log.
- Facoltativamente, è possibile registrare informazioni personalizzate nell'istruzione
catcha qualsiasi livello di log preferito. L'esempio seguente non registra informazioni personalizzate. Il codice presuppone che lo sviluppatore non si preoccupa di quando o dove i circuiti vengono disconnessi durante l'eliminazione del componente.
async ValueTask IAsyncDisposable.DisposeAsync()
{
try
{
if (module is not null)
{
await module.DisposeAsync();
}
}
catch (JSDisconnectedException)
{
}
}
Se è necessario ripulire i propri oggetti JS o eseguire altro codice JS sul client dopo che un circuito è andato perso in un'app Blazor lato server, usare il modello MutationObserver in JS sul client. Il MutationObserver modello consente di eseguire JS il codice quando un elemento viene rimosso dal DOM.
Per altre informazioni, vedere gli articoli seguenti:
-
ASP.NET Core Blazor interoperabilità JavaScript (JS interop): Include un esempio di codice del pattern
MutationObserver. - Gestire gli errori nelle applicazioni ASP.NET Core Blazor: la sezione Interop JavaScript illustra la gestione degli errori negli scenari di JS interop.
- ASP.NET Core Razor component disposal: l'articolo descrive come implementare modelli di eliminazione nei componenti Razor.
Il modulo seguente JS esporta una JS funzione per visualizzare un prompt di una finestra del browser. Inserire il codice seguente JS in un file esterno JS .
wwwroot/scripts.js:
export function showPrompt(message) {
return prompt(message, 'Type anything here');
}
Aggiungere il modulo precedente JS a un’app o una libreria di classi come asset web statico nella cartella wwwroot e quindi importarlo nel codice .NET chiamando InvokeAsync sull’istanza IJSRuntime.
IJSRuntime importa il modulo come IJSObjectReference, che rappresenta un riferimento a un JS oggetto dal codice .NET. Usare IJSObjectReference per invocare le funzioni JS esportate dal modulo.
CallJs6.razor:
@page "/call-js-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 6</PageTitle>
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt() => result = await Prompt("Provide text");
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallJs6.razor:
@page "/call-js-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 6</PageTitle>
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt() => result = await Prompt("Provide text");
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
try
{
await module.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallJsExample6.razor:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallJsExample6.razor:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference? module;
private string? result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string?> Prompt(string message) =>
module is not null ?
await module.InvokeAsync<string>("showPrompt", message) : null;
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
CallJsExample6.razor:
@page "/call-js-example-6"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 6</h1>
<p>
<button @onclick="TriggerPrompt">Trigger browser window prompt</button>
</p>
<p>
@result
</p>
@code {
private IJSObjectReference module;
private string result;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
module = await JS.InvokeAsync<IJSObjectReference>("import",
"./scripts.js");
}
}
private async Task TriggerPrompt()
{
result = await Prompt("Provide some text");
}
public async ValueTask<string> Prompt(string message)
{
return await module.InvokeAsync<string>("showPrompt", message);
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
await module.DisposeAsync();
}
}
}
Nell'esempio precedente:
- Per convenzione, l'identificatore
importè un identificatore speciale usato in modo specifico per l'importazione di un JS modulo. - Specificare il file JS esterno del modulo utilizzando il percorso stabile dell'asset web statico:
./{SCRIPT PATH AND FILE NAME (.js)}, dove:- Il segmento di percorso per la directory corrente (
./) è necessario per creare il percorso corretto della risorsa statica per il file JS. - Il segnaposto
{SCRIPT PATH AND FILE NAME (.js)}corrisponde al percorso e al nome del file inwwwroot.
- Il segmento di percorso per la directory corrente (
- Rilascia IJSObjectReference per la garbage collection in IAsyncDisposable.DisposeAsync.
- Non inserire un tag
per lo script dopo lo script perché il modulo viene caricato e memorizzato nella cache automaticamente quando viene richiamato il dinamico.
L'importazione dinamica di un modulo richiede una richiesta di rete, quindi può essere ottenuta solo in modo asincrono chiamando InvokeAsync.
IJSInProcessObjectReference rappresenta un riferimento a un JS oggetto le cui funzioni possono essere richiamate in modo sincrono nei componenti lato client. Per altre informazioni, vedere la sezione Interoperabilità sincrona JS nei componenti lato client.
Nota
Quando il file esterno JS viene fornito da una Razor libreria di classi, specificare il file JS del modulo usando il relativo percorso stabile della risorsa Web statica: ./_content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)}
- Il segmento di percorso per la directory corrente (
./) è necessario per creare il percorso corretto della risorsa statica per il file JS. - Il segnaposto
{PACKAGE ID}è l'ID pacchetto della libreria. Per impostazione predefinita, l'ID pacchetto è il nome dell'assembly del progetto, se<PackageId>non è specificato nel file di progetto. Nell'esempio seguente il nome dell'assembly della libreria èComponentLibrarye il file di progetto della libreria non specifica<PackageId>. - Il segnaposto
{SCRIPT PATH AND FILE NAME (.js)}corrisponde al percorso e al nome del file inwwwroot. Nell'esempio seguente, il file esterno JS (script.js) viene collocato nella cartellawwwrootdella libreria di classi. -
moduleè un campo privato nullable IJSObjectReference della classe del componente (private IJSObjectReference? module;).
module = await js.InvokeAsync<IJSObjectReference>(
"import", "./_content/ComponentLibrary/scripts.js");
Per altre informazioni, vedere Utilizzare i componenti ASP.NET Core Razor di una libreria di classi Razor (RCL).
In tutta la documentazione Blazor, gli esempi usano l'estensione di file .js per i file dei moduli, non la più recente estensione di file .mjs (RFC 9239). La nostra documentazione continua a usare l'estensione di file .js per gli stessi motivi per cui la documentazione della Mozilla Foundation continua a usare l'estensione di file .js. Per ulteriori informazioni, vedere Aside — .mjs e .js (documentazione MDN).
Acquisire riferimenti agli elementi
Alcuni scenari di interoperabilità JavaScript (JS) richiedono riferimenti agli elementi HTML. Ad esempio, una libreria dell'interfaccia utente potrebbe richiedere un riferimento a un elemento per l'inizializzazione oppure potrebbe essere necessario chiamare API simili a comandi in un elemento, ad esempio click o play.
Acquisire i riferimenti agli elementi HTML in un componente usando l'approccio seguente:
- Aggiungere un
@refattributo all'elemento HTML. - Definire un campo di tipo ElementReference il cui nome corrisponde al valore dell'attributo
@ref.
L'esempio seguente mostra l'acquisizione di un riferimento all'elemento username<input> :
<input @ref="username" ... />
@code {
private ElementReference username;
}
Avviso
Usare solo un riferimento a un elemento per modificare il contenuto di un elemento vuoto che non interagisce con Blazor. Questo scenario è utile quando un'API di terze parti fornisce contenuto all'elemento . Poiché Blazor non interagisce con l'elemento, non esiste alcuna possibilità di conflitto tra Blazorla rappresentazione dell'elemento e il DOM.
Nell'esempio seguente, è pericoloso modificare il contenuto dell'elenco non ordinato (ul) usando MyList tramite l'interoperabilità JS, perché Blazor interagisce con il DOM per popolare le voci di questo elenco (<li>) dall'oggetto Todos:
<ul @ref="MyList">
@foreach (var item in Todos)
{
<li>@item.Text</li>
}
</ul>
L'uso del riferimento all'elemento MyList per leggere semplicemente il contenuto DOM o attivare un evento è supportato.
Se JS l'interoperabilità modifica i contenuti dell'elemento MyList e Blazor tenta di applicare le modifiche all'elemento, le modifiche non corrisponderanno al DOM. La modifica dei contenuti dell'elenco mediante JS l'interoperabilità con il riferimento all'elemento MyList non è supportata.
Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).
Un ElementReference viene passato al codice JS tramite interoperabilità JS. Il JS codice riceve un'istanza HTMLElement che può essere usata con le normali API DOM. Ad esempio, il codice seguente definisce un metodo di estensione .NET (TriggerClickEvent) che consente di inviare un clic del mouse a un elemento.
La JS funzione clickElement crea un click evento sull'elemento HTML passato (element):
window.interopFunctions = {
clickElement : function (element) {
element.click();
}
}
Per chiamare una JS funzione che non restituisce un valore, usare JSRuntimeExtensions.InvokeVoidAsync. Il codice seguente attiva un evento click lato client chiamando la precedente funzione JS con il ElementReference acquisito:
@inject IJSRuntime JS
<button @ref="exampleButton">Example Button</button>
<button @onclick="TriggerClick">
Trigger click event on <code>Example Button</code>
</button>
@code {
private ElementReference exampleButton;
public async Task TriggerClick()
{
await JS.InvokeVoidAsync(
"interopFunctions.clickElement", exampleButton);
}
}
Per usare un metodo di estensione, creare un metodo di estensione statico che riceve l'istanza IJSRuntime :
public static async Task TriggerClickEvent(this ElementReference elementRef,
IJSRuntime js)
{
await js.InvokeVoidAsync("interopFunctions.clickElement", elementRef);
}
Il clickElement metodo viene chiamato direttamente sull'oggetto . L'esempio seguente presuppone che il metodo TriggerClickEvent sia disponibile nel namespace JsInteropClasses:
@inject IJSRuntime JS
@using JsInteropClasses
<button @ref="exampleButton">Example Button</button>
<button @onclick="TriggerClick">
Trigger click event on <code>Example Button</code>
</button>
@code {
private ElementReference exampleButton;
public async Task TriggerClick()
{
await exampleButton.TriggerClickEvent(JS);
}
}
Importante
La exampleButton variabile viene popolata solo dopo il rendering del componente. Se un ElementReference non inizializzato viene passato al codice JS, il codice JS riceve il valore null. Per manipolare i riferimenti agli elementi dopo che il componente ha completato il rendering, utilizzare i metodi del ciclo di vita del componente OnAfterRenderAsync o OnAfterRender.
Quando si usano tipi generici e si restituisce un valore, usare ValueTask<TResult>:
public static ValueTask<T> GenericMethod<T>(
this ElementReference elementRef, IJSRuntime js) =>
js.InvokeAsync<T>("{JAVASCRIPT FUNCTION}", elementRef);
Il {JAVASCRIPT FUNCTION} segnaposto è l'identificatore della JS funzione.
GenericMethod viene chiamato direttamente sull'oggetto con un tipo . L'esempio seguente presuppone che GenericMethod sia disponibile dallo spazio dei nomi JsInteropClasses:
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string? returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string? returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
@inject IJSRuntime JS
@using JsInteropClasses
<input @ref="username" />
<button @onclick="OnClickMethod">Do something generic</button>
<p>
returnValue: @returnValue
</p>
@code {
private ElementReference username;
private string returnValue;
private async Task OnClickMethod()
{
returnValue = await username.GenericMethod<string>(JS);
}
}
Elementi di riferimento tra componenti
Non è possibile passare un oggetto ElementReference tra i componenti perché:
- L'istanza è garantita esistere solo dopo che il componente è stato renderizzato, ossia durante o dopo l'esecuzione del metodo OnAfterRender/OnAfterRenderAsync del componente.
- Un ElementReference è un
struct, che non può essere passato come parametro di componente.
Affinché un componente padre renda disponibile un riferimento a un elemento ad altri componenti, il componente padre può:
- Consenti ai componenti figli di registrare callback.
- Richiamare i callback registrati durante l'evento OnAfterRender con il riferimento all'elemento passato. Indirettamente, questo approccio consente ai componenti figli di interagire con il riferimento all'elemento padre.
<style>
.red { color: red }
</style>
<script>
function setElementClass(element, className) {
var myElement = element;
myElement.classList.add(className);
}
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
CallJs7.razor (componente padre):
@page "/call-js-7"
<PageTitle>Call JS 7</PageTitle>
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJs7.razor (componente padre):
@page "/call-js-7"
<PageTitle>Call JS 7</PageTitle>
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor (componente padre):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor (componente padre):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor (componente padre):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJsExample7.razor (componente padre):
@page "/call-js-example-7"
<h1>Call JS Example 7</h1>
<h2 @ref="title">Hello, world!</h2>
Welcome to your new app.
<SurveyPrompt Parent="this" Title="How is Blazor working for you?" />
CallJs7.razor.cs:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJs7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private readonly List<IObserver<ElementReference>> subscriptions = [];
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription(IObserver<ElementReference> observer,
CallJs7 self) : IDisposable
{
public IObserver<ElementReference> Observer { get; } = observer;
public CallJs7 Self { get; } = self;
public void Dispose() => Self.subscriptions.Remove(Observer);
}
}
CallJs7.razor.cs:
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJs7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private readonly List<IObserver<ElementReference>> subscriptions = [];
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription(IObserver<ElementReference> observer,
CallJs7 self) : IDisposable
{
public IObserver<ElementReference> Observer { get; } = observer;
public CallJs7 Self { get; } = self;
public void Dispose() => Self.subscriptions.Remove(Observer);
}
}
CallJsExample7.razor.cs:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
CallJsExample7.razor.cs:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages;
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
CallJsExample7.razor.cs:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages
{
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
}
CallJsExample7.razor.cs:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Components;
namespace BlazorSample.Pages
{
public partial class CallJsExample7 :
ComponentBase, IObservable<ElementReference>, IDisposable
{
private bool disposing;
private IList<IObserver<ElementReference>> subscriptions =
new List<IObserver<ElementReference>>();
private ElementReference title;
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
foreach (var subscription in subscriptions)
{
subscription.OnNext(title);
}
}
public void Dispose()
{
disposing = true;
foreach (var subscription in subscriptions)
{
try
{
subscription.OnCompleted();
}
catch (Exception)
{
}
}
subscriptions.Clear();
}
public IDisposable Subscribe(IObserver<ElementReference> observer)
{
if (disposing)
{
throw new InvalidOperationException("Parent being disposed");
}
subscriptions.Add(observer);
return new Subscription(observer, this);
}
private class Subscription : IDisposable
{
public Subscription(IObserver<ElementReference> observer,
CallJsExample7 self)
{
Observer = observer;
Self = self;
}
public IObserver<ElementReference> Observer { get; }
public CallJsExample7 Self { get; }
public void Dispose()
{
Self.subscriptions.Remove(Observer);
}
}
}
}
Nell'esempio precedente lo spazio dei nomi dell'app è BlazorSample. Se si testa il codice localmente, aggiornare lo spazio dei nomi.
SurveyPrompt.razor (componente figlio):
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186158">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186158">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2186157">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string? Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string Title { get; set; }
}
<div class="alert alert-secondary mt-4" role="alert">
<span class="oi oi-pencil mr-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold"
href="https://go.microsoft.com/fwlink/?linkid=2109206">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
[Parameter]
public string Title { get; set; }
}
SurveyPrompt.razor.cs:
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Components;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent?.Subscribe(this);
}
public void OnCompleted() => subscription = null;
public void OnError(Exception error) => subscription = null;
public void OnNext(ElementReference value) =>
_ = (JS?.InvokeAsync<object>("setElementClass", [ value, "red" ]));
public void Dispose()
{
subscription?.Dispose();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Components;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent?.Subscribe(this);
}
public void OnCompleted() => subscription = null;
public void OnError(Exception error) => subscription = null;
public void OnNext(ElementReference value) =>
_ = (JS?.InvokeAsync<object>("setElementClass", [ value, "red" ]));
public void Dispose()
{
subscription?.Dispose();
// The following prevents derived types that introduce a
// finalizer from needing to re-implement IDisposable.
GC.SuppressFinalize(this);
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription =
Parent is not null ? Parent.Subscribe(this) : null;
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS?.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared;
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable? subscription = null;
[Parameter]
public IObservable<ElementReference>? Parent { get; set; }
[Inject]
public IJSRuntime? JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription =
Parent is not null ? Parent.Subscribe(this) : null;
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS?.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared
{
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable subscription = null;
[Parameter]
public IObservable<ElementReference> Parent { get; set; }
[Inject]
public IJSRuntime JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent.Subscribe(this);
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS?.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
}
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorSample.Shared
{
public partial class SurveyPrompt :
ComponentBase, IObserver<ElementReference>, IDisposable
{
private IDisposable subscription = null;
[Parameter]
public IObservable<ElementReference> Parent { get; set; }
[Inject]
public IJSRuntime JS {get; set;}
protected override void OnParametersSet()
{
base.OnParametersSet();
subscription?.Dispose();
subscription = Parent.Subscribe(this);
}
public void OnCompleted()
{
subscription = null;
}
public void OnError(Exception error)
{
subscription = null;
}
public void OnNext(ElementReference value)
{
JS?.InvokeAsync<object>(
"setElementClass", new object[] { value, "red" });
}
public void Dispose()
{
subscription?.Dispose();
}
}
}
Nell'esempio precedente lo spazio dei nomi dell'app è BlazorSample con componenti condivisi nella Shared cartella . Se si testa il codice in locale, aggiornare lo spazio dei nomi.
Proteggere le chiamate di interoperabilità con JavaScript
Questa sezione si applica solo ai componenti di Interactive Server, ma i componenti lato client possono anche impostare JS timeout di interoperabilità se le condizioni lo giustificano.
Nelle app lato server con interattività sul server, l'interoperabilità con JavaScript (JS) può non riuscire a causa di errori di rete e deve essere considerata non affidabile. Blazor le app usano un timeout di un minuto per JS le chiamate di interoperabilità. Se un'app può tollerare un timeout più aggressivo, impostare il timeout usando uno degli approcci seguenti.
Impostare un timeout globale in Program.cs con CircuitOptions.JSInteropDefaultCallTimeout:
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(options =>
options.JSInteropDefaultCallTimeout = {TIMEOUT});
builder.Services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Impostare un timeout globale nel metodo Startup.ConfigureServices di Startup.cs con CircuitOptions.JSInteropDefaultCallTimeout:
services.AddServerSideBlazor(
options => options.JSInteropDefaultCallTimeout = {TIMEOUT});
Il segnaposto {TIMEOUT} è un TimeSpan (ad esempio, TimeSpan.FromSeconds(80)).
Impostare un timeout per chiamata nel codice del componente. Il timeout specificato esegue l'override del timeout globale impostato da JSInteropDefaultCallTimeout:
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, [ "Arg1" ]);
var result = await JS.InvokeAsync<string>("{ID}", {TIMEOUT}, new[] { "Arg1" });
Nell'esempio precedente:
- Il segnaposto
{TIMEOUT}è un TimeSpan (ad esempio,TimeSpan.FromSeconds(80)). - Il
{ID}segnaposto è l'identificatore della funzione da richiamare. Ad esempio, il valoresomeScope.someFunctionrichiama la funzionewindow.someScope.someFunction.
Sebbene una causa comune degli errori di interoperabilità siano gli errori di rete con JS componenti lato server, è possibile impostare timeout per singola chiamata per le chiamate di interoperabilità JS dei componenti lato client. Sebbene non esista alcun circuito Blazor-SignalR per un componente lato client, le chiamate di interoperabilità JS potrebbero non riuscire per altri motivi pertinenti.
Per altre informazioni sull'esaurimento delle risorse, vedere Indicazioni per la mitigazione delle minacce per ASP.NET Core Blazor rendering interattivo lato server.
Evitare riferimenti a oggetti circolari
Gli oggetti che contengono riferimenti circolari non possono essere serializzati nel client per:
- Chiamate di metodo .NET.
- Chiamate di metodo JavaScript da C# quando il tipo di ritorno ha riferimenti circolari.
Librerie JavaScript che eseguono il rendering dell'interfaccia utente
A volte è possibile usare librerie JavaScript (JS) che producono elementi dell'interfaccia utente visibili all'interno del DOM del browser. A prima vista, questo potrebbe sembrare difficile perché il sistema di diffing di Blazor si basa sull'avere il controllo dell'albero DOM e genera errori se del codice esterno modifica l'albero DOM e invalida il meccanismo con cui applica le differenze. Questa non è una Blazorlimitazione specifica. La stessa sfida si verifica con qualsiasi framework dell'interfaccia utente basato su diff.
Fortunatamente, è semplice incorporare in modo affidabile l'interfaccia utente generata esternamente all'interno di un'interfaccia Razor utente del componente. La tecnica consigliata consiste nel produrre un elemento vuoto nel codice del componente (.razor file). Per quanto riguarda il sistema di diffing di Blazor, l'elemento è sempre vuoto, quindi il renderer non discende ricorsivamente nell'elemento e ne lascia invariato il contenuto. In questo modo è possibile popolare l'elemento con contenuto arbitrario gestito esternamente.
L'esempio seguente illustra il concetto. All'interno dell'istruzione if, quando firstRender è true, interagire con unmanagedElement al di fuori di Blazor usando l'interoperabilità JS. Ad esempio, chiamare una libreria esterna JS per popolare l'elemento.
Blazor lascia solo il contenuto dell'elemento fino a quando questo componente non viene rimosso. Quando il componente viene rimosso, viene rimosso anche l'intero sottoalbero DOM del componente.
<h1>Hello! This is a Razor component rendered at @DateTime.Now</h1>
<div @ref="unmanagedElement"></div>
@code {
private ElementReference unmanagedElement;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
...
}
}
}
Si consideri l'esempio seguente che esegue il rendering di una mappa interattiva usando le API Mapbox open source.
Il modulo seguente JS viene inserito nell'app o reso disponibile da una libreria di Razor classi.
Nota
Per creare la mappa Mapbox , ottenere un token di accesso da Mapbox Sign in e specificarlo dove {ACCESS TOKEN} appare nel codice seguente.
wwwroot/mapComponent.js:
import 'https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.js';
mapboxgl.accessToken = '{ACCESS TOKEN}';
export function addMapToElement(element) {
return new mapboxgl.Map({
container: element,
style: 'mapbox://styles/mapbox/streets-v11',
center: [-74.5, 40],
zoom: 9
});
}
export function setMapCenter(map, latitude, longitude) {
map.setCenter([longitude, latitude]);
}
Per produrre stili corretti, aggiungere il tag del foglio di stile seguente alla pagina HTML host.
Aggiungere l'elemento seguente <link> al markup dell'elemento <head> (posizione del <head> contenuto):
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css"
rel="stylesheet" />
CallJs8.razor:
@page "/call-js-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 8</PageTitle>
<HeadContent>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css"
rel="stylesheet" />
</HeadContent>
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
try
{
await mapInstance.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
if (mapModule is not null)
{
try
{
await mapModule.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallJs8.razor:
@page "/call-js-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<PageTitle>Call JS 8</PageTitle>
<HeadContent>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.12.0/mapbox-gl.css"
rel="stylesheet" />
</HeadContent>
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
try
{
await mapInstance.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
if (mapModule is not null)
{
try
{
await mapModule.DisposeAsync();
}
catch (JSDisconnectedException)
{
}
}
}
}
CallJsExample8.razor:
@page "/call-js-example-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
await mapInstance.DisposeAsync();
}
if (mapModule is not null)
{
await mapModule.DisposeAsync();
}
}
}
CallJsExample8.razor:
@page "/call-js-example-8"
@implements IAsyncDisposable
@inject IJSRuntime JS
<h1>Call JS Example 8</h1>
<div @ref="mapElement" style='width:400px;height:300px'></div>
<button @onclick="() => ShowAsync(51.454514, -2.587910)">Show Bristol, UK</button>
<button @onclick="() => ShowAsync(35.6762, 139.6503)">Show Tokyo, Japan</button>
@code
{
private ElementReference mapElement;
private IJSObjectReference? mapModule;
private IJSObjectReference? mapInstance;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
mapModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./mapComponent.js");
mapInstance = await mapModule.InvokeAsync<IJSObjectReference>(
"addMapToElement", mapElement);
}
}
private async Task ShowAsync(double latitude, double longitude)
{
if (mapModule is not null && mapInstance is not null)
{
await mapModule.InvokeVoidAsync("setMapCenter", mapInstance,
latitude, longitude);
}
}
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (mapInstance is not null)
{
await mapInstance.DisposeAsync();
}
if (mapModule is not null)
{
await mapModule.DisposeAsync();
}
}
}
L'esempio precedente produce un'interfaccia utente mappa interattiva. L'utente:
- Può trascinare per scorrere o zoom.
- Selezionare i pulsanti per passare a posizioni predefinite.
Nell'esempio precedente:
- Il
<div>con@ref="mapElement"viene lasciato vuoto per quanto riguarda Blazor. Lomapbox-gl.jsscript può popolare in modo sicuro l'elemento e modificarne il contenuto. Usare questa tecnica con qualsiasi JS libreria che esegue il rendering dell'interfaccia utente. È possibile incorporare componenti da un framework SPA di terze parti JS all'interno Razor dei componenti, purché non tentino di raggiungere e modificare altre parti della pagina. Non è sicuro per il codice esterno JS modificare elementi che Blazor non consideravuoti. - Quando si usa questo approccio, tenere presenti le regole su come Blazor conservare o distruggere gli elementi DOM. Il componente gestisce in modo sicuro gli eventi di clic del pulsante e aggiorna l'istanza di mappa esistente perché gli elementi DOM vengono mantenuti laddove possibile. Se si esegue il rendering di un elenco di elementi della mappa dall'interno di un
@foreachciclo, si vuole usare@keyper garantire la conservazione delle istanze del componente. In caso contrario, le modifiche apportate ai dati dell'elenco potrebbero causare la conservazione dello stato delle istanze precedenti in modo indesiderato. Per altre informazioni, vedere come usare l'attributo@keydi direttiva per mantenere la relazione tra elementi, componenti e oggetti modello. - L'esempio incapsula JS la logica e le dipendenze all'interno di un modulo JavaScript e carica il modulo in modo dinamico usando l'identificatore
import. Per altre informazioni, vedere Isolamento JavaScript nei moduli JavaScript.
Supporto della matrice di byte
Blazor supporta l'interoperabilità JavaScript (JS) con array di byte ottimizzati che evita la codifica e la decodifica degli array di byte in Base64. L'esempio seguente usa l'interoperabilità JS per passare un array di byte a JavaScript.
Specificare una receiveByteArrayJS funzione. La funzione viene chiamata con InvokeVoidAsync e non restituisce un valore:
<script>
window.receiveByteArray = (bytes) => {
let utf8decoder = new TextDecoder();
let str = utf8decoder.decode(bytes);
return str;
};
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
CallJs9.razor:
@page "/call-js-9"
@inject IJSRuntime JS
<h1>Call JS Example 9</h1>
<p>
<button @onclick="SendByteArray">Send Bytes</button>
</p>
<p>
@result
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private string? result;
private async Task SendByteArray()
{
var bytes = new byte[] { 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
0x20, 0x43, 0x61, 0x70, 0x74, 0x69, 0x61, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e };
result = await JS.InvokeAsync<string>("receiveByteArray", bytes);
}
}
CallJsExample9.razor:
@page "/call-js-example-9"
@inject IJSRuntime JS
<h1>Call JS Example 9</h1>
<p>
<button @onclick="SendByteArray">Send Bytes</button>
</p>
<p>
@result
</p>
<p>
Quote ©2005 <a href="https://www.uphe.com">Universal Pictures</a>:
<a href="https://www.uphe.com/movies/serenity-2005">Serenity</a><br>
<a href="https://www.imdb.com/name/nm0821612/">Jewel Staite on IMDB</a>
</p>
@code {
private string? result;
private async Task SendByteArray()
{
var bytes = new byte[] { 0x45, 0x76, 0x65, 0x72, 0x79, 0x74, 0x68, 0x69,
0x6e, 0x67, 0x27, 0x73, 0x20, 0x73, 0x68, 0x69, 0x6e, 0x79, 0x2c,
0x20, 0x43, 0x61, 0x70, 0x74, 0x69, 0x61, 0x6e, 0x2e, 0x20, 0x4e,
0x6f, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x72, 0x65, 0x74, 0x2e };
result = await JS.InvokeAsync<string>("receiveByteArray", bytes);
}
}
Per informazioni sull'uso di una matrice di byte quando si chiama .NET da JavaScript, vedere Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor.
Trasmettere da .NET a JavaScript
Blazor supporta lo streaming di dati direttamente da .NET a JavaScript (JS). I flussi vengono creati usando un oggetto DotNetStreamReference.
DotNetStreamReference rappresenta un flusso .NET e usa i parametri seguenti:
-
stream: Il flusso inviato al JS. -
leaveOpen: determina se il flusso viene lasciato aperto dopo la trasmissione. Se non viene specificato un valore,leaveOpenper impostazione predefinita èfalse.
In JSusare un buffer di matrice o un flusso leggibile per ricevere i dati:
Uso di un
ArrayBuffer:async function streamToJavaScript(streamRef) { const data = await streamRef.arrayBuffer(); }Utilizzo di
ReadableStream:async function streamToJavaScript(streamRef) { const stream = await streamRef.stream(); }
Nel codice C#:
var streamRef = new DotNetStreamReference(stream: {STREAM}, leaveOpen: false);
await JS.InvokeVoidAsync("streamToJavaScript", streamRef);
Nell'esempio precedente:
- Il
{STREAM}segnaposto rappresenta l'oggetto Stream inviato a JS. -
JSè un'istanza IJSRuntime iniettata.
L'eliminazione di un'istanza DotNetStreamReference in genere non è necessaria. Quando leaveOpen è impostato sul valore predefinito di false, l'oggetto sottostante Stream viene eliminato automaticamente dopo la trasmissione a JS.
Se leaveOpen è true, l'eliminazione di un DotNetStreamReference oggetto non elimina l'oggetto sottostante Stream. Il codice dell'app determina quando eliminare l'oggetto sottostante Stream. Quando si decide come eliminare l'oggetto sottostante Stream, considerare quanto segue:
- L'eliminazione di un Stream mentre viene trasmesso a JS è considerata un errore dell'applicazione e può causare il verificarsi di un'eccezione non gestita.
- Stream la trasmissione inizia non appena DotNetStreamReference viene passato come argomento a una JS chiamata di interoperabilità, indipendentemente dal fatto che il flusso venga effettivamente usato nella JS logica.
Date queste caratteristiche, si consiglia di eliminare l'elemento sottostante Stream solo dopo che è stato completamente utilizzato da JS (quando si risolve la promise restituita da arrayBuffer o stream). Ne consegue che un DotNetStreamReference deve essere passato a JS solo se verrà consumato incondizionatamente dalla logica di JS.
Chiamare i metodi .NET dalle funzioni JavaScript in ASP.NET Core Blazor illustra l'operazione inversa, il flusso da JavaScript a .NET.
Intercettare le eccezioni JavaScript
Per intercettare le eccezioni JS, racchiudere il codice di interoperabilità JS in un blocco try-catch e intercettare un'eccezione JSException.
Nell'esempio seguente la nonFunctionJS funzione non esiste. Quando la funzione non viene trovata, l'oggetto JSException viene intrappolato con un Message che indica l'errore seguente:
Could not find 'nonFunction' ('nonFunction' was undefined).
CallJs11.razor:
@page "/call-js-11"
@inject IJSRuntime JS
<PageTitle>Call JS 11</PageTitle>
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJs11.razor:
@page "/call-js-11"
@inject IJSRuntime JS
<PageTitle>Call JS 11</PageTitle>
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string? errorMessage;
private string? result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string errorMessage;
private string result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
CallJsExample11.razor:
@page "/call-js-example-11"
@inject IJSRuntime JS
<h1>Call JS Example 11</h1>
<p>
<button @onclick="CatchUndefinedJSFunction">Catch Exception</button>
</p>
<p>
@result
</p>
<p>
@errorMessage
</p>
@code {
private string errorMessage;
private string result;
private async Task CatchUndefinedJSFunction()
{
try
{
result = await JS.InvokeAsync<string>("nonFunction");
}
catch (JSException e)
{
errorMessage = $"Error Message: {e.Message}";
}
}
}
Interrompere una funzione JavaScript a esecuzione prolungata
Usare un JSAbortController con un CancellationTokenSource nel componente per interrompere dal codice C# una funzione JavaScript di lunga durata.
La classe seguente JSHelpers contiene una funzione simulata a esecuzione prolungata, longRunningFn, che conta continuamente finché AbortController.signal indica che è stato chiamato AbortController.abort. La sleep funzione è a scopo dimostrativo per simulare l'esecuzione lenta della funzione a esecuzione prolungata e non sarebbe presente nel codice di produzione. Quando un componente chiama stopFn, a longRunningFn viene segnalato di interrompersi tramite il controllo condizionale del ciclo while su AbortSignal.aborted.
<script>
class Helpers {
static #controller = new AbortController();
static async #sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
static async longRunningFn() {
var i = 0;
while (!this.#controller.signal.aborted) {
i++;
console.log(`longRunningFn: ${i}`);
await this.#sleep(1000);
}
}
static stopFn() {
this.#controller.abort();
console.log('longRunningFn aborted!');
}
}
window.Helpers = Helpers;
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
Componente seguente:
- Richiama la JS funzione
longRunningFnquando ilStart Taskpulsante è selezionato. Un CancellationTokenSource viene usato per gestire l'esecuzione della funzione di lunga durata. CancellationToken.Register imposta un JS delegato di chiamata di interoperabilità per eseguire la JS funzionestopFnquando l'oggetto CancellationTokenSource.Token viene annullato. - Quando viene selezionato il pulsante
Cancel Task, CancellationTokenSource.Token viene annullato tramite una chiamata a Cancel. - Il CancellationTokenSource viene rilasciato nel metodo
Dispose.
CallJs12.razor:
@page "/call-js-12"
@inject IJSRuntime JS
<h1>Cancel long-running JS interop</h1>
<p>
<button @onclick="StartTask">Start Task</button>
<button @onclick="CancelTask">Cancel Task</button>
</p>
@code {
private CancellationTokenSource? cts;
private async Task StartTask()
{
cts = new CancellationTokenSource();
cts.Token.Register(() => JS.InvokeVoidAsync("Helpers.stopFn"));
await JS.InvokeVoidAsync("Helpers.longRunningFn");
}
private void CancelTask()
{
cts?.Cancel();
}
public void Dispose()
{
cts?.Cancel();
cts?.Dispose();
}
}
CallJsExample12.razor:
@page "/call-js-example-12"
@inject IJSRuntime JS
<h1>Cancel long-running JS interop</h1>
<p>
<button @onclick="StartTask">Start Task</button>
<button @onclick="CancelTask">Cancel Task</button>
</p>
@code {
private CancellationTokenSource? cts;
private async Task StartTask()
{
cts = new CancellationTokenSource();
cts.Token.Register(() => JS.InvokeVoidAsync("Helpers.stopFn"));
await JS.InvokeVoidAsync("Helpers.longRunningFn");
}
private void CancelTask()
{
cts?.Cancel();
}
public void Dispose()
{
cts?.Cancel();
cts?.Dispose();
}
}
La console degli strumenti di sviluppo del browser indica l'esecuzione della funzione a esecuzione prolungata JS dopo che si seleziona il pulsante Start Task e quando la funzione viene interrotta dopo che si seleziona il pulsante Cancel Task:
longRunningFn: 1
longRunningFn: 2
longRunningFn: 3
longRunningFn aborted!
Interop JavaScript [JSImport]/[JSExport]
Questa sezione si applica ai componenti lato client.
In alternativa all'uso di JavaScript (JS) nei componenti lato client usando il meccanismo di interoperabilità JS di Blazor basato sull'interfaccia IJSRuntime, per le app destinate a .NET 7 o versioni successive è disponibile un'API di interoperabilità JS[JSImport]/[JSExport].
Per altre informazioni, vedere Interoperabilità JSImport/JSExport JavaScript con ASP.NET Core Blazor.
Interoperabilità JavaScript senza marshalling
Questa sezione si applica ai componenti lato client.
L'interoperabilità unmarshalled tramite l'interfaccia IJSUnmarshalledRuntime è obsoleta e deve essere sostituita con l'interoperabilità JavaScript[JSImport]/[JSExport].
Per altre informazioni, vedere Interoperabilità JSImport/JSExport JavaScript con ASP.NET Core Blazor.
Interoperabilità JavaScript unmarshalled
Blazor WebAssembly I componenti possono riscontrare prestazioni scarse quando gli oggetti .NET vengono serializzati per l'interoperabilità JavaScript (JS) e uno dei seguenti è vero:
- Un volume elevato di oggetti .NET viene serializzato rapidamente. Ad esempio, le prestazioni scarse possono risultare quando JS vengono effettuate chiamate di interoperabilità sulla base dello spostamento di un dispositivo di input, ad esempio la rotazione di una rotellina del mouse.
- Gli oggetti .NET di grandi dimensioni o molti oggetti .NET devono essere serializzati ai fini dell'JS interoperabilità. Ad esempio, le prestazioni scarse possono risultare quando JS le chiamate di interoperabilità richiedono la serializzazione di decine di file.
IJSUnmarshalledObjectReference rappresenta un riferimento a un JS oggetto le cui funzioni possono essere richiamate senza l'overhead della serializzazione dei dati .NET.
Nell'esempio seguente :
- Una struct contenente una stringa e un numero intero viene passata non serializzata a JS.
- JS le funzioni elaborano i dati e restituiscono un valore booleano o una stringa al chiamante.
- Una JS stringa non è convertibile direttamente in un oggetto .NET
string. LaunmarshalledFunctionReturnStringfunzione chiamaBINDING.js_string_to_mono_stringper gestire la conversione di una JS stringa.
Nota
Gli esempi seguenti non sono casi d'uso tipici per questo scenario perché lo struct passato a JS non comporta prestazioni di componenti scarse. L'esempio usa solo un oggetto di piccole dimensioni per illustrare i concetti per il passaggio di dati .NET nonrializzati.
<script>
window.returnObjectReference = () => {
return {
unmarshalledFunctionReturnBoolean: function (fields) {
const name = Blazor.platform.readStringField(fields, 0);
const year = Blazor.platform.readInt32Field(fields, 8);
return name === "Brigadier Alistair Gordon Lethbridge-Stewart" &&
year === 1968;
},
unmarshalledFunctionReturnString: function (fields) {
const name = Blazor.platform.readStringField(fields, 0);
const year = Blazor.platform.readInt32Field(fields, 8);
return BINDING.js_string_to_mono_string(`Hello, ${name} (${year})!`);
}
};
}
</script>
Nota
Per indicazioni generali sulla posizione di JS e sulle nostre raccomandazioni per le app di produzione, vedere Posizione di JavaScript nelle app di ASP.NET Core Blazor.
Avviso
Il nome della funzione, il js_string_to_mono_string comportamento e l'esistenza sono soggetti a modifiche in una versione futura di .NET. Ad esempio:
- È probabile che la funzione venga rinominata.
- La funzione stessa potrebbe essere rimossa a favore della conversione automatica delle stringhe dal framework.
CallJsExample10.razor:
@page "/call-js-example-10"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Call JS Example 10</h1>
@if (callResultForBoolean)
{
<p>JS interop was successful!</p>
}
@if (!string.IsNullOrEmpty(callResultForString))
{
<p>@callResultForString</p>
}
<p>
<button @onclick="CallJSUnmarshalledForBoolean">
Call Unmarshalled JS & Return Boolean
</button>
<button @onclick="CallJSUnmarshalledForString">
Call Unmarshalled JS & Return String
</button>
</p>
<p>
<a href="https://www.doctorwho.tv">Doctor Who</a>
is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>
@code {
private bool callResultForBoolean;
private string? callResultForString;
private void CallJSUnmarshalledForBoolean()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForBoolean =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
"unmarshalledFunctionReturnBoolean", GetStruct());
}
private void CallJSUnmarshalledForString()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForString =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, string>(
"unmarshalledFunctionReturnString", GetStruct());
}
private InteropStruct GetStruct()
{
return new InteropStruct
{
Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
Year = 1968,
};
}
[StructLayout(LayoutKind.Explicit)]
public struct InteropStruct
{
[FieldOffset(0)]
public string Name;
[FieldOffset(8)]
public int Year;
}
}
@page "/call-js-example-10"
@using System.Runtime.InteropServices
@using Microsoft.JSInterop
@inject IJSRuntime JS
<h1>Call JS Example 10</h1>
@if (callResultForBoolean)
{
<p>JS interop was successful!</p>
}
@if (!string.IsNullOrEmpty(callResultForString))
{
<p>@callResultForString</p>
}
<p>
<button @onclick="CallJSUnmarshalledForBoolean">
Call Unmarshalled JS & Return Boolean
</button>
<button @onclick="CallJSUnmarshalledForString">
Call Unmarshalled JS & Return String
</button>
</p>
<p>
<a href="https://www.doctorwho.tv">Doctor Who</a>
is a registered trademark of the <a href="https://www.bbc.com/">BBC</a>.
</p>
@code {
private bool callResultForBoolean;
private string callResultForString;
private void CallJSUnmarshalledForBoolean()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForBoolean =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>(
"unmarshalledFunctionReturnBoolean", GetStruct());
}
private void CallJSUnmarshalledForString()
{
var unmarshalledRuntime = (IJSUnmarshalledRuntime)JS;
var jsUnmarshalledReference = unmarshalledRuntime
.InvokeUnmarshalled<IJSUnmarshalledObjectReference>(
"returnObjectReference");
callResultForString =
jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, string>(
"unmarshalledFunctionReturnString", GetStruct());
}
private InteropStruct GetStruct()
{
return new InteropStruct
{
Name = "Brigadier Alistair Gordon Lethbridge-Stewart",
Year = 1968,
};
}
[StructLayout(LayoutKind.Explicit)]
public struct InteropStruct
{
[FieldOffset(0)]
public string Name;
[FieldOffset(8)]
public int Year;
}
}
Se un'istanza IJSUnmarshalledObjectReference non viene eliminata nel codice C#, può essere eliminata in JS. La funzione seguente dispose elimina il riferimento all'oggetto quando viene chiamato da JS:
window.exampleJSObjectReferenceNotDisposedInCSharp = () => {
return {
dispose: function () {
DotNet.disposeJSObjectReference(this);
},
...
};
}
I tipi di matrice possono essere convertiti da JS oggetti in oggetti .NET usando js_typed_array_to_array, ma la JS matrice deve essere una matrice tipizzata. Le matrici da JS possono essere lette nel codice C# come matrice di oggetti .NET (object[]).
Altri tipi di dati, ad esempio le matrici di stringhe, possono essere convertiti, ma richiedono la creazione di un nuovo oggetto matrice Mono () e l'impostazione del relativo valore (mono_obj_array_newmono_obj_array_set).
Avviso
JS Le funzioni fornite dal Blazor framework, ad esempio js_typed_array_to_array, mono_obj_array_newe mono_obj_array_set, sono soggette a modifiche del nome, modifiche comportamentali o rimozione nelle versioni future di .NET.
Eliminazione dei riferimenti agli oggetti di interoperabilità JavaScript
Esempi presenti negli articoli sull'interoperabilità di JavaScript (JS) illustrano i tipici schemi di eliminazione degli oggetti:
Quando si chiama JS da .NET, come descritto in questo articolo, eliminare tutte le istanze di IJSObjectReference/IJSInProcessObjectReference/
JSObjectReferencecreate da .NET o da JS per evitare perdite di memoria JS.Quando si chiama .NET da JS, come descritto in Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor, eliminare un'istanza DotNetObjectReference creata, sia da .NET sia da JS, per evitare perdite di memoria in .NET.
JS I riferimenti agli oggetti di interoperabilità vengono implementati come una mappa indicizzata tramite un identificatore dal lato della chiamata di interoperabilità JS che crea il riferimento. Quando l'eliminazione di oggetti viene avviata da .NET o JS lato, Blazor rimuove la voce dalla mappa e l'oggetto può essere sottoposto a Garbage Collection purché non sia presente alcun altro riferimento sicuro all'oggetto.
Come minimo, rilasciare sempre gli oggetti creati dal lato .NET per evitare perdite di memoria gestita da .NET.
Attività di pulizia DOM durante l'eliminazione dei componenti
Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).
Chiamate di interoperabilità con JavaScript senza un circuito
Per altre informazioni, vedere ASP.NET Core Blazor interoperabilità JavaScript (JS interop).
Risorse aggiuntive
- Chiamare metodi .NET da funzioni JavaScript in ASP.NET Core Blazor
-
InteropComponent.razoresempio (dotnet/AspNetCoreramo del repositorymainGitHub): ilmainramo rappresenta lo sviluppo corrente dell'unità prodotto per la versione successiva di ASP.NET Core. Per selezionare il ramo per una versione diversa, ad esempiorelease/{VERSION}, in cui il segnaposto{VERSION}indica la versione di rilascio, utilizzare l'elenco a discesa Switch branches or tags per selezionare il ramo. Per un ramo che non esiste più, usare la scheda Tag per trovare l'API , ad esempiov7.0.0. -
Blazor repository GitHub degli esempi (
dotnet/blazor-samples) (come scaricare) - Gestire gli errori nelle app Blazor ASP.NET Core (sezione Interop JavaScript)
- Mitigazione delle minacce: funzioni JavaScript richiamate da .NET