NavigationManager.NavigateTo in razor pages c# sometimes don't work - c#

i've a very simple question about asp.net core and razor pages.
Inside c# razor code I want to redirect to another route in certain cases.
If there's an async (awaiting) webservice call before, NavigationManager.NavigateTo doesn't work correctly, but no exception is thrown either.
async Task Cancel()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated){
// if there's some async webservice call action here,
// NavigationManager.NavigateTo does neither work nor an exception is thrown
NavigationManager.NavigateTo("/Project", true);
}
}
Do you have any idea what could be the cause?
What are the alternatives to NavigationManager.NavigateTo?
P.S. Please don't propose Response.Redirect in that way (because, that's not suitable for me).
var context = new Microsoft.AspNetCore.Http.HttpContextAccessor();
context.HttpContext.Response.Redirect("/Project", true)
Kind regards,
-he.

Bug is already fixed now.
The problem with NavigationManager.NavigateTo(...) occurred only, because I used a button inside a posting form in my Razor page. Replaced form tag with div and now everything works fine.
Kind regards,
-he
<div>
<p>
<label>Project name: #projectName</label>
</p>
<p>
<!-- [...] -->
<span class="btn-cancel">
<button class="btn-primary" #onclick="#(async () => await Cancel())">Cancel</button>
</span>
</p></div>
#code {
// ...
async Task Cancel()
{
// here're other async action calls
NavigationManager.NavigateTo("/Project", true);
}
}

Another possible solution is using <form #onsubmit="Cancel"> with a <button type="submit">. This preserves advantages of forms, for example the handling of the enter-key.

little late here, but thought to share my case here. For me, NavigationManager.NavigateTo() was not working for some of the target blazor components even though they were simple components with only HTML contents like below, without any C# code in it.
#page "/unauthorized"
<h3>You are not authorized.</h3>
I noticed that "Build Action" property of the components which are not working here aren't set as "Content". So once I made this property as "Content", it started working.

Related

Blazor Dynamic Custom Elements in application

I'm developing a webapp built in C# with Blazor WASM that is Asp.Net hosted. I'm making a blazor component that through the use of a library already in production, will generate a HTML fragment (or full embed) that is then displayed in this way
...
<div>
#((MarkupString)document)
</div>
...
with document containing the markup generated by the library.
As long as we're doing it with static content all is fine and dandy, but now we need to have some input in there that will then be sent back to the server to execute some actions.
In a MarkupString there is no way to include <InputFile /> or <InputText /> components in such a way that they are shown in the fragment and I can read their contents, and I can find no way to actually interact with the standard HTML tags, especially regarding the file upload.
Moreover we'll probably soon need to have a specific image uploader with preview which would be a custom Blazor component and this led me to the CustomElements .NET 7 feature that looks like what I need for both problems.
However I couldn't find how to actually implement this in my app, and the documentation I found is still very partial in that way. Is there a way to do what I need?
EDIT: Managed to fix this partially, with Chen's answer. I still have trouble with the binding though, as the #bind-Value directive is not working with
Unhandled exception rendering component: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.
with this markup:
...
<custom-input-text #bind-value="$field1" name="$field1"></custom-input-text>
...
(the capital V in bind-Value becomes lowercase all by itself)
Am I doing something wrong again?
CustomElements should meet your requirements, you can create your own logic in Blazor components, and then use it in your application.
To use the component, you need to add the following JavaScript script references to your host app in this specific order.
<script src="_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
You also need to add the corresponding middleware:
app.UseBlazorFrameworkFiles();
And use app.UseWebAssemblyDebugging(); for debugging.
Then you need to register the corresponding component in the Blazor program:
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
Then you can call this component in your application, including passing parameters, etc.
<my-counter title="Khalid" increment-amount="2" />
Here is a complete example with detailed explanation, you can use it as a reference.
Helpful links:
Blazor Custom Elements.
Using .NET 7's Blazor Custom Elements to render dynamic content.
ASP.NET Core Razor components.
Hope this can help you.
Edit1:
It looks like you can't do two-way binding between Razor Page and Razor Component. The official document mentions that parameters can be passed through JavaScript properties, but it doesn't seem to be able to read the parameters.
For example:
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get parameter failed, it's undefined
var text = elem.incrementAmount;
//successfully set parameter
elem.incrementAmount = "test";
}
</script>
So I'm guessing that CustomElements only have writable properties in Razor Page.
Edit2:
I found that JQuery can be used to detect the value of the input box. When getting this value and performing certain operations, can it also achieve the same effect as two-way binding?
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get the value of input
var value = $("my-counter").find("input").val();
elem.incrementAmount = "test";
}
</script>

document.getElementById() in blazor

