Search code examples
asp.netvalidationblazorrazor-pages

Two-Way Binding is not working in Blazor Component


I am working on a ASP.NET Blazor App in .NET 8. A model class SubjectName is bound with this SubjectForm.razor blazor component.

@inject ISubjectNameRepo Repo

@if(Subject is not null)
{
    <div class="row justify-content-center">
        <div class="col-6">
            <EditForm Enhance="true" method="post" FormName="SubjectForm" Model="Subject" OnValidSubmit="AddOrEditSubject" OnInvalidSubmit="InvSubmit" autocomplete="off">
                <DataAnnotationsValidator/>
                <h3>@Title</h3>
                @if (IsEditMode)
                {
                    <input type="hidden" name="Subject.Id" value="@Subject.Id"/>
                }
                <div class="mb-3">
                    <label for="subname" class="form-label">
                        Subject Name
                    </label>
                    <InputText id="subname" @bind-Value="Subject.SubName" class="form-control shadow-none" />
                </div>
                <div class="mb-3">
                    <button type="submit" class="btn btn-primary shadow-none">
                        Submit
                    </button>
                    <a href="/subjects/list" class="btn btn-secondary shadow-none ms-3">
                        Back to List
                    </a>
                </div>
                <ValidationSummary />
            </EditForm>
        </div>
    </div>
}
else
{
    <div>
        The subject is null ghere
    </div>
}

@code {
    [Parameter]
    public bool IsEditMode { get; set; } = false;

    [Parameter]
    public int Id { get; set; }

    [SupplyParameterFromForm]
    public SubjectName? Subject { get; set; }

    public string Title => IsEditMode ? $"Edit Subject {Id}" : "Add New Subject";

    [Parameter]
    public EventCallback<SubjectName> OnValidSubjectNameSubmit { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (IsEditMode)
        {
            Subject ??= await Repo.GetByIdAsync(Id);
        }
        else
        {
            Subject ??= new();
        }
    }

    private async Task AddOrEditSubject()
    {
        await OnValidSubjectNameSubmit.InvokeAsync(Subject);
    }

    private async Task InvSubmit()
    {
        
    }

}

Below is the code of SubjectName model class

    public class SubjectName
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id {  get; set; }

        [Required]
        [StringLength(50)]
        [Display(Name = "Subject Name")]
        public string? SubName { get; set; }

        public virtual ICollection<EvaluationTweak>? EvaluationTweaks { get; set; }
    }

When I run this app, navigate to this page, enter the data and then click "Submit" button, then the contents of the InputText vanish and it display a messahe Subject Name field is required

I am unable to understand whats wronf there?


Solution

  • Weird. I know the solution, but it is rather a fix. The true reason stays hidden for me.

    The outer layer of the issue is that the Subject property is null when submitting the form. It should have the values from the form. The form is submitted correctly.

    I will show only the relevant part:

    Model you are binding to form:

    public class SubjectName
    {
      public string? SubName { get; set; }
      public virtual ICollection<EvaluationTweak>? EvaluationTweaks { get; set; }
    }
    

    Problem here are the EvaluationTweaks, if you remove them, everything work as expected. Now the reason why. Check the EvaluationTweak class:

    public class EvaluationTweak
    {
        [Required]
        public virtual SubjectName Subject { get; set; } = new();
    }
    

    When you remove the Subject property, it will also work... Note that you can replace it with int SubjectId so you keep the reference there. Also note that removing Subject property from EvaluationTweak will not change the db schema.

    But why is that a problem? I have no idea, my guess is - it has somehow problem with serialization? It is perceived as circular reference maybe?

    Would be nice to investigate further.