Blazor webassembly button onclick not triggered after bound input text changed

Viewed 262

I have this page that displays a collection of elements with EDIT/ADD functionality.

My issue is that the EDIT onclick binding stops working when I change the value of the related input text field.

In the example below, I click the edit button, the edit row is displayed and as long as I don't change the value in <input type="text" @bind="InputGroup.GroupName"/> when I press the <button @onclick="(e=> SaveEdit(g))">Save</button> button, I see a call to SaveEdit. If I change the input, nothing - but the page does rerender.

Been looking at this all day - anyone got any ideas what I've done wrong?

Here's the code - it is all self contained...

@page "/groupAdmin"
@using System.Diagnostics

<h3>GroupAdmin</h3>
<ul>
    <li>@InputGroup.GroupName</li>
</ul>
<table>
    <thead>
    <tr>
        <th>Name</th>
    </tr>
    </thead>
    <tbody>

    @foreach (var g in Groups)
    {
        if (Mode == AdminPageEditMode.Edit && g.GroupName == InputGroup.GroupName)
        {
            <tr>
                <td>
                    <input type="text" @bind="InputGroup.GroupName"/>
                </td>
                <td>
                    <button @onclick="(e=> SaveEdit(g))">Save</button>
                    <button @onclick="(e => SetMode(AdminPageEditMode.None))">Cancel</button>
                </td>
            </tr>
        }
        else
        {
            <tr>
                <td>
                    @g.GroupName
                </td>
                <td>
                    <button @onclick="(e => SetMode(AdminPageEditMode.Edit, g))">Edit</button>
                </td>
            </tr>
        }
    }
    </tbody>
    <tfoot>
    @if (Mode == AdminPageEditMode.Add)
    {
        <tr>
            <td>
                <input type="text" @bind="InputGroup.GroupName"/>
            </td>
            <td>
                <button @onclick="SaveAdd">Save</button>
                <button @onclick="(e => SetMode(AdminPageEditMode.None))">Cancel</button>
            </td>
        </tr>
    }
    else
    {
        <tr>
            <td></td>
            <td>
                <button @onclick="(e => SetMode(AdminPageEditMode.Add))">Add</button>
            </td>
        </tr>
    }

    </tfoot>
</table>


@code {

    private PagingList _paging;
    private AdminPageEditMode _mode = AdminPageEditMode.None;

    [Parameter]
    public IList<ConfigurationKeyGroup> Groups { get; set; } = new List<ConfigurationKeyGroup>();

    [Parameter]
    public ConfigurationKeyGroup InputGroup { get; set; } = new ConfigurationKeyGroup();


    [Parameter]
    public AdminPageEditMode Mode
    {
        get => _mode;
        set
        {
            _mode = value;
        }
    }

    protected override Task OnInitializedAsync()
    {
        Groups.Add(new ConfigurationKeyGroup("Test"));
        return base.OnInitializedAsync();
    }

    protected void SetMode(AdminPageEditMode mode, ConfigurationKeyGroup groupkey = null)
    {
        Debug.WriteLine($"Set Mode {mode} from {Mode}, {groupkey}");
        this.Mode = mode;
        this.InputGroup = new ConfigurationKeyGroup();
        switch (mode)
        {
            case AdminPageEditMode.Edit:
                this.InputGroup.GroupName = groupkey.GroupName;
                break;
        }
        this.StateHasChanged();
    }

    private async void SaveEdit(ConfigurationKeyGroup original)
    {
        Debug.WriteLine($"Save edit 111 {original.GroupName}");
        var g = Groups.IndexOf(original);
        Groups[g].GroupName = InputGroup.GroupName;
        SetMode(AdminPageEditMode.None);
    }

    private void SaveAdd()
    {
        Debug.WriteLine($"Save add 119");
        this.Groups.Add(new ConfigurationKeyGroup(this.InputGroup.GroupName));
        this.SetMode(AdminPageEditMode.None);
    }


    public class ConfigurationKeyGroup
    {
        public string GroupName { get; set; }

        public ConfigurationKeyGroup()
        {

        }

        public ConfigurationKeyGroup(string groupName)
        {
            GroupName = groupName;
        }
    }

    public enum AdminPageEditMode
    {
        None,
        Add,
        Edit
    }

}
2 Answers

I can see one issue, you should always check to the null on objects in the markup code before using them... the example from blazor sample:

    @if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">

Also, I'm not sure you need this return base.OnInitializedAsync() in here:

protected override Task OnInitializedAsync()
{
    Groups.Add(new ConfigurationKeyGroup("Test"));
    return base.OnInitializedAsync();
}

Thanks for those who took the time - forced me to clean up a couple of things and explain some other things, which led me to the answer.

Looking at 'Nik P's suggesting in the comments, and by making the binding apply on chnage, I noticed the page was re-evaluting the line if (Mode == AdminPageEditMode.Edit && g.GroupName == InputGroup.GroupName) and as InputGroup.GroupName was now different, the save button wasn't rendered.

My guess is that somewhere, removing that button from the DOM also cancels the event invocation from the button. I can't prove it, but by changing the condition to test against the original group name and not the updated one, makes it work.

if (Mode == AdminPageEditMode.Edit && g.GroupName == EditGroup.GroupName) { <tr> <td> <input type="text" @bind="InputGroup.GroupName" /> </td> <td> <button @onclick="(e=> SaveEdit(g))">Save</button> <button @onclick="(e => SetMode(AdminPageEditMode.None))">Cancel</button> </td> </tr> }

...

protected void SetMode(AdminPageEditMode mode, ConfigurationKeyGroup groupkey = null) { Debug.WriteLine($"Set Mode {mode} from {Mode}, {groupkey}"); this.Mode = mode; this.InputGroup = new ConfigurationKeyGroup(); switch (mode) { case AdminPageEditMode.Edit: this.InputGroup.GroupName = groupkey.GroupName; this.EditGroup = groupkey; break; } this.StateHasChanged(); }

Related