Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blazor prerender + WASM - form submission handler execution #59193

Closed
Xymanek opened this issue Nov 27, 2024 · 1 comment
Closed

Blazor prerender + WASM - form submission handler execution #59193

Xymanek opened this issue Nov 27, 2024 · 1 comment
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Duplicate Resolved as a duplicate of another issue Status: Resolved

Comments

@Xymanek
Copy link

Xymanek commented Nov 27, 2024

Example scenario:

// TestPrerenderWasm.razor

@page "/TestPrerenderWasm"
@using BlazorApp1.Client.Components
<h3>TestPrerenderWasm</h3>

<WasmForm />
@rendermode InteractiveWebAssembly

<h3>WasmForm</h3>

<EditForm Model="_model" OnSubmit="HandleSubmit" FormName="WasmForm">
    <InputText @bind-Value="_model.Str1"></InputText>
    
    <button type="submit">Submit</button>
</EditForm>

@code {
    private class ModelClass
    {
        public string Str1 { get; set; } = "";
    }

    private ModelClass _model = new();

    private void HandleSubmit(EditContext context)
    {
        Console.WriteLine("InteractiveWebAssembly submit");
    }
}

Question of the day - where does InteractiveWebAssembly submit get printed? The answer is actually more complex than one might think. It depends on whether the WASM application has loaded and initialized* yet or not.

  • If the WASM application has been initialized, then the message is printed to the browser console
  • If the WASM application has not been initialized (e.g. slow connection), the message is printed to server (asp.net core process) console (as part of static render).
  • If the mode was InteractiveAuto and the WASM has not been loaded yet, the message would've been once again printed to server console, but this time the code has access to interactivity APIs.

* I am unsure of the exact switchover point, though it's not really important in this context.

The problem

Some logic requires running exclusively on server (e.g. database access). As it stands now, there is no straightforward way to force that to happen. Of course targeting WASM means that the developer should aim to interact with server via IHttpClient, but the mere fact that there is a possibility for static server rendering to handle the form submission forces this consideration.

Of course one could register an IHttpClient on the server that would point to localhost, but then "ambient" information such as user authentication, source IP, etc. is lost.

Currently this dilemma can be avoided using 2 approaches, both sub optimal:

  1. Prevent form interaction until WASM is loaded (e.g. using the new RendererInfo), and then only rely on IHttpClient - worse UX, negates benefit from SSR. Also does not really make sense in context of InteractiveAuto where the application can be in WS-based interactivity
  2. Manually branch logic between server and WASM logic, e.g. via registering different service implementation. Huge overhead for the developer.

A potential "simple" solution

Introduce OnSubmitServer callback that would automatically send a request to the server even in WASM mode. This will enable something similar to the Remix framework, where a form is handled by either full page load or, if the JS is loaded, the form submission is intercepted but still sent to the server, AJAX-style, ensuring that interactivity remains (e.g. live validation).

More elaborate potential solution

While form submission is the obvious "problem" here, running a piece of logic specifically on server is a common need. It could be valuable to introduce some kind of generic RPC system, e.g.

@rendermode InteractiveWebAssembly

<button @click="HandleClick">Do the thing!</button>

@code {
    private class ModelClass
    {
        public string Str1 { get; set; } = "";
    }

    private ModelClass _model = new();

    private async Task HandleClick()
    {
      int result = await RunOnServer(() => {
        Console.WriteLine("Working with database....");

        return 42;
      })
    }
}

RunOnServer would branch depending on the current render mode:

  • Server (static or interactive) - simply invoke the callback.
  • WASM - do a HTTP POST that would then be processed in same approach as the SSR form handling is (must pass all authorization checks, execute the component).

Of course this will come with a lot of gotchas - the payloads (in both directions) will need to be serialized, component state will be lost, etc.

Related issues

#55307 and #51468 touch on potentially related topics

SDK

dotnet --list-sdks
8.0.403 [C:\Program Files\dotnet\sdk]

P.S. If there is an easier existing solution please do let me know 😄

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Nov 27, 2024
@javiercn
Copy link
Member

@Xymanek thanks for contacting us.

This is a dupe of #53129

@javiercn javiercn closed this as not planned Won't fix, can't repro, duplicate, stale Nov 27, 2024
@javiercn javiercn added the ✔️ Resolution: Duplicate Resolved as a duplicate of another issue label Nov 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Duplicate Resolved as a duplicate of another issue Status: Resolved
Projects
None yet
Development

No branches or pull requests

2 participants