2

I am new to ASP .NET MVC. My problem is - I want to 'POST' a collection of the items , so that controller can process it. My model is collection of -

public class CheckedRentalProperty
{
   public bool IsSelected { get; set; }
   public int Id { get; set; }
   public String Address { get; set; }
}

My controller is defined like this -

public class RentalPropertiesController : Controller
{
    public ActionResult Index()
    {
        List<CheckedRentalProperty> checkHsList = new List<CheckedRentalProperty>();
        // Fill the list
        return View(checkHsList);
    }

    [HttpPost]
    public ActionResult Save(IEnumerable<CheckedRentalProperty> checkHsList)
    {
        // why checkHsList is coming as null ??
    }
}

And the view is like this -

@model IEnumerable<XXX.Models.CheckedRentalProperty>

@using (Html.BeginForm("Save", "RentalProperties", FormMethod.Post))
{
    <div class="form-horizontal">
        <div class="form-group">
            <table class="table">
                <tr>
                    <th>

                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Address)
                    </th>

                </tr>

                @foreach (var item in Model)
                {
                    <tr>
                        <td>@Html.CheckBoxFor(modelItem => item.IsSelected)</td>

                        <td>
                            @Html.DisplayFor(modelItem => item.Address)
                        </td>
                    </tr>
                }
            </table>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

My expectations was - when I hit the "Save" button, the Model, which is IEnumerable<CheckedRentalProperty> item, will be passed to the Save() action of the controller. However, I find that the passed parameter is "null" all the time. What am I missing?

adiga
  • 34,372
  • 9
  • 61
  • 83
user1748546
  • 97
  • 2
  • 11
  • This question is not a duplicate. Please read Orel's answer and check out the differences – user1748546 Sep 10 '17 at 16:46
  • It is a duplicate. Read it carefully because it also explains exactly why your code does not work and how to solve it using either a `for` loop or `EditorTemplate` (and `Orel's` answer just repeats what is in the duplicate anyway!) And Orel's` answer contains nonsense such as _IEnumerable are not too friendly_ which is just plain wrong and it is completely unnecessary to create another view model to wrap your existing one –  Sep 10 '17 at 22:37

1 Answers1

4

Model that are solely IEnumerable are not too friendly as MVC Model.

There are many issues arise here, but in a nutshell, MVC webform bindings needs form name requests to be send in the following format: PropertyName[Index].Property Which is not the case at your example.

It is a good design practice, to create a wrapping ViewModel which will hold the properties you need for the given controller + pages.

ViewModel

public class RentalPropertiesViewModel
{
    public List<CheckedRentalProperty> CheckedRentalProperties { get; set; }
}

Controller: Next we will want to use this ViewModel in our controller.

public ActionResult Index()
{
    var checkHsList = new List<CheckedRentalProperty>();
    checkHsList.Add(new CheckedRentalProperty { Id = 1, Address = "Address1", IsSelected = true });
    checkHsList.Add(new CheckedRentalProperty { Id = 2, Address = "Address2", IsSelected = false });
    checkHsList.Add(new CheckedRentalProperty { Id = 3, Address = "Address3", IsSelected = true });

    var model = new RentalPropertiesViewModel
    {
        CheckedRentalProperties = checkHsList
    };
    return View(model);
}

[HttpPost]
public ActionResult Save(RentalPropertiesViewModel model)
{
    // why checkHsList is coming as null ??

    return null;
}

View: Now in our view we should set the Model as the new ViewModel type we created.

@model TestBindings.Models.RentalPropertiesViewModel

And our view form should be something like:

<table class="table">
    <tr>
        <th>
            Is Selected
        </th>
        <th>
            Address
        </th>

    </tr>
    @for (int i = 0; i < Model.CheckedRentalProperties.Count(); i++)
    {
        <tr>
            @Html.HiddenFor(model => model.CheckedRentalProperties[i].Id);
            <td>@Html.CheckBoxFor(model => model.CheckedRentalProperties[i].IsSelected)</td>

            <td>@Html.TextBoxFor(model => model.CheckedRentalProperties[i].Address)</td>
        </tr>
    }
</table>

I've use the following format model => model.CheckedRentalProperties[i].IsSelected and now MVC InputExtensions will bind it correctly. e.g: CheckedRentalProperties[0].IsSelected

Important Note: Notice i'm passing Id property as hidden, so MVC Binder will know to set the Id to the correct item.

Orel Eraki
  • 11,940
  • 3
  • 28
  • 36