Search code examples
c#formsasp.net-coreblazorblazor-webassembly

Why my properties in my blazor wasm always become null when i submit the form


This is my code:

From what I can see, I am creating a new instance of the class, binding each attribute to the class in the form, then converting to JSON and printing. What am I missing?

@inject IIdentityService IdentityService;
@inject ErrorService ErrorService;

<div class=" w-full flex justify-center text-main  ">
    <div class="flex w-full">
        <div class="w-1/2 flex">
            <div class="w-full h-[100vh] bg-cover bg-center"
                 style="background-image: url('./images/identity/login.jpg')">
            </div>
        </div>
        <div class="bg-white p-8 w-1/2 rounded-lg pt-[10%] px-[5%]">
            <EditForm Model="RegisterModel" OnValidSubmit="HandleValidSubmit" FormName="RegisterForm">
                <DataAnnotationsValidator/>
                <ValidationSummary/>

                <!-- First Name and Last Name -->
                <div class="mb-4 flex justify-between">
                    <InputText
                        @bind-Value="FirstName"
                        placeholder="First Name"
                        class="form-control mt-1 block w-[48%] px-6 py-4 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                        required/>
                    <ValidationMessage For="@(() => FirstName)"/>

                    <InputText
                        @bind-Value="LastName"
                        placeholder="Last Name"
                        class="form-control mt-1 block w-[48%] px-6 py-4 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                        required/>
                    <ValidationMessage For="@(() => LastName)"/>
                </div>

                <!-- Phone Number -->
                <div class="mb-4">
                    <InputText
                        @bind-Value="RegisterModel.PhoneNumber"
                        placeholder="Phone Number"
                        class="form-control mt-1 block w-full px-6 py-4 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                        required/>
                    <ValidationMessage For="@(() => RegisterModel.PhoneNumber)"/>
                </div>

                <!-- Email -->
                <div class="mb-4">
                    <InputText
                        @bind-Value="RegisterModel.Email"
                        type="email"
                        placeholder="Email"
                        class="form-control mt-1 block w-full px-6 py-4 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                        required/>
                    <ValidationMessage For="@(() => RegisterModel.Email)"/>
                </div>

                <!-- Password -->
                <div class="mb-4">
                    <InputText
                        @bind-Value="RegisterModel.Password"
                        type="password"
                        placeholder="Password"
                        class="form-control mt-1 block w-full px-6 py-4 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
                        required/>
                    <ValidationMessage For="@(() => RegisterModel.Password)"/>
                </div>

                <!-- Register Button -->
                <div>
                    <button type="submit"
                            class="w-full inline-flex justify-center py-2 px-4 border border-transparent shadow-sm font-medium rounded-sm text-white bg-[#00205c] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 text-xl transition-all ease-out">
                        Register
                    </button>
                </div>
            </EditForm>

            <div class="w-full flex justify-between mt-3  ">
                <button class="hover:underline" type="button">
                    <NavLink href="/login">
                        Already Have An Account?
                    </NavLink>
                </button>
                <button class="hover:underline " type="submit">
                    <a href="">
                        Forgot Password?
                    </a>
                </button>
            </div>
        </div>
    </div>
</div>

@code {

    [SupplyParameterFromForm(FormName = "RegisterForm")]
    private RegisterFormModel RegisterModel { get; set; } = RegisterFormModel.Empty;

    private string FirstName { get; set; } = string.Empty;
    private string LastName { get; set; } = string.Empty;

    private async Task HandleValidSubmit()
    {
        RegisterModel.Username = $"{FirstName} {LastName}";

        var result = await IdentityService.Register(RegisterModel);

        if (result.IsFailure)
        {
            ErrorService.SetErrorMessage(result.ErrorTypes);
        }
    }

The Problem is when i bind the form value to the p tag, it still display my input value, but when i send the submit, it become null or i should say default value.

I also check it in the server and try to change the default value of the DTOs in the backend, so i can know when i send, the bind value in my blazor form is empty or null.

What can i do? the @rendermode dont work in blazor wasm or i just missing something

Edit: my problem also happen with all of property not only firstname/lastname

Edit2: My service

public class IdentityService(
    IHttpClientFactory factory,
    string baseUrl = nameof(IdentityService),
    string endpoint = "api/User"
) : BaseApiService(factory, baseUrl, endpoint), IIdentityService
{
    public async Task<Result> Register(RegisterFormModel registerModel)
    {
        var content = SerializeItem(registerModel);
        var result = await Client.PostAsJsonAsync($"{Endpoint}/Register", content);
        return await HandleResponse(result);
    }

    public async Task<Result<LoginResponse>> Login(LoginFormModel loginModel)
    {
        var content = SerializeItem(loginModel);
        var result = await Client.PostAsJsonAsync($"{Endpoint}/Login", content);
        return await HandleResponse<LoginResponse>(result);
    }
}
    protected static StringContent SerializeItem<TItem>(TItem item)
        where TItem : class
    {
        return new StringContent(JsonSerializer.Serialize(item), Encoding.UTF8, "application/json");
    }

My model:

public class RegisterFormModel
{
    public string? Email { get; set; }
    public string? Username { get; set; }
    public string? Password { get; set; }
    public string? PhoneNumber { get; set; }

    public static RegisterFormModel Empty =>
        new()
        {
            Email = default,
            Username = default,
            Password = default,
            PhoneNumber = default,
        };
}


Solution

  • This issue is more likely happened in SSR. In SSR the input field has to be in the editform model.

        public class RegisterFormModel
        {
            public string? FirstName { get; set; }
            public string? LastName { get; set; }
    ...
    
            var content = SerializeItem(registerModel);
            var result = await Client.PostAsync($"{Endpoint}/Register", content);
    
     <InputText @bind-Value="RegisterModel.FirstName" ...
    ...
     <InputText @bind-Value="RegisterModel.LastName" ...
    

    And need use following pattern to initilize the model

        [SupplyParameterFromForm(FormName = "RegisterForm")]
        private RegisterFormModel RegisterModel { get; set; }
    
        protected override void OnInitialized() => RegisterModel ??= new RegisterFormModel();
    

    -------------------update--------------------
    Inside this page, add a testValue to check if the Value dispaly changed correctly.

    @testValue
    
    @code {
        private string testValue { get; set; } = "test";
    ...
        private async Task HandleValidSubmit()
        {
    
            testValue = FirstName + RegisterModel.PhoneNumber;
    

    ------------------------update2----------------------
    The PostAsJsonAsync method doesn't match the [FromForm] attribute which accept MultipartFormDataContent. There are 2 ways to do this post.

    1. Delete [FromForm]
    public async Task<IActionResult> Register(Register.Commond commond)
    

    then use PostAsync instead.

                var content = SerializeItem(registerModel);
                var result = await Client.PostAsync($"{Endpoint}/Register", content);
    
    1. Still use FromForm (in case you cannot change the api)
                var content = new MultipartFormDataContent();
                var stringContent = new StringContent(registerModel.Username, Encoding.UTF8);
                content.Add(stringContent, "UserName");
                stringContent = new StringContent(registerModel.Email, Encoding.UTF8);
                content.Add(stringContent, "Email");
                stringContent = new StringContent(registerModel.PhoneNumber, Encoding.UTF8);
                content.Add(stringContent, "PhoneNumber");
                stringContent = new StringContent(registerModel.Password, Encoding.UTF8);
                content.Add(stringContent, "Password");
    
                var result = await client.PostAsync($"{Endpoint}/Register", content);