-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
Add mechanism for interactively-rendered form to submit as HTTP request to SSR endpoint #53129
Comments
EditForm
submit POST reload page to handle server logic with HttpContext
after editing form in interactive mode.
Related to #51046 Thanks for the suggestion, @megafetis. |
Thanks for contacting us. We're moving this issue to the |
I think it can be easy to underestimate the impact this seemingly simple issue can have on the new user experience. Until this is solved, one way or the other, I cannot recommend Blazor as a "great default option for new projects" as I otherwise have in the past. I realize that nothing will happen code-wise until .NET 9, but even just mentioning this problem and workaround on learn.microsoft.com would be a huge improvement. Pardon for being a bit dramatic. I have wasted the better part of two days exploring this, and was left quite frustrated (albeit a bit vindicated) when I finally found this issue. My primary points are those above the horizontal line; the rest of the comment is trying to provide context to back up those points. Again, sorry if it's a bit ramble-y/rant-y. It's been a long day. I was prototyping Blazor 8 for a greenfield project, since "we can use simple static/streaming SSR + forms for the simple non-interactive bits, client-side interactivity for the basic bits, and fall back to server interactivity as a last resort for the complex bits" sounds very promising. Unfortunately, adding just a little bit of interactivity to a form throws that all out the window. Making any input component interactive forces the entire containing form to be interactive, which forces the form's handlers to be interactive, which forces the user to research and ultimately choose between:
And don't forget, you have to make this choice (and work) for every form in your project that needs this small bit of interactivity. Larger, more complicated projects are more likely to already have done a lot of the cruft-work described above, making it a lot easier to "just go" with option 2 or 3. My primary concern is with greenfield projects like mine. I personally ended up choosing option 5, and I suspect most new users will as well. Being faced with this problem/choice this early is a big "quit moment." When the very first letter of a CRUD app throws you at a brick wall, why bother with the rest? |
@Dryvnt Thanks for the feedback. It really is useful, even though I appreciate you've been going through some inconvenience. The solution of adding a As for this point:
Would you be able to clarify? AFAIK it's possible to add some child component into an SSR form and put <input type="number" name="@Name" @bind="value" />
<button onclick="@(() => { value++; })">Increment</button>
@code {
int value;
[Parameter]
public string? Name { get; set; }
[Parameter]
public int Value { get; set; }
protected override void OnInitialized()
{
value = Value;
}
} ... and then use it interactively inside an SSR form: <form @formname="MyForm" method="post" @onsubmit="HandleSubmit">
<AntiforgeryToken />
<FormCounter Name="@nameof(SomeInput)" Value="SomeInput" @rendermode="InteractiveServer" />
<button type="submit">Save</button>
</form>
@code {
[SupplyParameterFromForm]
public int SomeInput { get; set; } = 123;
void HandleSubmit()
{
Console.WriteLine("You submitted " + SomeInput);
}
} In this case the form is static and submits via an HTTP form post, but still contains a button that changes a form field interactively. To be clear, I'm sure you have a real scenario where something is more difficult than this. I'm not trying to say you're wrong - just asking for clarification on the sort of scenario you're actually dealing with. Hope that's OK. |
Thank you for your engagement. I appreciate that you take my frustration seriously. That is a good point you bring up. Basic forms, working with raw Going by the documentation, this is the "intended" way to add validation to a form. In the spirit of "use the provided tools to make the simple stuff simple," I used those components without second thought. Things like manual input name resolution is very nice when you want to keep it simple and make it "just work" so you can focus on the "important" parts. It works perfectly fine with SSR, and I am honestly quite pleased, but trying to have interactive sub-elements does not jive. To provide a concrete example of actual real code that does not work even though it feels like it should be possible, @page "/coolform"
@using TestBlazorBar.Shared
<EditForm Model="Model" OnValidSubmit="OnSubmit" FormName="cool-form">
<DataAnnotationsValidator/>
<SomeSubForm Value="Model" @rendermode="InteractiveWebAssembly"/>
<ValidationSummary/>
<button type="submit">Submit</button>
</EditForm>
@code {
[SupplyParameterFromForm] public SomeFormModel? Model { get; set; }
protected override void OnInitialized()
{
Model ??= new SomeFormModel();
}
public void OnSubmit(EditContext editContext)
{
Console.WriteLine("wow!");
}
} Motivation-wise, this is a sketch of what I would ultimately like to be able to do, sans proper layouting/styling,etc.. I believe something rhyming with this (though probably with Razor Pages + JS at current point in time) is a strong way to get a project off the ground while keeping yourself flexible and still have solid fundamentals at the base of your application, though I am open to criticism in that line of thought; I might just be fundamentally holding it wrong. It's not the first time I would be doing something dumb because it felt right at the time. @page "/widgets"
@inject MyAppContext Context
@inject NavigationManager NavigationManager
<EditForm Model="Model" OnValidSubmit="ValidSubmitHandler" FormName="new-widget">
<DataAnnotationsValidator/>
<InputText @bind-Value="Model.Name"/>
@* InputDoodad has some internal interactivity, e.g.
- toggle some elements when you press a button
- when you fill in field A, field B gets auto-filled
- calculate and display some preview values based on this or that
*@
<InputDoodad @bind-Value="Model.Doodad" @rendermode="InteractiveWebAssembly"/>
<ValidationSummary/>
</EditForm>
@code {
[SupplyParameterFromForm]
public NewWidgetForm? Model { get; set; }
// ...
public async Task ValidSubmitHandler() {
var widget = Model!.MapToEntity();
await Context.Widgets.AddAsync(widget);
await Context.SaveChangesAsync();
NavigationManager.NavigateTo(f"/widgets/{widget.Id}")
}
public class NewWidgetForm {
[StringLength(256, 3)] public string Name { get; set; }
[Required] public DoodadDto Doodad { get; set; }
// etc.
}
} I can accept needing the entire form to be interactive, one way or another, as I imagine serializing whatever EditContext etc. could be quite gnarly, plus the generics of Knowing that I can do the workaround, adding the |
A more concrete example of a "simple interactive input element" I would like to use in an otherwise static form: An input for a Mind that this code isn't entirely well tested or reviewed, I'm sure there's a better way to do this, this is the mangled husk of a component I tried and failed to make work in this context. @typeparam TValue
@inherits InputNumber<TValue?>
<InputCheckbox @bind-Value="ValueEnabled" />
<input
@bind="CurrentValueAsString"
@attributes="AdditionalAttributes"
name="@NameAttributeValue"
class="@CssClass"
disabled="@(!ValueEnabled)"
type="number"
step="any"/>
@code {
private TValue? _underlyingValue;
private bool _valueEnabled;
private bool ValueEnabled
{
get => _valueEnabled;
set
{
_valueEnabled = value;
if (_valueEnabled)
{
Value = _underlyingValue;
}
else
{
_underlyingValue = Value;
Value = default;
}
}
}
protected override void OnParametersSet()
{
_valueEnabled = Value is not null;
}
protected override bool TryParseValueFromString(string? value, out TValue? result, out string? validationErrorMessage)
{
if (value is not (null or ""))
return base.TryParseValueFromString(value, out result, out validationErrorMessage);
result = default;
validationErrorMessage = null;
return true;
}
} |
Thanks for clarifying, @Dryvnt. I think much of this is an aspect of how the In the examples you're giving, you're trying to use
In summary, I think you're right on this point:
Exactly. In general we don't expect bindings to cross rendermode boundaries because:
The ability to mix rendermodes achieves its greatest benefit when done on a per-page basis, or for large and sophisticated components that represent the main content on the page in some sense (e.g., a document editor or interactive map). We're still fairly early in this new world of mixing rendermode, so the community is still developing patterns and recommendations for how and when to use it. Thanks for your feedback on this point since it does help to develop clarity. I appreciate it's not obvious at this stage, but hopefully it will become more obvious over time!
That totally makes sense and is exactly the feature this issue is tracking. We see quite a few use cases for this (e.g., on a fully interactive page, wanting to submit a form as a full page load so it can set cookies in the response, e.g., for logging in or out). |
Is there an existing issue for this?
Is your feature request related to a problem? Please describe the problem.
I need to work with page in interactive mode, and after that submit to handle it with access to HttpContext.
I expect that model will be filled by
[SupplyParameterFromForm] RegisterInputModel Model
after i submit form with javascript interop and invoke a function
submit()
.But in Server interactive mode the hidden value
<input type="hidden" name="_handler" value="{FormName}" />
is removed.My temporary working soution is:
Describe the solution you'd like
solution 1: Do not remove hidden field
_handler
withFormName
value and in interactive mode.solution 2: Provide some API in
EditContext
to call submit with reload page.Additional context
Sorry for my English...
The text was updated successfully, but these errors were encountered: