Search code examples
formsasp.net-corepostblazor

Property value is reset before form is submitted


I have a statically-rendered EditForm, which is responsible for submitting a model so I can add it to my database.

@page "/Test"
@using Domain.Entities

<h3>Test</h3>

<EditForm Model="@Model" OnSubmit="@HandleSubmitted" FormName="CreateModel" Enhance>
    <InputText @bind-Value="@Model.Name"></InputText>
    <button type="submit" > Submit</button>
</EditForm>

@code {
    [SupplyParameterFromForm]
    private TestModel Model { get; set; } = new();

    protected override void OnInitialized()
    {
        Console.WriteLine($"Initialized: {Model}");
    }

    private void HandleSubmitted(EditContext obj)
    {
        Console.WriteLine($"Submitted: {Model}");
    }
}
public record TestModel
{
    public Guid Id { get; set; } = Guid.NewGuid();

    public string Name { get; set; } = "";
}

Here's the log output (refresh page, edit name, submit form):

Initialized: TestModel { Id = 9e8e339a-cccc-499e-9f1e-76883657530d, Name =  }
Initialized: TestModel { Id = 00000000-0000-0000-0000-000000000000, Name = New Name }
Submitted: TestModel { Id = 00000000-0000-0000-0000-000000000000, Name = New Name }

I was expecting the Id (9e8...) to not reset when submitting the form. I'm not sure if I'm missing some concept or if I did not set up the form correctly. I want to find a proper way to submit the form with a valid Id.

I tried adding the Id as an editable field before and that seemed to resolve the issue - it wasn't submitting the empty guid anymore. However, I wouldn't like to expose the Id to the end-user at this stage. I don't suppose having that Id input field and setting it to hidden is the actual way to go?


Solution

  • You have to submit the Id value to server to bind to Model. You could bind like following when using Guid type.

    <EditForm Model="@Model" OnSubmit="@HandleSubmitted" FormName="CreateModel" Enhance>
        <input name="Model.Id" value="@Model.Id" type="hidden"/>
        <InputText @bind-Value="@Model.Name"></InputText>
        <button type="submit" > Submit</button>
    </EditForm>
    

    Another thing you could try is using "Form" instead of "EditForm", because it refresh the page when submit. So it will be possible to save id to session storage with some encryption and retrieve the data when page is refreshed.

    --update--

    You could try add a encryptedId field to submit. Calculate the encryptedId before submit using javascript, then check if it is matching after submit.

        public record TestModel
        {
            public Guid Id { get; set; } = Guid.NewGuid();
    
            public string encryptedId { get; set; }
    
            public string Name { get; set; } = "";
        }
    

    Trigger "addEncrypt" on submit button click, before the editform is submitted.

    @page "/"
    @using System.Security.Cryptography
    @using System.Text
    
    <EditForm id="myform" Model="@Model" OnSubmit="@HandleSubmitted" FormName="CreateModel" Enhance>
        <input name="Model.Id" value="@Model.Id" type="hidden"/>     
        <InputText @bind-Value="@Model.Name"></InputText>
        <button type="submit" onclick="addEncrypt()"> Submit</button>
    </EditForm>
    <script>
        function addEncrypt() {
            var base64String = window.btoa('@Model.Id');    //convert id to base64
            var x = document.createElement("input");    //add a hidden field for encrypedId
            var myform = document.getElementById("myform");
            x.setAttribute("name", "Model.encryptedId");
            x.setAttribute("value", base64String)
            myform.appendChild(x);
        }
       
    </script>
    
    @code {
        [SupplyParameterFromForm]
        private TestModel Model { get; set; } = new();
    
        protected override async Task OnInitializedAsync()
        {
    
            Console.WriteLine($"Initialized: {Model}");
        }
    
        private void HandleSubmitted(EditContext obj)
        {
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(Model.Id.ToString());
            var code = System.Convert.ToBase64String(plainTextBytes);
            if (code != Model.encryptedId)                     //if the id is modified, the code will not equal encryptedId
            {
                throw new Exception("Id not match");
            }
    
            Console.WriteLine($"Submitted: {Model}");
        }
    }
    

    I used base64 encryption here, you could change to some other algorithm, and put the alogrithm js file sperately. If user want to change Id, he has to find the alogrithm and calculate the encryptedId.