如何在不使用JS的情况下打开新窗口

30
在 Blazor 中,我使用 NavigationManager.NavigateTo(url) 来更改窗口位置,但如何在不需要在 OnAfterRenderAsync() 上调用 JS 的情况下使用它来打开一个指定 URL 的新标签页呢?
6个回答

43

截至2022年6月1日,目前没有使用纯Blazor直接处理它的方法,您需要使用JSInterop。幸运的是,这很容易实现。在您的.razor文件顶部添加以下内容:

@inject IJSRuntime JSRuntime;

然后像这样使用它

await JSRuntime.InvokeAsync<object>("open", url, "_blank");

请注意,IJSRuntime 接口本身仅提供 InvokeAsync<TValue> 方法,而 JSRuntimeExtensions 类则提供了一个扩展方法,用于在不返回值的情况下直接调用方法:InvokeVoidAsync

await JSRuntime.InvokeVoidAsync("open", url, "_blank");

3
虽然这个方法可以运行,但最终会抛出一个TaskCanceledException异常。我猜我可以捕获并忽略它,但我想知道是否有办法避免这个异常? - StefanFFM
1
有没有一种方法可以防止TaskCanceledException? - Suresh Tadisetty
@SureshTadisetty,老实说我不知道,我从来没有真正使用过Blazor,即使我做了一点点,现在也已经快一年了。 - MindSwipe
请注意,使用InvokeVoidAsync可以避免TaskCanceledException异常;答案已更新。 - user3071284
@user3071284 的问题在于 InvokeVoidAsync 没有在 IJSRuntime 接口中指定。我更新了答案以添加该部分。此外,即使查看了源代码,我也不知道 InvokeVoidAsync 如何避免异常。也许在我最初发布这个答案和今天之间,在内部修复了这个问题。 - MindSwipe
显示剩余6条评论

23

以前这段代码是可以工作的。

await _jsRuntime.InvokeVoidAsync("open", new object[2] { url, "_blank" });

目前,它会导致未捕获的异常:

> "TypeError: Converting circular structure to JSON

我在此找到了相关解释(https://github.com/dotnet/aspnetcore/issues/16632):

这是因为window.open返回一个WindowProxy对象(请参阅https://developer.mozilla.org/en-US/docs/Web/API/Window/open)。WindowProxy不能被JSON序列化,因此不能用作.NET代码的返回值。

要解决此问题,请不要直接调用window.open,而是调用自己的JS函数,该函数要么不返回任何内容,要么返回可JSON序列化的内容。

根据上述建议,我将以下内容添加到index.html中:

<script>
    window.blazorOpen = (args) => {
        window.open(args);
    };
</script>

我修改了C#的代码,调用时传递窗口参数:

await _jsRuntime.InvokeVoidAsync("blazorOpen", new object[2] { url, "_blank" });

现在我们通过丢弃由window.open返回的WindowProxy对象来有效避免此问题,该对象曾经被返回给InvokeVoidAsync和.NET试图(但未能成功)处理。


10
当使用时,会出现“TypeError:将循环结构转换为JSON”的错误提示。
await _jsRuntime.InvokeVoidAsync("open", new object[2] { url, "_blank" });

或者
await _jsRuntime.InvokeAsync<object>("open", url, "_blank");

这是由于window.open返回一个WindowProxy对象(请参见https://developer.mozilla.org/en-US/docs/Web/API/Window/open)。WindowProxy不可JSON序列化,因此无法作为.NET代码的返回值。
引自这里。要避免使用javascript函数,可以使用以下方法解决该问题。
await JSRuntime.InvokeVoidAsync("eval", $"let _discard_ = open(`{url}`, `_blank`)");

谢谢,这确实有帮助,尽管不确定当前是否可以使用eval(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!) - Guru_07

4

只需使用常规链接即可

<a href="@Url" target="_blank">@UrlDescription</a>

现在它可以工作了 @onclick="@(async ()=>await JSRuntime.InvokeAsync<object>("open", url, "_blank"))" - Flexy
我对你的设计感到困惑。你希望他们点击某个东西,但它不能是一个被设计成执行你要求的功能的元素?不管怎样,很高兴你感到满意。 - Mister Magoo
在该函数内部,C# 生成一个链接,然后将用户重定向到该 URL。 - Flexy
无论是InvokeAsync还是InvokeVoidAsync都会“工作”/执行,但在打开窗口后会抛出一个未捕获的TypeError:将循环结构转换为JSON。 - Mike
这应该是被接受的答案。根本不需要使用JS。 - bobt
显示剩余4条评论

3

根据最新的API更改,以下是可将任何自定义对象属性添加到URL的工作解决方案:

    /// <summary>
    /// Show Link
    /// </summary>
    /// <returns></returns>
    private async Task ShowLink()
    {
        if (this.selectedItem != null)
        {
            string url = $"https://example.com/sites/" + this.selectedItem.Id + "/SitePages/Reports/somepage.aspx";
            //NavigationManager.NavigateTo(url, false);//opens the new page on same browser tab
            string[] values = { url, "_blank" };
            CancellationToken token = new CancellationToken(false);
            await JSRuntime.InvokeAsync<object>("open", token, values);//opens the link in new browser tab
        }
    }

2
最好的方法是:

以下是具体步骤:

<NavLink class="MyCssClass" 
         target="_blank" 
         href="https://www.google.com">
         Google in new window
</NavLink>

(我知道,抢了这个线程)提示:如果您正在使用此示例(您可以使用常规锚标记),则可以从NavigationManger.ToAbsoluteUri获取完整的URL,例如在像这样的项目列表中:<a href="@NavigationManager.ToAbsoluteUri(yourItem.UrlRelativePath)" ></a> - JimiSweden

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