EditForm - How can I prevent submit on pressing enter key

Viewed 9443

I've found this article but I'm having a hard time to understand how can I prevent submit on "enter" key independently by any <input>

<EditForm Model="exampleModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText id="name" @bind-Value="exampleModel.Name" />
    <InputText id="name2" @bind-Value="exampleModel.Name2" />

    <button type="submit">Submit</button>
</EditForm>
@code {
    private ExampleModel exampleModel = new ExampleModel();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }

    public class ExampleModel
    {
         [Required]
         [StringLength(10, ErrorMessage = "Name is too long.")]
         public string Name { get; set; }
         public string Name2 {get; set;}
    }
}

Use Case

Enter key On HTML forms if you are filling out a text box and press the enter key it will submit the form, even if you haven't finished filling in the rest of the information. There are many websites which use this feature such as Google search box will submit when you press the enter key. This works because you only have one text box to fill out, but if there are more than one field to fill in you don't want the form to submit on the enter key.

6 Answers

Leonardo Lurci, here's a complete solution implemented purely in C#, no JSInterop. As it turned out, Microsoft has already provided this feature, but they did not provide enough samples to demonstrate how to use it.

As it turned out, I cannot use the pair @onkeypress="@KeyHandler" and @onkeypress:preventDefault with the Forms components such as InputText, but applying these directives to Html tags is viable and works perfectly well. See for instance how I apply these directives to the "submit" button.

Consequently, I subclass the base class InputBase, this is the class from which the InputText components derive, overrides the default view rendering by adding an input element to which I can add the directives of the new feature.

TextBox.razor (this comes instead of InputText)


    @inherits InputBase<string>

    <input type="text" value="@CurrentValueAsString" id="Id" class="@CssClass" 
       @onkeydown="KeyDownHandler" @onkeypress="KeyPressHandler" 
                                                   @onkeypress:preventDefault/>

     @code{

    protected override bool TryParseValueFromString(string value, out string 
                                      result, out string validationErrorMessage)
    {
        result = value;
        validationErrorMessage = null;
        return true;
    }

    void KeyDownHandler(KeyboardEventArgs args)
    {
        if (args.Key == "Backspace" && CurrentValueAsString.Length >=1)
        {
            CurrentValueAsString = CurrentValueAsString.Substring(0, 
                                       CurrentValueAsString.Length - 1);
        }
    }

     void KeyPressHandler(KeyboardEventArgs args)
     {

        if (args.Key == "Enter")
        {
            return;
        }
        var key = (string)args.Key;
        CurrentValueAsString += key;
     }
    }

Usage


    <p>Leave me a comment</p>

    <EditForm Model="Model" OnValidSubmit="HandleValidSubmit" >
    <DataAnnotationsValidator />

     <div class="form-group">
        <label for="name">Name: </label>
        <TextBox Id="name" Class="form-control" @bind-Value="@Model.Name" >
        </TextBox>
        <ValidationMessage For="@(() => Model.Name)" />

     </div>
     <div class="form-group">
        <label for="body">Text: </label>
        <InputTextArea Id="body" Class="form-control" @bind-Value="@Model.Text" > 
        </InputTextArea>
        <ValidationMessage For="@(() => Model.Text)" />
     </div>
     <p>
        <button type="submit" @onkeypress="KeyHandler" @onkeypress:preventDefault>
            Submit
        </button>
     </p>
    </EditForm>


    @code
    {
        private Comment Model = new Comment();

        private void HandleValidSubmit()
        {
             Console.WriteLine("Submit...");
        }


        void KeyHandler(KeyboardEventArgs args)
        {
            if (args.Key == "Enter")
            {
               return;
             }
        }

        public class Comment
        {
            public string Name { get; set; } = "Jeff";
            public string Text { get; set; } = "I'm Jeff. I'm from Canada";
        }

    }

Please, don't hesitate to ask any questions

Hope this helps...

As Enet suggested in the comments, there isn't a full Blazor (Csharp) solution for this use-case. You have to interact with JavaScript.

Currently, I solved with this workaround:

blazor-side

@inject IJSRuntime JS
<EditForm id="askQuestionForm" Model="exampleModel" OnValidSubmit="HandleValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <InputText id="name" @bind-Value="exampleModel.Name" />
    <InputText id="name2" @bind-Value="exampleModel.Name2" />

    <button type="submit">Submit</button>
</EditForm>
@code {
    private ExampleModel exampleModel = new ExampleModel();

    private void HandleValidSubmit()
    {
        Console.WriteLine("OnValidSubmit");
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender) 
        {
            JS.InvokeVoidAsync("PreventEnterKey", "askQuestionForm");
        }
    }

    public class ExampleModel
    {
         [Required]
         [StringLength(10, ErrorMessage = "Name is too long.")]
         public string Name { get; set; }
         public string Name2 {get; set;}
    }
}

interop.js

