Blazor监听JavaScript事件。

6

我有一个叫做Hello的JavaScript事件:

addEventListener('hello', function () {
  alert("event listener");
})

而在另一个 JavaScript 函数中,我触发了这个事件:

let event = new Event("hello", { bubbles: true });
document.dispatchEvent(event);

我现在想做的是在JavaScript函数中触发事件。 Blazor 应该监听事件,而不是JavaScript调用 Blazor 方法。 希望有人能够协助我。 谢谢, 此致,敬礼。

你使用的是哪个版本的.NET? - Mister Magoo
我正在使用 .Net 5.0。 - Teckniel
我之所以问这个问题,是因为.NET 6预览版2将自定义事件类型引入到Blazor中。在此之前,如果您想处理类似于您的自定义事件,您需要通过让您的自定义事件处理程序分派一个标准事件来“欺骗”blazor,例如在您的Blazor代码中有像<div id='foo' @onchange=HandleHello></div>这样的东西,然后在您的JS代码中,每当捕获到hello事件时,就向foo分派一个change事件。 - Mister Magoo
谢谢你的建议。我仍然可以更改到6版本prev 2。 我刚开始构建这个应用程序。 你有什么建议吗? - Teckniel
2
前往.NET 6预览版3,然后您可以执行以下操作:https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-2/#support-for-custom-event-arguments-in-blazor - Mister Magoo
2个回答

11

对于自定义事件,您需要手动利用 JavaScript/.NET 互操作性

使用 实例方法调用 方法:

  • 将 .NET 实例作为引用传递到 JavaScript:
    • 静态调用 DotNetObjectReference.Create。
    • 将实例包装在 DotNetObjectReference 实例中,并在 DotNetObjectReference 实例上调用 Create。请处理 DotNetObjectReference 对象(本节稍后提供示例)。
  • 使用 invokeMethodinvokeMethodAsync 函数在实例上调用 .NET 实例方法。.NET 实例也可以作为参数在从 JavaScript 调用其他 .NET 方法时进行调用。

示例

请注意,这是一个非常简化的示例。您可能想添加一些东西;从您的互操作类开始使用 IDisposable避免内存泄漏

  1. 在 C# 中,创建一个辅助类来管理互操作:
public class CustomEventHelper
{
    private readonly Func<EventArgs, Task> _callback;

    public CustomEventHelper(Func<EventArgs, Task> callback)
    {
        _callback = callback;
    }

    [JSInvokable]
    public Task OnCustomEvent(EventArgs args) => _callback(args);
}

public class CustomEventInterop : IDisposable
{
    private readonly IJSRuntime _jsRuntime;
    private DotNetObjectReference<CustomEventHelper> Reference;

    public CustomEventInterop(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    public ValueTask<string> SetupCustomEventCallback(Func<EventArgs, Task> callback)
    {
        Reference = DotNetObjectReference.Create(new ScrollEventHelper(callback));
        // addCustomEventListener will be a js function we create later
        return _jsRuntime.InvokeAsync<string>("addCustomEventListener", Reference);
    }

    public void Dispose()
    {
        Reference?.Dispose();
    }
}
  1. 在 Blazor 组件中,添加一个互操作类的实例(Interop),并添加一个本地方法作为回调 (HandleCustomEvent):
private CustomEventInterop Interop { get; set; }

protected override async Task OnAfterRenderAsync(bool firstRender) {
    if (!firstRender)
    {
        return;
    }
    Interop = new(JS);
    await Interop.SetupCustomEventCallback(args => HandleCustomEvent(args));
    HasRendered = true;
}

private void HandleCustomEvent(EventArgs args) {
    // ... handle custom event here
}
  1. 在JavaScript中,添加一个引用DotNetObjectReference并且可以调用C#互操作的方法:
function addCustomEventListener(dotNetObjectRef) {
  document.addEventListener('hello', (event) => {
    // Calls a method by name with the [JSInokable] attribute (above)
    dotNetObjectRef.invokeMethodAsync('OnCustomEvent')
  });
}

如果使用 TypeScript,您可能会查看此GitHub 问题


我只想订阅在JavaScript中创建的事件。 没有使用按钮或UI元素。 这就像观察者模式。 - Teckniel
1
除非您使用.NET 6预览版,否则JS互操作是处理真正自定义事件的最佳方式。mrmagoo的黑客技巧可能有效,但它仍然是一个黑客技巧。 - Connor Low
4
我们不要混淆,迁移到.NET 6 P2/3并使用自定义事件类型是“Blazor方式”,但仍处于预览阶段。其他任何方法都是变通方法。我个人不会使用这种方法,但肯定能够工作并且是一个可靠的方法。 - Mister Magoo
1
没错,这不是在Blazor中使用JS互操作实现事件监听器的唯一方法。在.NET 6发布之前,我认为JS互操作是解决问题的最强大的方式。它甚至可以扩展以接受来自js的可序列化负载,而您无法使用@onclick解决方法。如果您不需要任何花哨的东西,那么@mister magoo也为您提供了一个很好的解决方法。我相信当.NET 6发布时,会有更多吸引人的答案出现。 - Connor Low
1
即使是在 .Net6 发布后,我仍然无法让 .net6 的方式正常运行。 - JohnnyJP
显示剩余7条评论

5

对于任何想了解@Mister Magoo提出的解决方案的人来说,这不再是.NET 6的预览版本,并且有一些示例文档在这里

简而言之:

创建一个带有EventHandlerAttribute的C#类:

[EventHandler("oncityclicked", typeof(CustomSelectionCityEventArgs),
    enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}

public class CustomSelectionCityEventArgs: EventArgs
{
    public Guid Id { get; set; }
}

<script src="_framework/blazor.webview.js" autostart="false"></script> 之后将 JS 添加到 ./wwwroot/index.html 中:
<script>
    Blazor.registerCustomEventType('cityclicked', {
        createEventArgs: event => {
            return {
                id: event.detail
            };
        }
    });
</script>

在你的 Razor 中:

@code {
    private void HandleCityClicked(CustomSelectionCityEventArgs eventArgs)
    {
        Console.WriteLine("Bouep");
    }
}

<div id="CarteLeaflet" @oncityclicked="HandleCityClicked"></div>

最后,在JS中,您可以分派事件:

function OnMarkerClick(pId) {
    const event = new CustomEvent('cityclicked', {
        bubbles: true,
        detail: pId
    });
    document.getElementById('CarteLeaflet').dispatchEvent(event);
}

不要犯和我一样的错误,在C#中事件名称应该以"on"开头(JS: "cityclicked",C#: "oncityclicked")。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接