egil / Htmxor

Supercharges Blazor static server side rendering (SSR) by seamlessly integrating the Htmx.org frontend library.
MIT License
130 stars 13 forks source link

NavigationManager.NavigateTo does not trigger NavigationException as expected, causing redirection failure when using Htmxor #64

Open CoCoNuTeK opened 2 months ago

CoCoNuTeK commented 2 months ago

Whenever I attempt to call RedirectManager.RedirectTo, I expect NavigationManager.NavigateTo(uri) to throw a NavigationException, which should be handled by the framework to facilitate the redirection to the specified URL.

However, instead of triggering the expected NavigationException, what gets triggered is an HtmxorNavigationException. This leads to an Object reference not set to an instance of an object error, which ultimately prevents the redirection from working properly when the Htmxor package is active.

Expected Behavior:

Actual Behavior:

Steps to Reproduce:

  1. Attempt to call RedirectManager.RedirectTo with Htmxor active.
  2. Observe that NavigationException is not triggered, and redirection fails due to HtmxorNavigationException.
egil commented 2 months ago

Thanks for the report. What is interesting is that HtmxorNavigationException inherits from NavigationException, so the behavior should be preserved, at least in my testing.

Can you provide a minimal runnable code sample that I can use to understand your scenario better?

CoCoNuTeK commented 2 months ago

Using your pizza example code, you can place RedirectManager.RedirectTo("/myorders"); inside the OnInitializedAsync method of Index.razor. Redirections don't seem to work with RedirectManager at all. My idea was to use HTMX redirection via HX-Redirect, which performs a client-side redirect to a new location. I'm unsure how to trigger that directly from the @code section, but it would likely provide a smoother experience, especially with Blazor SSR.

In contrast, Blazor's typical way to handle redirection is by throwing a NavigationManagerException, which the framework manages as a redirect. Is there any way to return custom headers from a Blazor component? While Razor pages handle HTML injection well with direct routing, I'm not sure how to modify the headers (e.g., to include an HTTP request header for the HX-Redirect).

egil commented 2 months ago

Using your pizza example code, you can place RedirectManager.RedirectTo("/myorders"); inside the OnInitializedAsync method of Index.razor. Redirections don't seem to work with RedirectManager at all.

I assume you are talking about the IdentityRedirectManager type? Looks like you are not the only one that experience this (https://github.com/dotnet/aspnetcore/issues/53996).

Anyway, Htmxor do the same thing as Blazor SSR, but it provides its own HtmxorNavigationManager (registrered by default) that throws HtmxorNavigationException that has additional data included which allows Hrmxor to redirect in the expected way.

So, the behavior should be identical to Blazor SSR, but do check if your code is using HtmxorNavigationManager.

The key part of the HtmxorRenderer that handles this is here:

https://github.com/egil/Htmxor/blob/d8e09e4da17ab4c74fbea95d8e995137785c8395/src/Htmxor/Rendering/HtmxorRenderer.Rendering.cs#L34-L102

I'm unsure how to trigger that directly from the @code section, but it would likely provide a smoother experience, especially with Blazor SSR.

You need access to the HtmxContext object that is created with each request, and is available as an injected service as well as a cascading value, Then, e.g. during OnInitialized, you can set HX response headers that will cause htmx to do client side stuff, e.g.:

// note: this is untested code
[CascadingParameter]
public HtmxContext Context { get; set; }

protected override void OnInitialized()
{
  Context.Response.Location("/url/to/other/page");
}

Btw. I have been away from this project for a few months, simply have not had the time, so some of this is based on vague memories and may not all be correct.