0

In continuation of this post I face a problem when cloning an object by serializing/deserializing it with Newtonsoft.Json.

The purpose:

From a list of ItemView's I'm passing a ItemView parameter to a dialog/modal component with an EditForm. If the user clicks the Cancel-button and the dialog closes I must "reset" the parameter so that any unsaved changes in the form is not reflected in the parent list.

Therefore, in my OnInitializedAsync, I'm cloning the parameter to an ItemViewClone object so that I can set ItemView = ItemViewClone when the user cancels the form.

Models:

public class Item
{
    public Guid Id { get; set; }
    public string Title { get; set; }
}

public class ItemView
{
    public Item Item { get; set; }
    public int Foo { get; set; }
    public double Bar { get; set; }
}

Extension / cloning:

public static class Extensions
{
    public static T Clone<T>(this T source)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source , new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Serialize, PreserveReferencesHandling = PreserveReferencesHandling.Objects }));
    }
}

Blazor component:

<div class="modal-container">
    <div class="modal">
        <EditForm Model="@ItemView" OnValidSubmit="SaveForm">
            <p><input type="text" @bind="@ItemView.Item.Title" /></p>
            <p><input type="button" @onclick="Cancel" value="Cancel" /><input type="submit" value="Save" /></p>
        </EditForm>
    </div>
</div>

@code {

    [Parameter]
    public EventCallback<ItemView> Callback { get; set; }

    [Parameter]
    public ItemView ItemView { get; set; } = new();
    public ItemView ItemViewClone { get; set; } = new();

    protected override async Task OnInitializedAsync()
    {
        ItemViewClone = ItemView.Clone(); // Or:
        ItemViewClone.Item = ItemView.Item.Clone();
    }

    async Task Cancel
    {
        ItemView = ItemViewClone; // <= Doesn't work
        ItemView.Item = ItemViewClone.Item; // <= This works
        await Callback.InvokeAsync(null);
    }

}

If I clone ItemView it doesn't work. When I edit ItemView.Item.Title and close the dialog without saving, I see the changes in the parent list. But if I clone the Item inside the ItemView as also shown in the example, it works.

What could I be missing?

Cloning ItemView to ItemViewClone do work. It holds all the right values. It's the "resetting" that doesn't work.

Mads
  • 385
  • 1
  • 5
  • 18
  • consider binding your form to ItemViewClone *not* a parameter of the component. OnValidSubmit copy the properties that you expose on the form to the ItemView Parameter. – Brian Parker May 21 '22 at 16:58
  • That’s not a bad idea. :) Must try that when I’m back in the office. – Mads May 21 '22 at 20:42
  • Now I've tried passing the clone as the form model. I can do the editing but when I save the form and set `ItemView = ItemViewClone` no changes happens. Also, I get an EF Core error when calling `.Update` that the {Id} is already being tracked. It seems like a good approach to work on a clone that just can be discarded, but I don't know if it can work? – Mads May 23 '22 at 13:31
  • Your using Blazor-Server you can only have one object tracked. So you need to update that object or create a new instance of the context. – Brian Parker May 23 '22 at 15:21
  • I know. But how can I avoid that with this approach? I make a clone of the item in the list. Working on the clone and then lastly sets the item in the list quals to the clone; `ItemView = ItemViewClone`. Setting `.AsNoTracking()` on the list doesn't seem to change anything. – Mads May 24 '22 at 08:56
  • `ItemView = ItemViewClone` is assigning the reference. Write a method to copy the properties. `ItemView.Blah = ItemViewClone.Blah` for all the properties exposed on your form. – Brian Parker May 24 '22 at 10:16

0 Answers0