3

I have a view that works like a form so it loads a bunch of drop down and select from a FormViewModel class that is return from the Controllers in the ActionResult Index. Once all the data is enter into the form I use the Html.BeginForm("Create" to call the save method. The ActionResult Create(RequestForm model). So the question is I wanted to use two models, one to load and one to save the data. What I am doing now is have the object in from the FormViewModel and the RequestForm model.

Is there a better way to do this?

View:

@model  FormViewModel
@using (Html.BeginForm("Create", "RequestForm", FormMethod.Post))
{
    @Html.ValidationSummary(true);
     <div class="k-content">
     <h4>* Country</h4>
     @Html.TextBoxFor(x => x.CountryId, new { id = "ddlCountry", @class = "form-control" })
     @Html.ValidationMessageFor(model => model.CountryId)
     </div>
}

FormViewModel

[Required]
public int? CountryId { get; set; }

public List<CountryModel> ListOfCountries { get; set; }

RequestForm Model

public class RequestForm
{
    [Required]
    public int? CountryId { get; set; }
}
    

Controllers

public ActionResult Create(RequestForm model)
{
    var FormInfo = FormCreate(model);
    return View("");
}
Jackdaw
  • 7,626
  • 5
  • 15
  • 33
  • 2
    Could you please show us what do you have and what do you want and what is the error? – Serge Dec 22 '20 at 13:18
  • have you looked into other SO answers? [this one](https://stackoverflow.com/a/4765113/12339804) looks very promising – timur Dec 27 '20 at 05:29

3 Answers3

5

You can nested view models and only submit the one you want on form post.

For example:

public class FormViewModel
{
    public IEnumerable<CountryViewModel> AvailableCountries { get; set; }
    public CreateRequestViewModel Data { get; set; }
}

public class CountryViewModel
{
    public int CountryId { get; set; }
    public string CountryName { get; set; }
}

public class CreateRequestViewModel
{
    [Required]
    public int SelectedCountryId { get; set; }
}

I just made up the names but hope you will get the idea.


Then on the view, you can set it up like following:

@model FormViewModel
@using (Html.BeginForm("Create", "RequestForm", FormMethod.Post))
{
    @Html.ValidationSummary(true);
    
    <div class="k-content">
        <h4>* Country</h4>
        @Html.DropDownListFor(
            x => x.Data.SelectedCountryId,
            new SelectList(Model.AvailableCountries, "CountryId", "CountryName"),
            new { id = "ddlCountry", @class = "form-control" }
        )
        @Html.ValidationMessageFor(x => x.Data.SelectedCountryId)
    </div>
}

Again, I wrote it by hand so there might be compilation error. But the idea is that you use DropDownListFor(), instead of TextBoxFor(), to generate a drop down with options generated by the AvailableCountries list you populate.

And you only need to put [Required] on the view model you're posting data to, because that's validating user's data. You don't need to put that on the CountryViewModel because you're populating the list yourself.


Finally, there is one more important thing you need to pay attention, and that is the parameter name on the method the form is posting to:

public ActionResult Create(CreateRequestViewModel data)
{
    ...
}

The name of the parameter has to match the one you declare in the outer model, FormViewModel.

David Liang
  • 20,385
  • 6
  • 44
  • 70
2

You can use 2 models (1 to load the form contents, the other to send the selections, inputs to server). Model Binding does the job for you, i.e. it converts the inputs' values to the Model class (RequestForm) when it reaches the Action method (Create) of the Controller class

According to the information you shared, this approach seems OK

Muhammad Waqas Iqbal
  • 3,244
  • 1
  • 20
  • 9
  • thanks @muhammad-waqas-iqbal do you need the objects in both models ? – Jefferson Dec 28 '20 at 12:58
  • @Jefferson, well, one model (FormViewModel) will be used for populating the country list in your example while the other (RequestForm) will be used as a parameter to fetch the CountryId, selected by the user when the form is submitted – Muhammad Waqas Iqbal Dec 29 '20 at 05:44
2

You can use one view model to load the data in the view and use it to post the data to server then on server you could convert the model to the new entity which will be saved to the database.

@model  FormViewModel
@using (Html.BeginForm("Create", "RequestForm", FormMethod.Post))
{
     @Html.ValidationSummary(true);
      <div class="k-content">
      <h4>* Country</h4>
      @Html.TextBoxFor(x => x.CountryId, new { id = "ddlCountry", @class = "form-control" })
      @Html.ValidationMessageFor(model => model.CountryId)
       </div>
}

FormViewModel

 [Required]
 public int? CountryId { get; set; }

 public List<CountryModel> ListOfCountries { get; set; }

DB entity

 public int? CountryId { get; set; }

Controller

 public ActionResult Create()
 {
        // you can use this method in edit mode too by loading the data to model 
        FormViewModel model = new FormViewModel() ;
         return View(model) ;
  }

 [HttpPost]
  public ActionResult Create(FormViewModel model)
  {
        //convert the model to the DB entity may be you can use automapper 

          var entity = converted model;
         // save to DB 

         return View(model) ;
  }

I tried to illustrate the code as much as I can

Mohamed Salah
  • 959
  • 10
  • 40