How can I open a new window without using JS
Asked Answered
L

7

36

In blazor i use NavigationManager.NavigateTo(url)in order to change window location, but how can I use it to open a new tab with a specified URL without having to invoke JS on OnAfterRenderAsync()

Lauer answered 7/7, 2020 at 6:3 Comment(0)
M
51

As of 1 of June 2022 there is no way of currently doing it directly with pure Blazor, you'll need to use JSInterop. Luckily this is easy enough to do. At the top of your .razor file add

@inject IJSRuntime JSRuntime;

And then use it like so

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

Note that the IJSRuntime interface itself only provides a InvokeAsync<TValue> method, the JSRuntimeExtensions class provides an extension method for IJSRuntime to directly invoke a method without a return value: InvokeVoidAsync

await JSRuntime.InvokeVoidAsync("open", url, "_blank");
Milkandwater answered 7/7, 2020 at 6:9 Comment(11)
Rollback to revision 1 as IJSRuntime does not have InvokeVoidAsyncMilkandwater
do I have to use in in OnAfterRender or can I run it from an onclick?Lauer
also the Js opens a new window only when i click somewhere inside the page, it does not execute automatically in method: await JsRuntime.InvokeAsync<object>("open", new object[] { "https://google.com/", "_blank" });Lauer
You should be able to call the code anywhere, but I'd recommend OnClick because else most browsers will block it as it may not be a user initiated eventMilkandwater
This does work, however eventually a TaskCanceledException will be thrown. I guess I can catch and ignore it, I am just wondering if there is a way to prevent the exception?Unreal
Is there a way to prevent TaskCanceledException?Hypomania
@SureshTadisetty I honestly have no idea, I haven't ever really done anything with Blazor, and even the little bit I did is now almost a year agoMilkandwater
Note that using InvokeVoidAsync avoids the TaskCanceledException; answer has been updatedInvolucre
@Involucre problem is that InvokeVoidAsync isn't specified on the IJSRuntime interface. I updated the answer to add that part. Plus, I don't see how InvokeVoidAsync avoids the exception, even after looking at the source code. Maybe it was fixed internally somewhere between I originally posted this answer and todayMilkandwater
@Milkandwater I do see InvokeVoidAsync as an extension method. Using it does prevent the TaskCanceledexception that was occurring about a minute after invocation, in my case at least. learn.microsoft.com/en-us/dotnet/api/…Involucre
@Involucre hmm, funny. I'll try and reproduce it locally and see why the extension method doesn't throw an exceptionMilkandwater
D
25

Formerly, this code worked.

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

At present, it now results in an uncaught exception:

> "TypeError: Converting circular structure to JSON

I found this behavior explained here (https://github.com/dotnet/aspnetcore/issues/16632):

This is because window.open returns a WindowProxy object (see https://developer.mozilla.org/en-US/docs/Web/API/Window/open). WindowProxy is not JSON-serializable, so can't be used as a return value to .NET code.

To fix this, don't call window.open directly, but instead call a JS function of your own that either returns nothing or returns something that is JSON-serializable.

Per the above recommendation, I added the following to index.html:

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

And modified my C# code-behind call to pass the window arguments:

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

Effectively we now avoid the issue by discarding the WindowProxy object returned by window.open, which was formerly returned to InvokeVoidAsync and .NET was attempting to (unsuccessfully) process.

Disparate answered 29/9, 2020 at 12:32 Comment(0)
S
11

You will get TypeError: Converting circular structure to JSON when using

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

or

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

This is because window.open returns a WindowProxy object (see https://developer.mozilla.org/en-US/docs/Web/API/Window/open). WindowProxy is not JSON-serializable, so can't be used as a return value to .NET code.

Taken from see here.

To get around this w/o using a javascript function, I use the following

await JSRuntime.InvokeVoidAsync("eval", $"let _discard_ = open(`{url}`, `_blank`)");
Simms answered 14/6, 2021 at 17:35 Comment(1)
Thank you, it really helped though not sure if currently eval can be used (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)Seventieth
R
4

Just use a regular link

<a href="@Url" target="_blank">@UrlDescription</a>
Rocco answered 7/7, 2020 at 13:19 Comment(9)
i want to open a window from a C# function in blazor @onclick event handlerLauer
Ok, what are you going to click on?Rocco
it works now @onclick="@(async ()=>await JSRuntime.InvokeAsync<object>("open", url, "_blank"))"Lauer
I'm confused by your design. You want them to click something but it can't be an element designed to do what you ask? Anyway, glad you are happy.Rocco
well a link is generated by c# within that function and then user is redirected to that urlLauer
Right, but you know the URL, you can emit that into the html and avoid any extra code just to navigate?Rocco
C# generates a link in @onclick, which is a "pay" button, the link redirects to the payout page with certain tokens, now I could generate the link, then ask the user to click on another element, but that would require 2 clicks instead of 1Lauer
Both InvokeAsync and InvokeVoidAsync will "work"/execute, but after opening the window will throw an Uncaught TypeError: Converting circular structure to JSON.Disparate
This should be the accepted answer. There's no need to use JS at all.Arnaud
E
3

Per the latest change in API here is the working solution where you can add any custom object attribute to the URL as shown below:

    /// <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
        }
    }
Eagre answered 23/6, 2021 at 21:41 Comment(0)
C
2

The best way is to:

<NavLink class="MyCssClass" 
         target="_blank" 
         href="https://www.google.com">
         Google in new window
</NavLink>
Cohe answered 19/6, 2022 at 12:38 Comment(1)
(I know, hijacking the thread 🙈) Tip: If you are using this example (you are ok with regular anchor tag) you can get the full URL from the NavigationManger.ToAbsoluteUri, - f ex in a list of items like this : <a href="@NavigationManager.ToAbsoluteUri(yourItem.UrlRelativePath)" ></a>Litterbug
C
1

.net 7+

you have to use await JSRuntime.InvokeVoidAsync("open", url, "_blank");

if you use JSRuntime.InvokeAsync<object> it results in an error:

blazor.server.js:1  [2024-04-16T07:48:04.751Z] Error: Microsoft.JSInterop.JSException: Converting circular structure to JSON
    --> starting at object with constructor 'Window'
    --- property 'window' closes the circle
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Window'
    --- property 'window' closes the circle
    at JSON.stringify (<anonymous>)
    at D (https://localhost:44326/_framework/blazor.server.js:1:5345)
    at https://localhost:44326/_framework/blazor.server.js:1:3541
   at Microsoft.JSInterop.JSRuntime.InvokeAsync[TValue](Int64 targetInstanceId, String identifier, Object[] args)
Cutout answered 16/4 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.