1

I have a view where set of Images relating to certain album will be edited to change their description and to make them cover photo.

EditImageViewModel.cs

public class EditImageViewModel
{
    public int ImageId{get;set;}
    public string ImageUrl{get;set;}
    public string ImageDes{get;set;}
    public bool IsCoverPic{get;set;}
}

From one of the controller ActionResult, I return model to view as return PartialView("_ImageEditView",model);.

model returned above is List<EditImageViewModel>

Now in the view I display it as below:

_ImageEditView.cshtml

@model IEnumerable<EditImageViewModel>
@using(Html.BeginForm("UpdateImage","Controller",FormMethod.Post))
{
    @foreach(var image in Model)
    {
        <img src="@image.ImageUrl"/>
        @Html.TextAreaFor(m=>image.ImageDes)
        @Html.RadioButtonFor(m=>image.IsCoverPic)
    }
    <button type="submit" class="update" value="Update"></button>
}

I have an ajax piece of code which calls ActionResult as below:

$('.update').on('click',function(e){
     e.preventDefault();
     var url=$(this).closest('form').attr('action');
     var formdata=$(this).closest('form').serialize();
     $.ajax({
        url:url,
        data:formdata,
        type:'POST',
        dataType:'JSON',
        success:function(resp){
        },
        error:function(resp){
        }
     })
});

My Controller ActionResult goes like this:

[HttpPost]
public ActionResult UpdateImage(List<EditImageViewModel> model)
{
    //actions to be performed
}

My problem here is no matter what, the model will always be null in the controller, when the post occurs. The formdata with have the data when checked in browser console. But it isn't get passed to controller method.

After going through few posts I learnt that it creates duplicate ids for multiple records in List when foreach is used. So I changed it to for loop as below:

@model IEnumerable<EditImageViewModel>
@using(Html.BeginForm("UpdateImage","Controller",FormMethod.Post))
{
    @for(int i=0;i<Model.Count();i++)
    {
        <img src="@Model[i].ImageUrl"/>
        @Html.TextAreaFor(m=>m[i].ImageDes)
        @Html.RadioButtonFor(m=>m[i].IsCoverPic)
    }
    <button type="submit" class="update" value="Update"></button>
}

But still the model is null when received in controller. I also tried using serializeArray instead of serialize, but it did not help much. I referred few posts like Post 1, Post 2 etc., but none of them solved this problem.

Ultimately, how can I pass this list of Model from ajax to controller?

Community
  • 1
  • 1
Guruprasad J Rao
  • 29,410
  • 14
  • 101
  • 200
  • You need to make the model `IList` in order to use indexing (or preferably use a custom `EditorTemplate` (refer [this answer](http://stackoverflow.com/questions/30094047/html-table-to-ado-net-datatable/30094943#30094943) for more detail). But your `RadioButtonFor()` makes no sense - did you mean `CheckBoxFor()`? –  Mar 30 '16 at 05:23
  • @StephenMuecke.. Was expecting you to drop in.. ;) However, its actually a radio button, since I want only one image as cover photo, and when one of the radio button is clicked others will be set disabled is what I had on mind and preferred radio button group. Isnt that right option? – Guruprasad J Rao Mar 30 '16 at 05:28
  • But you code will just generate a single radio button for each `EditImageViewModel` with no value attribute that would cause model binding to fail (it will submit a `null` value to a property which only accepts `true` or `false`). And since each one has a different group name, you can select all of them (but never unselect any of them) –  Mar 30 '16 at 05:31
  • @StephenMuecke Also, I've tried using `for` loop instead of `foreach`, which when inspected generates `id` and `name` like `[1].ImageDes`, `[2].ImageDes` etc., Isn't that quite right way to do. As far as `EditorTemplates` are considered we cannot have looping for it right? – Guruprasad J Rao Mar 30 '16 at 05:33
  • Using a `for` loop will work fine (but I assume its really `@model List` in the view) and that your indexers are starting at zero - `name="[0].ImageDes"`, `name="[1].ImageDes"` etc (note also that the `id` attributes have nothing to do with model binding - all that matters is the `name` attribute) –  Mar 30 '16 at 05:37
  • And if you use an `EditorTemplate`, then a loop is not required - in the main view, `@Html.EditorFor(m => m)` will generate the correct html based on each item in the model (and you model can be `IEnumerable` when using an `EditorTemplate`) –  Mar 30 '16 at 05:38
  • Exactly.. Its starts from `name=[0]...`. But still the `model` will be `null`. Would there be problem with `_form.serialize`. Was that the right option to send data? However, I will try once with editor template and let you know how it goes.. :) – Guruprasad J Rao Mar 30 '16 at 05:41
  • 1
    `$('form').serialize();` will work fine. Based on the code you have shown, the model will not be `null` (although only the `ImageDes` property of each item will be bound) –  Mar 30 '16 at 05:44
  • try $('form').serializeArray(); – REDEVI_ Mar 30 '16 at 06:36
  • 1
    @REDEVI_, OP has already stated he has tried `serializeArray()` - but `.serialize()` is the correct method to use anyway –  Mar 30 '16 at 06:41
  • @StephenMuecke.. Thanks much for your help.. With `IList` its working now.. :) – Guruprasad J Rao Mar 30 '16 at 13:51
  • @StephenMuecke.. Buddy you there? – Guruprasad J Rao Mar 30 '16 at 18:01
  • I am now. Glad you sorted it, but I hope you did not use the awful anti-mvc code in the answer you accepted. –  Mar 30 '16 at 23:06
  • @StephenMuecke.. No I did not use anti-mvc code.. :) But still facing some issues.. May be due to my project setup.. In test sample project its working fine for me, but not on the development project.. I dont find any other way to dig this more.. spent 2 days already.. – Guruprasad J Rao Mar 31 '16 at 04:51

1 Answers1

0

If you want to post a collection to the controller, you will need to add indexes to your name. Otherwise MVC does not know how to bind the model.

For example

@model IEnumerable<EditImageViewModel>
@using(Html.BeginForm("UpdateImage","Controller",FormMethod.Post))
{
    @for(int i=0;i<Model.Count();i++)
    {
        <img src="@Model[i].ImageUrl"/>
        <textarea name="@string.Format("ImageDes[{0}]", i)">@Model[i].imageDes</textarea>
      <input type="radio" name="@string.Format("IsCoverPic[{0}]", i)" value="@Model[i].IsCoverPic" > 

    }
    <button type="submit" class="update" value="Update"></button>
}
iBoonZ
  • 1,015
  • 11
  • 20
  • Yes.. But `model` has to be `IList` instead of `IEnumerable` and `@Html.TextAreaFor` and `@Html.RadioButtonFor` instead of above 2 `html` controls.. Please update the same.. – Guruprasad J Rao Mar 30 '16 at 13:51
  • 1
    I did not use the helper methods to make the point that an index is required :). You can still use the helper methods if you want – iBoonZ Mar 30 '16 at 13:58