0

I am trying to bind custom form fields to an existing ViewModel. I have a ViewModel that looks similar to this

public class BasicProfileViewModel {
   public Employee Employee { get; set; }
}

I am loading my form using the above but what i also have is an CustomFormFieldComponent which add custom form fields

Which is based of some of the values in the Employee model

 @await Component.InvokeAsync("CustomFormFields", new { Model.Level, Model.Employee.CompanyId, Model.Employee.EmployeeId })

The component is really simple just return an list that i retrieve from the DB

@model List<CustomFormField>

@for (int i = 0; i < Model.Count(); i++)
{
  <div class="row">

    <div class="form-group form-material col-md-6">

      @Html.LabelForModel(Model[i].FieldLabel, new { @class = "form-control-label" })
      @if (Model[i].IsMandatory)
      {
        <span>*</span>
      }

      @if (Model[i].DropdownValues.Any())
      {
        var values = new SelectList(Model[i].DropdownValues);
        <select asp-items="@values" class="form-control"></select>
      }
      else
      {
        @Html.TextBoxFor(m => Model[i].FieldValue, new { @class = "form-control" })
      }
    </div>

  </div>
}

How can i get those custom form fields part of my ViewModel?

I am thinking of doing an custom model binder, Employee is an complex object so setting the values using reflection is not as straight forward. And i have not even started with the custom form fields.

public class RequestBasicProfile
    {
        public Employee Employee { get; set; } = new Employee();
        public List<CustomFormField> FormFields { get; set; }
    }
    public class RequestBasicProfileBinder : IModelBinder
        {
            public Task BindModelAsync(ModelBindingContext bindingContext)
            {
                if (bindingContext == null)
                {
                    throw new ArgumentNullException(nameof(bindingContext));
                }

                var result = new RequestBasicProfile();

                foreach (var item in bindingContext.HttpContext.Request.Form)
                {

                    var propertyInfo = result.Employee.GetType().GetProperty(item.Key.Replace("Employee.", ""));
                    if (propertyInfo != null)
                    {
                        var value = item.Value[0];
                        propertyInfo.SetValue(result.Employee, Convert.ChangeType(value, propertyInfo.PropertyType), null);
                    }
                }

                bindingContext.Result = ModelBindingResult.Success(result);
                return Task.CompletedTask;
            }
        }
R4nc1d
  • 2,923
  • 3
  • 24
  • 44

1 Answers1

0

So turns out i was trying to over complicated things. Its a lot easier when I realized how MVC handles data binding. So i ended up just doing the following

Sudo Code

Model

public classs MyViewModel
{
  public string EmpFirstName { get; set; }
  public string EmpMiddleName { get; set; }

  // custom form fields
  public List<FormFieldDto> Fields { get; set; }
}

Razor

@Html.EditorFor(m => m.Employee.EmpFirstName)
@Html.EditorFor(m => m.Employee.EmpMiddleName)
@for (var i = 0; i < Model.Count; i++){
    <input type="text" name="@Model[i].Id" />
}

trick is to make sure that the name that gets generated is matches your model

name="MyViewModel.EmpFirstName" // will get generated correctly
name="MyViewModel.EmpMiddleName" // will get generated correctly
name="MyViewModel.Fields[0].FieldValue" //might have to manually change the name depending on how your object looks
name="MyViewModel.Fields[2].FieldValue" //might have to manually change the name depending on how your object looks
R4nc1d
  • 2,923
  • 3
  • 24
  • 44