通过调用 C# 代码的 JS 更新 Blazor
Updating Blazor via JS that calls C# code
我正在尝试在 js
.Js
生成的事件上更新我的 Blazor
页面,使用互操作调用事件上的 C# 代码,我需要以某种方式调用 this.StateHasChanged
在 c# 回调中:
JS
window.methods = {
timeout: null,
setTimeout: function () {
timeout = window.setInterval(async function () {
console.log("From js timeout occured ,sending to .net");
await DotNet.invokeMethodAsync('[assembly name]', "TimeoutCallback", Date.now().toString());
}, 2000);
},
clearTimeout: function () {
if (this.timeout == null || this.timeout == undefined) {
return;
}
console.log("Clearing timeout");
window.clearTimeout(timeout);
}
}
C#
@inherits StuffBase
@page "/test"
Current value: @val
<button onclick="@(async()=>await StartTimeout())">Start timeout </button>
<button onclick="@(async()=>await ClearTimeout())">Clear timeout</button>
public class StuffBase : BlazorComponent {
protected static string val { get; set; }
protected async Task StartTimeout() {
await JSMethods.SetTimeout();
}
[JSInvokable("TimeoutCallback")] //gets called by js on event !
public static async Task TimeoutCallback(string data) {
val = data;
Console.WriteLine("From callback ,received:" + data); //gets updated
await Task.Delay(1000);
//how can i call this.StateHasChanged
}
protected async Task ClearInterval() {
await JSMethods.ClearInterval();
}
}
互操作
public class JSMethods {
public static async Task SetTimeout() {
await JSRuntime.Current.InvokeAsync<string>("methods.setTimeout");
}
public static async Task ClearTimeout() {
await JSRuntime.Current.InvokeAsync<string>("methods.clearTimeout");
}
}
正如您首先看到的,我从 c#
-> setTimeout
调用,在 js 中将超时附加到其处理程序。
发生的事情是我设法从 js
调用 TimeoutCallback
但是在 console
中我更新了我的值,我不知何故需要通知 UI
更新自己.
我如何实现这一点,因为从 js
调用的所有 .NET
方法都具有 (根据文档)static
?
那么如果它们是静态的呢...这是否意味着您不能调用 StateHasChanged 方法。只需在 TimeoutCallback 方法的末尾键入:StateHasChanged();
无论如何,如果这是真的,并且您不能从 TimeoutCallback 方法调用 StateHasChanged 方法,您可以从 TimeoutCallback 方法调用事件处理程序,如下所示:TimeoutCallbackLoaded?.Invoke(data);
然后从那里调用 StateHasChanged();
编辑:好的,我现在注意到您已经定义了一个名为 JSMethods 的 class,其中包含两个调用 SetTimeout 和 ClearTimeout 的方法。在那种情况下,更好的设计是在 class 中也定义 TimeoutCallback。毕竟都是相关的,对吧?然后如上所述定义一个事件处理程序,通知订阅者(您的 UI 代码)事件的发生,将 JSIterop 返回的数据传递给它,并在 [=22= 中处理事件的方法中]代码,可以调用StateHasChanged();
由于用户@Isaac 的建议添加事件,并从其回调中调用 StateHasChanged
,我已经解决了它:
public class StuffBase : BlazorComponent {
protected static string val { get; set; }
protected delegate void OnTimeout(string data);
private static event OnTimeout OnTimeOutOccured;
protected override void OnInit() {
OnTimeOutOccured += x => {
this.StateHasChanged();
};
}
protected async Task StartTimeout() {
await JSMethods.SetTimeout();
}
[JSInvokable("TimeoutCallback")]
public static async Task TimeoutCallback(string data) {
val = data;
Console.WriteLine("From callback ,received:" + data);
await Task.Delay(1000);
OnTimeOutOccured?.Invoke(data);
}
protected async Task ClearTimeout() {
await JSMethods.ClearTimeout();
}
}
我认为将 C# 实例传递给 JS 并让您的 JS 回调到该 C# 实例是一个更好的选择。
StuffBase.cs
public class StuffBase : ComponentBase
{
protected static string val { get; set; }
protected async Task StartTimeout()
{
await JSRuntime.Current.InvokeAsync<string>("methods.setTimeout", new DotNetObjectRef(this));
}
[JSInvokable("TimeoutCallback")] //gets called by js on event !
public async Task TimeoutCallback(string data)
{
val = data;
Console.WriteLine("From callback ,received:" + data); //gets updated
await Task.Delay(1000);
StateHasChanged();
}
protected async Task ClearTimeout()
{
await JSMethods.ClearTimeout();
}
}
JS
window.methods = {
timeout: null,
setTimeout: function (stuffBaseInstance) {
timeout = window.setInterval(async function () {
console.log("From js timeout occured ,sending to .net");
await stuffBaseInstance.invokeMethodAsync('TimeoutCallback', Date.now().toString());
}, 2000);
},
clearTimeout: function () {
if (this.timeout == null || this.timeout == undefined) {
return;
}
console.log("Clearing timeout");
window.clearTimeout(timeout);
}
}
在对上述答案进行了多次研究之后,我无法让它们在 core 3.1 和 Blazor web assembly 中工作。对于客户端 blazor,JSInvokable 仅适用于静态方法。所以我的解决方案是让 javascript 在 index.html 中单击剃须刀页面中定义的隐藏按钮:
index.html
<script language="javascript">
window.resetjs = function () {
document.getElementById('jspromptbutton').click();
}
</script>
</head>
<body>
<button type="button" class="btn btn-primary"
onclick="resetjs()">
fire prompt
</button>
然后在 JSInteropRazor 中,添加一个隐藏按钮来触发非静态方法来触发 statehaschanged,或者在我的例子中刷新 razor 页面中的数据。
JSInterop.razor
<button type="button" id="jspromptbutton" style="display:none;" @onclick="TriggerNetInstanceMethod">
Fire statehaschanged and refreshAPI
</button>
@code {
public async Task TriggerNetInstanceMethod()
{
this.StateHasChanged();
string url = "http://myapi.com";
var newApiData = await Http.GetJsonAsync<Tuple<string, string>>(url);
}
}
有点麻烦,但我会一直坚持下去,直到找到更好的解决方案..
这样使用:
private static App? _app;
public App()
{
_app = this;
}
我在App.razor
用过,因为组件可能不工作,通过这种方式可以级联到应用程序,然后在所有组件中使用。
然后像这样使用:
[JSInvokable]
public static void TimeoutCallback(string data)
{
...
_app?.StateHasChanged();
}
我正在尝试在 js
.Js
生成的事件上更新我的 Blazor
页面,使用互操作调用事件上的 C# 代码,我需要以某种方式调用 this.StateHasChanged
在 c# 回调中:
JS
window.methods = {
timeout: null,
setTimeout: function () {
timeout = window.setInterval(async function () {
console.log("From js timeout occured ,sending to .net");
await DotNet.invokeMethodAsync('[assembly name]', "TimeoutCallback", Date.now().toString());
}, 2000);
},
clearTimeout: function () {
if (this.timeout == null || this.timeout == undefined) {
return;
}
console.log("Clearing timeout");
window.clearTimeout(timeout);
}
}
C#
@inherits StuffBase
@page "/test"
Current value: @val
<button onclick="@(async()=>await StartTimeout())">Start timeout </button>
<button onclick="@(async()=>await ClearTimeout())">Clear timeout</button>
public class StuffBase : BlazorComponent {
protected static string val { get; set; }
protected async Task StartTimeout() {
await JSMethods.SetTimeout();
}
[JSInvokable("TimeoutCallback")] //gets called by js on event !
public static async Task TimeoutCallback(string data) {
val = data;
Console.WriteLine("From callback ,received:" + data); //gets updated
await Task.Delay(1000);
//how can i call this.StateHasChanged
}
protected async Task ClearInterval() {
await JSMethods.ClearInterval();
}
}
互操作
public class JSMethods {
public static async Task SetTimeout() {
await JSRuntime.Current.InvokeAsync<string>("methods.setTimeout");
}
public static async Task ClearTimeout() {
await JSRuntime.Current.InvokeAsync<string>("methods.clearTimeout");
}
}
正如您首先看到的,我从 c#
-> setTimeout
调用,在 js 中将超时附加到其处理程序。
发生的事情是我设法从 js
调用 TimeoutCallback
但是在 console
中我更新了我的值,我不知何故需要通知 UI
更新自己.
我如何实现这一点,因为从 js
调用的所有 .NET
方法都具有 (根据文档)static
?
那么如果它们是静态的呢...这是否意味着您不能调用 StateHasChanged 方法。只需在 TimeoutCallback 方法的末尾键入:StateHasChanged();
无论如何,如果这是真的,并且您不能从 TimeoutCallback 方法调用 StateHasChanged 方法,您可以从 TimeoutCallback 方法调用事件处理程序,如下所示:TimeoutCallbackLoaded?.Invoke(data);
然后从那里调用 StateHasChanged();
编辑:好的,我现在注意到您已经定义了一个名为 JSMethods 的 class,其中包含两个调用 SetTimeout 和 ClearTimeout 的方法。在那种情况下,更好的设计是在 class 中也定义 TimeoutCallback。毕竟都是相关的,对吧?然后如上所述定义一个事件处理程序,通知订阅者(您的 UI 代码)事件的发生,将 JSIterop 返回的数据传递给它,并在 [=22= 中处理事件的方法中]代码,可以调用StateHasChanged();
由于用户@Isaac 的建议添加事件,并从其回调中调用 StateHasChanged
,我已经解决了它:
public class StuffBase : BlazorComponent {
protected static string val { get; set; }
protected delegate void OnTimeout(string data);
private static event OnTimeout OnTimeOutOccured;
protected override void OnInit() {
OnTimeOutOccured += x => {
this.StateHasChanged();
};
}
protected async Task StartTimeout() {
await JSMethods.SetTimeout();
}
[JSInvokable("TimeoutCallback")]
public static async Task TimeoutCallback(string data) {
val = data;
Console.WriteLine("From callback ,received:" + data);
await Task.Delay(1000);
OnTimeOutOccured?.Invoke(data);
}
protected async Task ClearTimeout() {
await JSMethods.ClearTimeout();
}
}
我认为将 C# 实例传递给 JS 并让您的 JS 回调到该 C# 实例是一个更好的选择。
StuffBase.cs
public class StuffBase : ComponentBase
{
protected static string val { get; set; }
protected async Task StartTimeout()
{
await JSRuntime.Current.InvokeAsync<string>("methods.setTimeout", new DotNetObjectRef(this));
}
[JSInvokable("TimeoutCallback")] //gets called by js on event !
public async Task TimeoutCallback(string data)
{
val = data;
Console.WriteLine("From callback ,received:" + data); //gets updated
await Task.Delay(1000);
StateHasChanged();
}
protected async Task ClearTimeout()
{
await JSMethods.ClearTimeout();
}
}
JS
window.methods = {
timeout: null,
setTimeout: function (stuffBaseInstance) {
timeout = window.setInterval(async function () {
console.log("From js timeout occured ,sending to .net");
await stuffBaseInstance.invokeMethodAsync('TimeoutCallback', Date.now().toString());
}, 2000);
},
clearTimeout: function () {
if (this.timeout == null || this.timeout == undefined) {
return;
}
console.log("Clearing timeout");
window.clearTimeout(timeout);
}
}
在对上述答案进行了多次研究之后,我无法让它们在 core 3.1 和 Blazor web assembly 中工作。对于客户端 blazor,JSInvokable 仅适用于静态方法。所以我的解决方案是让 javascript 在 index.html 中单击剃须刀页面中定义的隐藏按钮:
index.html
<script language="javascript">
window.resetjs = function () {
document.getElementById('jspromptbutton').click();
}
</script>
</head>
<body>
<button type="button" class="btn btn-primary"
onclick="resetjs()">
fire prompt
</button>
然后在 JSInteropRazor 中,添加一个隐藏按钮来触发非静态方法来触发 statehaschanged,或者在我的例子中刷新 razor 页面中的数据。
JSInterop.razor
<button type="button" id="jspromptbutton" style="display:none;" @onclick="TriggerNetInstanceMethod">
Fire statehaschanged and refreshAPI
</button>
@code {
public async Task TriggerNetInstanceMethod()
{
this.StateHasChanged();
string url = "http://myapi.com";
var newApiData = await Http.GetJsonAsync<Tuple<string, string>>(url);
}
}
有点麻烦,但我会一直坚持下去,直到找到更好的解决方案..
这样使用:
private static App? _app;
public App()
{
_app = this;
}
我在App.razor
用过,因为组件可能不工作,通过这种方式可以级联到应用程序,然后在所有组件中使用。
然后像这样使用:
[JSInvokable]
public static void TimeoutCallback(string data)
{
...
_app?.StateHasChanged();
}