A form posts to [object RadioNodeList]
A control named action, method or enctype hides the form's own property
from Blazor's script. Give it another name.
Problem
On a static page, a form whose submit buttons are named action posts to an address like
/[object RadioNodeList], or /[object HTMLButtonElement] with one button, and shows a
page that doesn't exist. With JavaScript off, or on an interactive page, the same form works.
<Form Model="Model" FormName="article" OnValidSubmit="SaveAsync">
<TextField @bind-Value="Model!.Title" />
<Button Submit Name="action" Value="publish">Publish</Button>
<Button Submit Name="action" Value="draft">Save draft</Button>
</Form>Cause
In the browser, a form's controls are also properties of the form element, by name. A control named
action replaces form.action, the address the form posts to, with the control
itself, or with a list when several share the name. As an address, that becomes text such as
"[object RadioNodeList]". Blazor's enhanced form handling reads
form.action to send the post, so it sends it there. The browser's own post, with JavaScript off,
reads the attribute instead and isn't affected.
Other names the form element has go wrong in their own ways, with JavaScript on:
method: nothing posts, and the console shows that "[object RadioNodeList]" isn't a valid HTTP method.enctype: the form posts as plain text, and the server answers 400 Bad Request.target: Blazor leaves the post to the browser, so it loads the whole page instead of an enhanced post.elementsandsubmit: Blazor's post works, but scripts that readform.elementsor callform.submit()break;FilterForm's script readsform.elements.
Fix
Name the buttons something the form element doesn't have, such as intent, and read it under that name:
<Form Model="Model" FormName="article" OnValidSubmit="SaveAsync">
<TextField @bind-Value="Model!.Title" />
<Button Submit Name="intent" Value="publish">Publish</Button>
<Button Submit Name="intent" Value="draft">Save draft</Button>
</Form>
{
[SupplyParameterFromForm(FormName = "article", Name = "intent")]
private string? Intent { get; set; }
}
An interactive page doesn't post, so Intent is null there: give each button an
OnClick too, as the example does and
OnClick does nothing on a static page shows.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="article" OnValidSubmit="Save" MaxWidth="36.Rem">
<TextField @bind-Value="Model!.Title" />
<TextArea @bind-Value="Model!.Body" Rows="3" />
<FormActions Justify="Justify.Start">
<Button Submit Name="intent" Value="publish" StartIcon="Lucide.Send"
OnClick='() => _pressed = "publish"'>Publish</Button>
<Button Submit Name="intent" Value="draft" Variant="ButtonVariant.Outline" Accent="Accent.Neutral"
OnClick='() => _pressed = "draft"'>Save draft</Button>
<Button Submit Name="intent" Value="discard" Variant="ButtonVariant.Ghost" Accent="Accent.Danger"
OnClick='() => _pressed = "discard"'>Discard</Button>
</FormActions>
if (_result is not null)
{
<Alert Status="Status.Success">_result</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "article")]
private Article? Model { get; set; }
// Static pages: the pressed button's Name=Value posts with the form.
[SupplyParameterFromForm(FormName = "article", Name = "intent")]
private string? Intent { get; set; }
// Interactive pages post nothing, so OnClick says which button it was.
private string? _pressed;
private string? _result;
protected override void OnInitialized() => Model ??= new();
private void Save() => _result = (Intent ?? _pressed) switch
{
"publish" => $"Published \"{Model!.Title}\".",
"draft" => "Saved as a draft.",
"discard" => "Discarded the changes.",
_ => null,
};
public sealed class Article
{
[Required, StringLength(80)]
public string Title { get; set; } = "What's new in alpha.12";
public string? Body { get; set; } = "OnThisPage, PrevNext and CopyButton, for docs sites.";
}
}Related
- Button:
NameandValueon submit buttons. - Several submit buttons: the demo above, with its source.
- OnClick does nothing on a static page: why the buttons post at all.