I need to change a text of a div, and I only have the id of the div. Without #bind (as I only have the id), how can I do that? I searched for a bit and some articles that may or may not be outdated stated that blazor doesn't allow access to DOM elements / API, but even so, I don't an access to the entire DOM element, I only want to be able to change the text inside a div or, if that's not possible, even the value property.
You can use ref attrubute. Source page Example:
#page "/"
#inject IJSRuntime JSRuntime
Enter your name
<input #ref=ReferenceToInputControl />
#code
{
ElementReference ReferenceToInputControl;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await JSRuntime.InvokeVoidAsync("BlazorUniversity.setFocus", ReferenceToInputControl);
}
}
First of all, try and use Blazor as it was intended. I've only come across a few scenarios where I need to call Javascript with an Id and manipulate the DOM.
If you still NEED to run Javascript then you can invoke the Javascript with a parameter which will be your id.
await jsRuntime.InvokeVoidAsync("javascriptMethodWithIdParameter", Id);
Then use this in your Javascript:
window.javascriptMethodWithIdParameter = function(id)
{
document.getElementById(id).innerHTML = 'This is the id: ' + id;
}
Here is my simple sample:
https://blazorfiddle.com/s/u6stbg0y
.NET 6 Example above
ASP.NET Core Blazor JavaScript interoperability (JS interop) official documentation

Compiler error when trying to RenderPartialAsync with a simple ASP.NET Core website

Summary
I am wanting to correct my use/calling of the RenderPartialAsync method while trying to render an ASP.NET Core partial view.
Details
I'm trying to render a partial view in my simple ASP.NET Core 5.0 website:
<td>
#await Html.RenderPartialAsync("_Listings", item.Listings);
</td>
When I try the following, I keep getting a compiler error:
Cannot implicitly convert type 'void' to 'object'
I don't understand what it's trying to complain about. I'm guessing that this method awaits a Task which doesn't return anything ... but it's wanting something to be returned?
I thought that the RenderPartialAsync method will render the contents to the response stream inside that method... not return some HTML which I then need to do something, with.
In this context, "renders" means the method writes its output using Writer.
What am I doing wrong? Is it the placement of my razor 'code snippet' start/end code block or something?
Also, I tried using the Html.RenderPartial (notice this is the SYNC method) and I got a warning about how this blocks AND also the same error message.
Update #1
What is the return type of item.Listings
Answer: IEnumerable<Listing>
So I had to change the code from:
<td>
#await Html.RenderPartialAsync("_Listings", item.Listings);
</td>
to
<td>
#{
await Html.RenderPartialAsync("_Listings", item.Listings);
}
</td>
I don't understand why but this now works.
My guess is that #await <stuff> is TWO commands, not one .. so it needs to get handled differently. While #{ stuff } is a code block section.
But the proper answer should be to use Tag Helpers instead.
so this:
<td>
<partial name="_Listings" model="item.Listings" />
</td>

Page route prevents OnGet() receiving query argument

I'm porting a WebForms application to Razor Pages, and I'm using the following route to prevent breaking one of my URLs.
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Resources/CharClass", "/Resources/CharClass.aspx");
});
But using the code below, when I use the URL /Resources/CharClass.aspx?page=500, page is always null.
cshtml
#page "{page:int?}"
cshtml.cs
public void OnGet(int? page)
{
//
}
The argument seems to work okay for pages that aren't routed this way. How can I have both?
I suspect that the problem you have is with the name of the parameter - page. This seems to conflict with the Page property of the PageModel class, and nothing is bound during model binding. You can however access the query string value using traditional methods:
var page = Request.Query["page"];
EDIT: Just seen that this is partly covered in a reply to your other question on the issue: Razor page link ignores route argument
Try:
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AddPageRoute("/Resources/CharClass", "/Resources/CharClass.aspx/{page?}");
});

ASP.Net Core 2 RedirectToPage does not redirect when used with DropzoneJS

I'm using ASP.Net Core 2 Razorpages with dropzonejs to have a dragndrop fileupload on my index site after the user drops a file I want to redirect to my LoadingPage.
However I'm having problems with the page being redirected. Strangely the OnGet() method of the page I want to redirect to is being called, but the site in the browser never gets updated. Other redirects work just fine, so I suspect the issue to be something with dropzonejs.
Well, here is my code:
index.cshtml
<div id="fileuploadcontainer">
<form method="post"
class="dropzone"
id="music-dropzone"
asp-page="./Index">
</form>
<br />
</div>
index.cshtml.cs
public async Task<IActionResult> OnPostAsync(IFormFile file)
{
if (!ModelState.IsValid)
{
return Page();
}
[...]//this here is my file uploading, but the problem still persits even when removed
return RedirectToPage("/LoadingPage/Loading");
}
Loading.cshtml.cs
public async Task<IActionResult> OnGet()
{
[...]//this part here gets called just fine, but the site doesnt redirect, even when it is minfied to only return Page() :(
return Page();
}
I also already tried to remove the async modifiers, but to no avail...
Edit - Workaround:
I have tried any possible combination and solution I can think of but my problem still persists. However I found a nice workaround, you can use the events of dropzonejs and then redirect the page using JS. The code for that would be:
<script>
Dropzone.options.[yourDropzoneElementId] = {
maxFilesize: 10, // Mb
init: function () {
// Set up any event handlers
this.on('success', function () {
window.location.replace("/LoadingPage/Loading");
});
}
};
</script>
I appreciate any help, thank you!

Categories