Skip to content
IzzyBlazor 0.1.0-alpha.17

Enter all results ↑ ↓ move Esc close

OnClick does nothing on a static page

A static page has no connection to the server once it's loaded, so C# can't run on a click. Post a form or follow a link instead, or make the page interactive.

Problem

A button with OnClick does nothing on a static page: no error, no request, and the page doesn't change. The same goes for other C# events, such as a menu item's OnClick or a tag's OnRemove, and for code that runs when a bound value changes (ValueChanged, @bind-Value:after).

Post.razor
<Button OnClick="PublishAsync">Publish</Button>

Cause

C# event handlers run over an interactive page's circuit (or in WebAssembly). A static page renders once per request and sends HTML, with nothing left on the server to call, so the button renders as a plain <button type="button">. The API pages label these parameters Interactive only.

Fix

Pick by what the click should do:

  • Change data: make it a Submit button in a Form. On a static page the post runs OnValidSubmit; on an interactive page the same handler runs without a post. With several buttons, give each a Name and Value, which post with the form on a static page, and an OnClick for interactive pages, where nothing posts.
  • Show something else: make it a link with Href, carrying any choice in the query string.
  • React to every change: make the page interactive with @rendermode InteractiveServer (or WebAssembly or Auto). For filters, FilterForm applies them through the address, on a static page too.
Post.razor
<Form Model="Model" FormName="post" OnValidSubmit="SaveAsync">
    <TextField @bind-Value="Model!.Title" />
    <Button Submit Name="intent" Value="publish"
            OnClick='() => _pressed = "publish"'>Publish</Button>
    <Button Submit Name="intent" Value="draft"
            OnClick='() => _pressed = "draft"'>Save draft</Button>
</Form>

@code {
    [SupplyParameterFromForm(FormName = "post")]
    private PostDraft? Model { get; set; }

    // Static pages: the pressed button posts its Name and Value.
    [SupplyParameterFromForm(FormName = "post", Name = "intent")]
    private string? Intent { get; set; }

    // Interactive pages don't post: OnClick says which button it was.
    private string? _pressed;

    protected override void OnInitialized() => Model ??= new();

    private async Task SaveAsync()
    {
        var publish = (Intent ?? _pressed) == "publish";
        await Posts.SaveAsync(Model!, publish);
    }
}

Here each button posts its intent on a static page, and OnClick records it on an interactive one:

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.";
    }
}