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()
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");
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.
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`)");
Just use a regular link
#UrlDescription
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
}
}
The best way is to:
<NavLink class="MyCssClass"
target="_blank"
href="https://www.google.com">
Google in new window
</NavLink>
Related
I created an ASP.NET Core api controller which return a FileStreamResult object. (I can change the type of result if needed)
Here is the code of the Get function:
[HttpGet("[action]/{p_gInspectionID}/{p_nIndex}")]
public async Task<FileStreamResult> GetInspectionPictureToDownload(Guid p_gInspectionID, int p_nIndex)
{
var l_strFilePath = await GetPictureFilePathAsync(p_gInspectionID, p_nIndex);
using (var l_sReader = System.IO.File.OpenRead(l_strFilePath))
{
return (File(l_sReader, "image/jpeg"));
}
}
Now I need to consume this result in the Blazor (Webassembly) client side application.
My goal is to have a button to launch the download of the file in the browser when user clicks on it.
This should launch download functionnality of the browser.
Is it possible to achieve this in Blazor client application ?
I was trying to do the same thing, but my API was authorized, so after reading this article I end up downloading the bytes in the web assembly application and use JavaScript to download the file from the bytes.
function downloadFromByteArray(options: {
byteArray: string,
fileName: string,
contentType: string
}): void {
// Convert base64 string to numbers array.
const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));
// Convert numbers array to Uint8Array object.
const uint8Array = new Uint8Array(numArray);
// Wrap it by Blob object.
const blob = new Blob([uint8Array], { type: options.contentType });
// Create "object URL" that is linked to the Blob object.
const url = URL.createObjectURL(blob);
// Invoke download helper function that implemented in
// the earlier section of this article.
downloadFromUrl({ url: url, fileName: options.fileName });
// At last, release unused resources.
URL.revokeObjectURL(url);
}
here is how I solved the problem. In fact the solution was really straightforward. Thank you #Data Juggler for pointing me in the right direction.
My Blazor solution holds two project:
the server side API (Blazor server)
the client side (Blazor WebAssembly).
Here is the code for the server side:
[AllowAnonymous]
[HttpGet("[action]/{p_strPictureFilePath}")]
public IActionResult GetInspectionPicture(string p_strPictureFilePath)
{
var l_sReader = System.IO.File.OpenRead(p_strPictureFilePath);
return (File(l_sReader, "application/octet-stream", Path.GetFileName(p_strPictureFilePath)));
}
... and the code on the client side:
Added this script in client-shared.js file:
window.downloadInspectionPicture = function (p_strServerFilePath)
{
var link = document.createElement('a');
link.href = 'api/Data/GetInspectionPicture/' + this.encodeURIComponent(p_strServerFilePath);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
of course, a reference to that file is present in index.html:
<script src="client-shared.js"></script>
And finally, added a link in the razor file, and invoke script when link is clicked:
Download
#code
{
[Inject]
IJSRuntime ThisJSRuntime { get; set; }
private async Task DownloadPictureAsync()
{
await ThisJSRuntime.InvokeVoidAsync("downloadInspectionPicture", ServerFilePath);
}
}
Hope my answer is clear and can be useful to someone
I don't know if in fact your way is possible, but what I do is similar for my site https://pixeldatabase.net .
The user clicks the download button, and I show a link like this:
public async void Download()
{
// Set the ImagePath
DownloadLink = ImagePath;
}
Then on the page, I just show a Download link conditionallay:
#if (HasDownloadLink)
{
<a class="downloadlink" download="#FileName" href="#DownloadLink"
target="_blank">Download</a>
}
I’m trying to write a Blazor component that uses google maps via JSInterop. I am trying to call the “DirectionsService” class and “route” method.DirectionsService Route Method
Here is my my local method:
public async Task<DirectionResponse> Route(DirectionsRequest request)
{
var json = await _jsObjectRef.InvokeAsync<string>(
"google.maps.DirectionsService.route",
request);
var directionResponse = JsonConvert.DeserializeObject<DirectionResponse>(json);
return directionResponse;
}
This method has two parameters. One is a request object. The second is a callback function.
Method Definition
How can I accomplish this using the “InvokeAsync” method?
How do I include the callback function in order to receive the response properly?
You can't include a callback that calls directly into you .NET code.
However, you can pass a callback to another javascript function.
The javascript function is then able to call back to .NET with the standard syntax:
DotNet.invokeMethodAsync('YourApp', 'YourMethodAsync')
So... This is basically the conclusion I came up with. The code is not final, specially the reject portion but it essentially makes the call to the DirectionsService synchronous using async/await.
Here is the code:
let promise = new Promise((resolve, reject) => {
directionsService.route(dirRequest, (result, status) => {
if (status == 'OK') {
resolve(result);
}
else
{
reject(status);
}
});
});
//Wait for promise
let result = await promise;
return result;
This essentially allows me respond back to C# function that invoked this javascript function. Now the only thing left is for me to serialize the response back to the C# function, with the bits that I need.
Thanks Postlagerkarte for helping me out.
public static class HttpRequestHelper
{
public static string RequestBody()
{
var bodyStream = new StreamReader(HttpContext.Current.Request.InputStream);
bodyStream.BaseStream.Seek(0, SeekOrigin.Begin);
var bodyText = bodyStream.ReadToEnd();
return bodyText;
}
}
I plan to call this from ActionFilters to log incoming requests. Of course there could be multiple simultaneous requests.
Is this approach ok?
Is your question from the perspective of concurrency or ASP.NET Web API in general? Every request has its own context and you are okay with multiple requests going on in parallel. But here are two things for you to look at.
(1) Since you are using HttpContext, you are locking yourself to web hosting (IIS), which in many cases should be okay. But I would like you to be aware of this.
(2) Your code HttpRequestHelper.RequestBody() will work when called from an action filter, as you mentioned. However, if you try to call this from other places, say a message handler, this will not work. When I say this will not work, parameter binding that binds request body to action method parameter will not work. You will need to seek to the beginning once you are done. The reason it works from action filter is that binding would have already happened by the time action filter runs in the pipeline. This is another thing you might need to be aware of.
I've needed use InputStream of Http Request. I have a WebApp and IOS App that navigates to a aspx page, if the url request contains some parameters i read the information in database and if i not find any parameters in url request i read the request body and i work fine !
protected void Page_Load(object sender, EventArgs e)
{
try
{
if (string.IsNullOrEmpty(Request.QueryString["AdHoc"]) == false)
{
string v_AdHocParam = Request.QueryString["AdHoc"];
string [] v_ListParam = v_AdHocParam.Split(new char[] {','});
if (v_ListParam.Length < 2)
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(WS_DemandeIntervention));
WS_DemandeIntervention response = (WS_DemandeIntervention)jsonSerializer.ReadObject(Request.InputStream);
....
}
if (string.IsNullOrEmpty(Request.QueryString["IdBonDeCommande"])==false)
{
....
I want to write a little helper function that returns the site url.
Coming from PHP and Codeigniter, I'm very upset that I can't get it to work the way I want.
Here's what I'm trying:
#{
var urlHelper = new UrlHelper(Html.ViewContext.RequestContext);
var baseurl = urlHelper.Content("~");
}
<script>
function base_url(url) {
url = url || "";
return '#baseurl' + url;
}
</script>
I want to return the base url of my application, so I can make ajax calls without worrying about paths. Here's how I intend to use it:
// Development
base_url(); // http://localhost:50024
// Production
base_url("Custom/Path"); // http://site.com/Custom/Path
How can I do something like that?
EDIT
I want absolute paths because I have abstracted js objects that makes my ajax calls.
So suppose I have:
function MyController() {
// ... js code
return $resource('../MyController/:id');
}
// then
var my_ctrl = MyController();
my_ctrl.id = 1;
my_ctrl.get(); // GET: ../MyController/1
This works when my route is http://localhost:8080/MyController/Edit but will fail when is http://localhost:8080/MyController .
I managed to do it like this:
#{
var url = Request.Url;
var baseurl = url.GetLeftPart(UriPartial.Authority);
}
Thank you all!
Are you aware of #Url.Action("actionname") and #Url.RouteUrl("routename") ?
Both of these should do what you're describing.
Instead of manually creating your URL's, you can use #Url.Action() to construct your URLs.
<p>#Url.Action("Index", "Home")</p>
/Home/Index
<p>#Url.Action("Edit", "Person", new { id = 1 })</p>
/Person/Edit/1
<p>#Url.Action("Search", "Book", new { title = "Gone With The Wind" })</p>
/Book/Search?title="Gone+With+The+Wind"
Now the absolute best reason to go with this option is that #Url.Action automatically applies any vanity URL routes you have defined in your Global.asax file. DRY as the sub-saharan desert! :)
In your case, your can create a 'custom path' in two ways.
Option A)
<p>#Url.Action("Path", "Custom")</p>
/Custom/Path
Option B)
You can create a route using the Global.asax file. So your controller/action combo can be anything you want, and you can create a custom vanity route url - regardless of the controller/action combo.
The path is javascript path
var fileName = args.get_fileName();
lstImg.src = <%=GetListImageFilePath(fileName) %>
file name is error because it is javascript and not in .NET
how to put this argument in .NET Code
You'll need to use AJAX. One easy way to do it would be to use PageMethods. First, add a [WebMethod] attribute to your method:
[WebMethod]
protected static string GetListImageFilePath(string fileName)
{
This method must be static.
Then set EnablePageMethods="True" on your script manager. Then you can call your c# code from JavaScript like this:
var fileName = args.get_fileName();
PageMethods.GetListImageFilePath(fileName, function (path) {
lstImg.src = path;
});
You can't. The JavaScript runs on the client, and the asp.net code is on the server. You need to use some other way of communicating with the server eg: Ajax to a web service, a postback, etc
You simply can not do it because javascript is running at client side i.e on browser where as server code run at server. What you could do is change the your GetListImageFilePath function so that it returns the base URL for your image directory and then append the file name to create the image path.
var fileName = args.get_fileName();
lstImg.src = <%=GetListImageFilePath() %> + '/' + fileName;
For more information, like how server tags in Javascript are processed, I have answered a StackOverFlow thread here. Please have a look to clarify your doubt.
I think get_fileName() is server side function. So you can call it from the HTML directly.
Check these links
http://weblogs.asp.net/jalpeshpvadgama/archive/2012/01/07/asp-net-page-methods-with-parameters.aspx
http://stackoverflow.com/questions/7633557/asp-net-is-it-possible-to-call-methods-within-server-tag-using-eval
If you call the javascript function using RegisterStartupScript() or
RegisterClientScriptBlock() then these will be called in client side not in server side.
If you want to call the javascript function immediately in server side then declare an equivalent server side function.
add an ashx(http handler) in your website, then you can use lstImg.src = '/example.ashx?name=' + fileName.
public class ExampleHandler: IHttpHandler {
public void ProcessRequest (HttpContext context) {
var request = context.Request;
string fileName = (string)request.QueryString["name"];
// your logic
context.Response.Write(yourpath)
}
public bool IsReusable {
get {
return false;
}
}
}