Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

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.

Post.razor
<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.
  • elements and submit: Blazor's post works, but scripts that read form.elements or call form.submit() break; FilterForm's script reads form.elements.

Fix

Name the buttons something the form element doesn't have, such as intent, and read it under that name:

Post.razor
<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>

@code {
    [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

MultiSubmit.razor
@using 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>

@code {
    [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.";
    }
}