function PreventEnterKey(id) {
    $(`#${id}`).keydown(function (event) {
        if (event.keyCode == 13) {
            event.preventDefault();
            return false;
        }
    });
}

I hope Microsoft will develop this feature in the future.

Thanks to Enet.

Add event handlers to your input and submit elements like this:

<InputText id="name" @bind-Value="exampleModel.Name" @onkeydown="PreventSubmit"/>
<button type="submit" @onclick="ShouldISubmit">Submit</button>

Add this to your @code block:


public bool shouldsubmit { get; set; }

public bool entersubmit { get; set; }

public void PreventSubmit(KeyboardEventArgs ev)
{
    if (ev.Key == "Enter")
        entersubmit = true;
}

public void ShouldISubmit()
{
    if (entersubmit)
        shouldsubmit = false;
    else
        shouldsubmit = true;
    entersubmit = false;
}

Change your submit function to this


private void HandleValidSubmit()
{
    if (shouldsubmit) {
        Console.WriteLine("OnValidSubmit");
    }
}

I found a simple solution that I think is the easiest option. Create a custom component that will be your submit button:

<button type="button" @onclick="OnClick" @attributes="UnmatchedAttributes">@Text</button>


@code {

    [Parameter]
    public string Text { get; set; }

    [Parameter]
    public Func<Task> OnValidSubmitAsync { get; set; }

    [Parameter]
    public Action OnValidSubmit { get; set; }

    [CascadingParameter]
    public EditContext EditContext { get; set; }

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> UnmatchedAttributes { get; set; }

    private async Task OnClick() {
        if (EditContext != null) {
            if (EditContext.Validate()) {
                if (OnValidSubmit != null) {
                    OnValidSubmit();
                } else {
                    if (OnValidSubmitAsync != null) {
                        await OnValidSubmitAsync();
                    }
                }
            }
        }
    }

}

This component acts as a submit button, but since its type is not submit, an enter won't trigger it. It accepts a cascading parameter of type EditContext which allows it to validate the form. It also accepts a delegate which will be triggered when it is clicked and the form state is valid.

Usage example:

<EditForm Model="model">
    <input type="text" @bind="model.Value" />
    @* Add the custom submit component*@
    <EditFormSubmit Text="Submit" OnValidSubmit="Submit" class="btn btn-primary"></EditFormSubmit> 
</EditForm>

First you should understand how browsers behaves in this case. If there is a <button type="submit"> in the form, and user presses enter, the browser will emulate click event on the button.

Rather then cancelling the keypress event you can prevent the click event using addEventListener with capture: true.

Just add following javascript:

//prevent submit on enter
document.body.addEventListener('keypress', e => {
    if (e.which == ENTER && !e.target.matches('button[type=submit]')) {

        /** @type {HTMLFormElement} */
        const form = e.composedPath().find(e => e.matches('form'))

        /** @type {HTMLButtonElement} */
        const submitButton = form && form.querySelector('button[type=submit]:enabled');

        if (submitButton) {
            const preventSubmit = (e2) => {
                e2.preventDefault();
                return false;
            }
            // Browser emulates submit button click, when enter key is pressed, so we need to prevent that.
            form.addEventListener('click', preventSubmit, { once: true, capture: true });
            setTimeout(() => form.removeEventListener('click', preventSubmit, { capture: true }), 0); // better clear the eventlistener despite once:true in case the keypress propagation has been stopped by some other eventhandler
        }
        
    }
});

It works in Blazor Server and Blazor WebAssembly and you don't need to touch your exiting code.

Demo here: https://blazorrepl.com/repl/mvYgPhPx007ySOGe03

A Keep It Simple approach:

  1. Add an item to your Item's class definition, for WhatToDo:

    public string WhatToDo { get; set; }
    
  2. Add an invisible button as the first button in your submit buttons, and

  3. Test for its value in your OnSubmit method. If the user just presses Enter, the value of this button will be returned.

If it's value is (in my case "DoNothing") then simply return.

<EditForm..... with OnSubmit parameters>
...

   <button type="submit" @onclick="@(()=> selectedItem.WhatToDo = "DoNothing")" class="btn btn-primary" style="display:none;">Do Nothing</button>
    <button type="submit" @onclick="@(()=> selectedItem.WhatToDo = "Save")" class="btn btn-primary">Save</button>
    <button type="submit" @onclick="@(()=> selectedItem.WhatToDo = "ExitWOSave")" class="btn btn-primary">Exit w/o Saving</button>
</EditForm>

@code {
  private async Task InsertOrUpdateItem()
    {
        Console.WriteLine("WhatToDo is XX" + selectedItem.WhatToDo + "XX");

        if (selectedItem.WhatToDo == "ExitWOSave")   //test for this first
        {
            // Navigate away…
            return;
        };

        if (selectedItem.WhatToDo == "DoNothing")
        { return; }

        if (selectedItem.WhatToDo == "Save")
        { //do your save…
    return; 
}

}
Related