1

I'm trying to make an edit form for a table that is populated by a list of objects. I have the page laid out with a table on the left and a form on the right. The form is populated by a second object that is filed out in the getBankDetail onClick event.

My problem is that, when I click on my cancel button the form disables correctly, but any edits to the form details remain and the table with the original objects also reflects this change even though the edit form is created from a secondary object.

TABLE:

<table class="table table-hover fs-7">
    <thead>
        <tr">
            <th>Payment Types</th>
            <th>Originator Reference</th>
            <th>Originator Particulars</th>
            <th>Other Party Reference</th>
            <th>Other Party Analysis Code</th>
            <th>Enquiries Email Address</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var bankDetail in listBankDetails)
        {
            <tr @onclick="() => getBankDetail(bankDetail.BankCodeLookupId)">
                <td>@bankDetail.ItemDescription</td>
                <td>@bankDetail.OriginatorReference</td>
                <td>@bankDetail.OriginatorParticulars</td>
                <td>@bankDetail.OtherPartyReference</td>
                <td>@bankDetail.OtherPartyAnalysisCode</td>
                <td>@bankDetail.SendEnquiriesToEmailAddress</td>
            </tr>
        }
    </tbody>
</table>

FORM:

<form class="border container p-2">
    <div class="row p-2 fs-5">
        <label class="col">Edit Details</label><label class="col oi oi-pencil" @onclick="@editBankDetail"></label>
    </div>
    <div class="row p-2">
        <label for="txtPaymentType" class="col">Payment Type</label>
        <input class="form-control col" type="text" disabled="@isDisabled" id="txtPaymentType" @bind-value=@editDetail.ItemDescription/>
    </div>
    <div class="row p-2">
        <label for="txtOGRef" class="col">Originator Reference</label>
        <input id="txtOGRef" class="form-control col" type="text" disabled="@isDisabled" @bind-value=@editDetail.OriginatorReference />
    </div>
    <div class="row p-2">
        <label for="txtOGParticular" class="col">Originator Particulars</label>
        <input id="txtOGParticular" class="form-control col" type="text" disabled="@isDisabled" @bind-value=@editDetail.OriginatorParticulars />
    </div>
    <div class="row p-2">
        <label for="txtOPRef" class="col">Other Party Reference</label>
        <input id="txtOPRef" class="form-control col" type="text" @bind-value=@editDetail.OtherPartyReference disabled="@isDisabled" />
    </div>
    <div class="row p-2">
        <label for="txtOPCode" class="col">Other Party Analysis Code</label>
        <input id="txtOPCode" class="form-control col" type="text" @bind-value=@editDetail.OtherPartyAnalysisCode disabled="@isDisabled" />
    </div>
    <div class="row p-2">
        <label for="txtEnqEmail" class="col">Enquiries Email Address</label>
        <input id="txtEnqEmail" class="form-control col" type="text" @bind-value=@editDetail.SendEnquiriesToEmailAddress disabled="@isDisabled" />
    </div>
    <!--TODO: Replace these three spans with proper styling on the buttons, once I'm smart enough to know what to do.-->
    <div class="row p-2">
        <span class="col"/>
        <button class="px-1 btn btn-primary col" type="button" id="btnSaveEdit" disabled="@isDisabled">Save Changes</button>
        <span class="col"/>
        <button class="px-1 btn btn-secondary col" type="button" id="btnCancelEdit" disabled="@isDisabled" @onclick="cancelChanges">Cancel Changes</button>
        <span class="col" />
    </div>
</form>
@code
{
    //I define a list of the objects, and a spare to be the "Editable" item for the form.
    List<bankdetailobjects> listA = new List<bankdetailobjects>();
    bankdetailobject subObjectA = new bankdetailobject();

    //An Int to hold the ID number of the selected object
    int currentDetail;

    protected override async Task OnInitializedAsync()
    {
        //Get list of all payment types / bank details
        listA = await getPaymentTypes();

        //make sure edit form is disabled
        isDisabled = true;

        //store the details of the first entry, to be used on the edit form
        subObjectA= listA.First();
        currentDetail = subObjectA.BankCodeLookupId;

        //Show changes
        StateHasChanged();
    }

    public async Task<bankdetailobjects> getBankDetail(int i)
    {
        //Disable the edit fields to prevent accidental editing of the 'new' entry.
        isDisabled = true;

        //Set the ID for potential reset/cancellation purpose
        currentDetail = i;

        //Change the "Edit" table on right-side of screen to reflect chosen Bank Detail
        subObjectA= listA.Find(x => x.BankCodeLookupId == i);


        return subObjectA;
    }

    public void cancelChanges()
    {
        //Disable the editing fields
        isDisabled = true;
        //Refresh the field data
        getBankDetail(currentDetail);
        StateHasChanged();
    }
}

Let's say I select a random row from my table below

ID Bank Name
123 Big ol' bank

The getBankDetail function should be using the details of ID 123 to overwrite whatever was in subObjectA to match.

So if I use the edit form to change the name value of subObjectA to "Slightly bigger bank" the original object in listA should be unchanged, because I don't have any code that makes any changes to this list after it's creation? But when I hit cancel it will the update to

ID Bank Name
123 Slightly bigger bank

I can understand why my cancel function isn't resetting all the text fields to their original value in the form (I haven't actually asked it too yet) but what I don't understand is why the changes I make to subObjectA are also reflected in the table populated by listA, because once I've copied the ListA object over to subObjectA I'm leaving the original list alone right? There shouldn't be any changes to the list unless I specifically edit the list?

ViRuSTriNiTy
  • 5,017
  • 2
  • 32
  • 58
Wompguinea
  • 378
  • 3
  • 19
  • 3
    Without going through all this code, I'm pretty comfortable saying that you are passing a reference to an object, when you think you are making a new (you said "secondary") object. So when you `Find` an object in a `List`, pass it to a new table and `bind` to it, you are in fact editing the original object. The solution is to set the `value` of your inputs from the passed object but to `bind` to a different object create with `new()`. An aside: html `input` objects are text by default so you don't need to specify the input type for many of these. – Bennyboy1973 Apr 14 '23 at 03:18
  • 1
    As @Bennyboy1973 already pointed out, you are working with the same object. To be more specific, you are calling `getBankDetail()` here and there which returns an object from `listA` which is initialized only once. – ViRuSTriNiTy Apr 14 '23 at 09:18

1 Answers1

1

As @Bennyboy1973 points out, you are working with the same object.

You need to separate out your edit context from your list context.

Get a copy of the record you are editing directly from the data store. Edit that record and then save it back to the data store or discard it.

Use a Notification service to keep your list view in sync. The edit service raises a ListChanged event in the notification service when a record is updated in the data store. Your list context has a registered handler which drives a data refresh in the list context.

Providing an answer on how to implement all of this is beyond the scope of a SO answer. Here are some previous answers that demonstrate coding patterns and methods that should help you resolve your issue:

  1. https://stackoverflow.com/a/69562295/13065781
  2. https://stackoverflow.com/a/75944088/13065781
  3. https://stackoverflow.com/a/75157903/13065781
MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31