[英]Blazor - JavaScript Interop - No .NET call dispatcher has been set
I am experimenting with a Blazor WebAssembly app .我正在试验Blazor WebAssembly 应用程序。 When my page (ie index.html) loads, I want to pass a JavaScript array to the Blazor app when it loads.当我的页面(即 index.html)加载时,我想在加载时将 JavaScript 数组传递给 Blazor 应用程序。 While trying to call a method from JavaScript , I've run into an error that says:在尝试从 JavaScript 调用一个方法时,我遇到了一个错误:
Uncaught (in promise) Error: No .NET call dispatcher has been set
My code looks like this:我的代码如下所示:
index.html index.html
<body>
<app>Loading...</app>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script>
let items = [
{ name:'Item 1', description:'This is a description of the first item' },
{ name:'Item 2', description:'This is a description of the second item' },
{ name:'Item 3', description:'This is a description of the third item' },
];
try
{
DotNet
.invokeMethodAsync('MyOrg.MyApp.Index', 'LoadItems')
.then(results => {
console.log(results);
})
;
}
catch (ex)
{
console.log(ex);
}
</script>
</body>
Index.razor Index.razor
@page "/"
@using System.Threading.Tasks;
@using Microsoft.Extensions.Logging;
@inject ILogger<Index> logger;
<p>
Items loaded: <span>@items.Count</span>
</p>
@code {
List<object> items = new List<object>();
[JSInvokable]
private Task LoadSnippets()
{
try
{
logger.LogInformation("Loading items...");
items = new List<object>();
}
catch (Exception ex)
{
logger.LogError(ex, $"Failed to load items.");
}
return Task.CompletedTask;
}
}
The first difference I noticed was that the example shown in the docs relied on a static
method.我注意到的第一个区别是文档中显示的示例依赖于static
方法。 Is this a requirement?这是要求吗? If so, that would imply there's not a way to perform logging for example.如果是这样,那就意味着没有办法执行例如日志记录。 Logging aside, even if I add static
to the LoadItems
method, I still get the error listed above.除了登录,即使我将static
添加到LoadItems
方法,我仍然会收到上面列出的错误。 I don't understand why.我不明白为什么。
In short, I'm trying to create a "headless" Blazor app.简而言之,我正在尝试创建一个“无头” Blazor 应用程序。 I'd like to use the richness of C# to work with the data, I need to pass the results to the UI, which relies on HTML/CSS.我想使用 C# 的丰富性来处理数据,我需要将结果传递给依赖 HTML/CSS 的 UI。 Thank you!谢谢!
Welcome to Blazor!欢迎来到 Blazor!
Firstly, I would suggest reading the docs at Call .Net from js首先,我建议从 js阅读Call .Net 上的文档
The way you include the script in your index.html means, that the script is executed before your wasm app is loaded.您在 index.html 中包含脚本的方式意味着脚本在您的 wasm 应用程序加载之前执行。
To solve the issue, it is a good practice to use js interop in OnAfterRenderAsync lifecycle method ( Lifecycle docs )为了解决这个问题,在 OnAfterRenderAsync 生命周期方法( Lifecycle docs )中使用 js interop 是一个很好的做法
So if you update your script to something like:因此,如果您将脚本更新为以下内容:
<script>
netFromJs = {
staticCall: function () {
let items = [
{ name: 'Item 1', description: 'This is a description of the first item' },
{ name: 'Item 2', description: 'This is a description of the second item' },
{ name: 'Item 3', description: 'This is a description of the third item' },
];
try {
DotNet
.invokeMethodAsync('wasmprerender.Client', 'LoadItems')
.then(results => {
console.log(results);
});
}
catch (ex) {
console.log(ex);
}
}
}
</script>
Then in your component (page is also component), you will be able to do something like:然后在您的组件(页面也是组件)中,您将能够执行以下操作:
@inject IJSRuntime _js
@code {
static List<object> items = new List<object>();
[JSInvokable]
public static Task LoadItems()
{
try
{
Console.WriteLine("Loading items");
items = new List<object>();
}
catch (Exception ex)
{
}
return Task.CompletedTask;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await _js.InvokeVoidAsync("netFromJs.static");
}
}
To address your note about that the static call.解决您关于静态调用的注释。 Your example works with the Static method call, Blazor also supports the Instance method call.您的示例适用于静态方法调用,Blazor 还支持实例方法调用。 You will find a nice example here Instance call你会在这里找到一个很好的例子实例调用
Lastly, I do not recommend touch index.html too much.最后,我不太推荐 touch index.html。 Create and reference separate .js file or from .NET 5 RC1 you can use Js isolation创建和引用单独的 .js 文件或从 .NET 5 RC1 您可以使用Js 隔离
There's a way to ensure Blazor is initialized before trying to call interop有一种方法可以确保在尝试调用互操作之前初始化 Blazor
Modify your index to prevent Blazor autoloading修改索引以防止 Blazor 自动加载
<script src="_framework/blazor.webassembly.js" autoload="false"></script>
Then you can boot Blazor yourself and use a promise to execute your interop然后你可以自己启动 Blazor 并使用 promise 来执行你的互操作
<script>
let items = [
{ name:'Item 1', description:'This is a description of the first item' },
{ name:'Item 2', description:'This is a description of the second item' },
{ name:'Item 3', description:'This is a description of the third item' },
];
window.Blazor.start()
.then(()=>DotNet.invokeMethodAsync('MyOrg.MyApp.Index', 'LoadItems'))
.then(results => console.log(results))
.catch(ex=>console.log(ex));
</script>
// Error I faced // 我遇到的错误
blazor.webassembly.js:1 Uncaught (in promise) Error: No .NET call dispatcher has been set. blazor.webassembly.js:1 Uncaught (in promise) Error: No .NET call dispatcher has been set. at m (blazor.webassembly.js:1:2655) at h (blazor.webassembly.js:1:2548) at Object.e.invokeMethodAsync (blazor.webassembly.js:1:3389) at HTMLDocument.在 m (blazor.webassembly.js:1:2655) 在 h (blazor.webassembly.js:1:2548) 在 Object.e.invokeMethodAsync (blazor.webassembly.js:1:3389) 在 HTMLDocument。 (CropHelper.js:73:12) (CropHelper.js:73:12)
// Solution // 解决方案
<script>
window.Blazor.start()
.then(function () {
var fileref = document.createElement('script')
fileref.setAttribute("type", "text/javascript")
fileref.setAttribute("src", "js/CropHelper.js")
if (typeof fileref != "undefined")
document.getElementsByTagName("head")[0].appendChild(fileref)
})
.then(results => console.log(results))
.catch(ex => console.log(ex));
</script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.