Search code examples
blazor.net-6.0blazor-webassemblymudblazor

Blazor Web Assembly - Event Not Working Correctly


I have a Blazor WebAssembly (.NET 6) application. I am using the MudBlazor component library.

I have a MudTextField (text area) and MudFab (button) like this:

 <MudTextField T="string" Label="Message" Variant="Variant.Outlined" @bind-Value="@newMessage" Clearable="true" Lines="2" />
 <MudFab Color="Color.Primary" StartIcon="@Icons.Material.Filled.Send" OnClick="@SendAsync" />

In the OnClick event handler of the MudFab (button), I am calling SendAsync function, which looks like the following:

async Task SendAsync()
{
    //some logic
    await _client.SendMessageAsync(newMessage);      
}

So user types text in MudTextField. Then hits send button (MudFab), and SendAsync method mentioned above gets executed. Everything so far works fine.

Now I want to add feature where user can press Ctrl + Enter key while typing message, and this should execute SendAsync function. i.e, user does not have to use mouse and click send button (MudFab).

For this, I added following event handler in my code, and bound it to OnKeyDown event. The code looks like this:

// notice 'OnKeyDown'
<MudTextField T="string" Label="Message" Variant="Variant.Outlined" @bind-Value="@newMessage" Clearable="true" Lines="2" OnKeyDown="HandleKeyDown"/>
<MudFab Color="Color.Primary" StartIcon="@Icons.Material.Filled.Send" OnClick="@SendAsync" />

....

private async Task HandleKeyDown(KeyboardEventArgs e)
{
    if (e.CtrlKey && e.Key == "Enter")
    {
        await SendAsync();
    }
}

Problem

This code only works if I place a breakpoint in the HandleKeyDown method. Otherwise it doesn't work at all. I have been struggling for more than two days to make this simple logic work. What am I missing?

Things I have tried:

  • Tried adding StateChanged() after SendAsync(), but did not work.
  • Tried changing MudTextField to simple <input>, and MudFab to simple button, but did not work.
  • Tried to use Javascript Interop, it also did not work.

Last I tried doing it using jQuery as shown here, but it also had the same problem. It only worked if I place 'debugger;' in the on("keydown"... function:

// this code is inside <script> tag:
$(document).ready(function () {
    // Handle key press event
    $(document).on("keydown", function (e) {
        if (e.ctrlKey && e.key === "Enter") {
            
            e.preventDefault(); /
            //debugger; ------->> WORKS fine if I uncomment this
            $("#btnChat_SendMessage").click();
            console.log("pressed::ctrl-Enter::::::::::::::");

            // tried Adding a slight delay
            setTimeout(function () {
                $("#btnChat_SendMessage").trigger("click");
            }, 2000); 
        }
    });
});

If I check browser console, I can see

pressed::ctrl-Enter::::::::::::::

printed on the console. It is printed as many times as I have hit Ctrl + Enter. But logic doesn't get executed.

If I run $("#btnChat_SendMessage").trigger("click"); directly on the browser console, the logic gets executed just fine.

Why is this happening? Can somebody please point out my mistake.

Thanks.


Solution

  • Use the KeyDownPreventDefault property on MudTextField to specify which keys need to be overriden. MudText docs

    In the example below, a boolean field _preventDefault is created to keep track of which keys to override. Then we call the SendAsync() function only when the Ctrl+Enter 's are pressed.

    <MudTextField T="string" @bind-Value="@_newMessage"
                OnKeyDown="HandleOnKeyDown"
                KeyDownPreventDefault="@_preventDefault"
                Immediate="true"
                Variant="Variant.Outlined" Clearable="true" Lines="2" Label="Message"/>
     <MudFab Color="Color.Primary" StartIcon="@Icons.Material.Filled.Send" OnClick="@SendAsync" />
    
    <h2>@_sendAsyncMessage</h2>
    @code {
        string _newMessage="";
        bool _preventDefault = false;
        string _sendAsyncMessage="";
        public async Task HandleOnKeyDown(KeyboardEventArgs e)
        {
            if (e.Key == "Enter" && e.CtrlKey)
            {
                _preventDefault=true;
                await SendAsync();
            }
            else{
                _preventDefault=false;
            }
        }
        async Task SendAsync()
        {
            _sendAsyncMessage = $"Sent message:{_newMessage}";
            await Task.Delay(1000);
            _sendAsyncMessage="";
        }
    }
    

    MudBlazor snippet

    Note I added Immediate="true" property because the example relies on the _newMessage being bound on each key press. Use/remove it according to your needs.