locked
Modelbinding in Form with List RRS feed

  • Question

  • User1441549460 posted

    I've a controller with two actions "Index" and "Start". Index creates A BulkEditViewModel which contains a list of PersonModel. Then I call a View and pass the ViewModel. The View has a form with a table which lists all PersonModel and a Button which submit the Form to Start Action.

    Model and ViewModel:

    public class PersonModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool ShouldBeUpdate { get; set; }
    }
    
    public class BulkEditViewModel
    {
        public List<PersonModel> PersonModels { get; set; } = new List<PersonModel>();
    }

    Controller:

    public class HomeController : Controller
    {
    public static BulkEditViewModel CreateBulkEditViewModel()
    {
    BulkEditViewModel bulkEditViewModel = new BulkEditViewModel();
    bulkEditViewModel.PersonModels.Add(new PersonModel()
    {
    Id = 1,
    Name = "Christian"
    });
    bulkEditViewModel.PersonModels.Add(new PersonModel()
    {
    Id = 2,
    Name = "Tobias"
    });
    bulkEditViewModel.PersonModels.Add(new PersonModel()
    {
    Id = 3,
    Name = "Tina"
    });
    bulkEditViewModel.PersonModels.Add(new PersonModel()
    {
    Id = 4,
    Name = "Michael"
    });
    return bulkEditViewModel;
    }

    public ActionResult Index()
    {
    return View(CreateBulkEditViewModel());
    }

    public ActionResult Start(BulkEditViewModel bulkEditViewModel)
    {
    bulkEditViewModel.PersonModels = bulkEditViewModel.PersonModels.Where(p => p.Id != 3).ToList();
    return View("Index",bulkEditViewModel);
    }
    }

    View:

    @model TestFormModelBinding.Models.BulkEditViewModel
    @{
        ViewBag.Title = "Home Page";
    }
    
    @using (Html.BeginForm("Start", "Home", FormMethod.Post))  
    {  
        <button type="submit" class="btn btn-primary">Start</button>
        <table>
            @for(var idx = 0; idx < Model.PersonModels.Count; idx++)
            {
                <tr>
                    <td>
                        @Html.EditorFor(beVM => beVM.PersonModels[idx].ShouldBeUpdate)
                    </td>
                    <td>
                        @Html.EditorFor(beVM => beVM.PersonModels[idx].Id)
                    </td>
                    <td>
                        @Html.EditorFor(beVM => beVM.PersonModels[idx].Name)
                    </td>
                </tr>
            }
        </table>
    }

    The Start action receives the ViewModel with the list of Models. Then the action removes one ItemModel (with a specific id) from the list. And pass the changed ViewModel already back to the View. And here is the problem. Now the view at the browser removes the last line (another Item) not the item with the specific id. If I post the form again, the next last item will remove. And so on, until two items are in the list.

    But the data which sending to the view are always correct. So I think, their is a problem at the client site and the modelbinding between view and action?

    I've create a test project and pushed it to Github: https://github.com/Riedi2070/mvcModelBinding.git with the problem.

    Has someone any idea, what I do wrong?  

    Thursday, January 21, 2021 7:02 AM

Answers

  • User1312693872 posted

    Hi,Riedi2070

    Riedi2070

    If I post the form again, the next last item will remove. And so on, until two items are in the list.

    Add ModelState.Clear() will solve this problem:

    ModelState.Clear() is used to clear errors but it is also used to force the MVC engine to rebuild the model to be passed to your View.

    When you put back the parameter that contain your model to the view to be returned, it will use the value of the ModelState. In your code,

    the model parameter has been filtered but it won't be saved to modelstate unless you clear it first.

    public ActionResult Start(BulkEditViewModel bulkEditViewModel)
            {
                ModelState.Clear();
                bulkEditViewModel.PersonModels = bulkEditViewModel.PersonModels.Where(p => p.Id != 3).ToList();
                return View("Index", bulkEditViewModel);
            }

    Best Regards,

    Jerry Cai

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 22, 2021 2:34 AM

All replies

  • User1312693872 posted

    Hi,Riedi2070

    Riedi2070

    If I post the form again, the next last item will remove. And so on, until two items are in the list.

    Add ModelState.Clear() will solve this problem:

    ModelState.Clear() is used to clear errors but it is also used to force the MVC engine to rebuild the model to be passed to your View.

    When you put back the parameter that contain your model to the view to be returned, it will use the value of the ModelState. In your code,

    the model parameter has been filtered but it won't be saved to modelstate unless you clear it first.

    public ActionResult Start(BulkEditViewModel bulkEditViewModel)
            {
                ModelState.Clear();
                bulkEditViewModel.PersonModels = bulkEditViewModel.PersonModels.Where(p => p.Id != 3).ToList();
                return View("Index", bulkEditViewModel);
            }

    Best Regards,

    Jerry Cai

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 22, 2021 2:34 AM
  • User1441549460 posted

    Yeah!!! Thank u very much. With ModelState.Clear() it works perfectly!

    Wednesday, February 3, 2021 6:25 AM