1

TL;DR: I just need to figure out why i can't serialize() the partial view's parent div and receive the model. Coding this out manually will take forever as there are many parent partials I'd have to use the same logic on.

More info: I've tried EditorTemplate for binding purposes, but unfortunately there is no easy way to use them as variable lists as far as I've searched.

Begin:

Models

public class ContactModel 
{                
    public List<ContactDetailModel> Contacts { get; set; }
    ....


public class ContactDetailModel
{
    public ContactView Contact { get; set; }
    public PhoneModel PhoneModel { get; set; }
    ...

public class PhoneModel
{
    public int ContactId { get; set; }
    public int IsPrimaryPhoneNumberId { get; set; }
    public List<PhoneView> Phones { get; set; }
    public List<EmailPhoneTypeView> EmailPhoneTypes { get; set; }
...

To select & post inputs from just this partial view, I've implemented a variable class, and its relative template prefix to keep the MVC binding for the partial view.

@{
 var phoneClass = "phone" + @Model.Contacts[index].Contact.ContactId;
 var phoneTemplatePrefix = "Contacts[" + index + "].PhoneModel";
 }

This is ran inside of a loop, increasing indexes as needed to keep binding.

<div class="@phoneClass">
     @Html.Partial("_ContactPhone", Model.Contacts[index].PhoneModel, new ViewDataDictionary() { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = phoneTemplatePrefix } })
</div>

The partial I'm attempting to post. (Strongly Typed partial for PhoneModel)

@{var addNavigationClass = "AddContactPhone" + Model.ContactId;}

for (var phoneIndex = 0; phoneIndex < Model.Phones.Count(); phoneIndex++)
{
    @Html.HiddenFor(model => model.Phones[phoneIndex].ContactPhoneId)
    @Html.DropDownListFor...
    @Html.TextBoxFor(model => model.Phones[phoneIndex].PhoneNumber)
    <a href="#" class="removeMemberPhone">Trash</a>
    @Html.RadioButtonFor(model => model.IsPrimaryPhoneNumberId, Model.Phones[phoneIndex].ContactPhoneId) Primary</label>
}

Inside the View's click function

var model = $('.phone' + '@Model.ContactId' + ' :input').serialize();
console.log('model', model);

$.ajax({
   url: '/Contact/AddPhone',
   type: 'POST',
   data: model,
   success: function (data) {
     console.log(data.length);
    }
    ....

The log's output

model Contacts%5B1%5D.PhoneModel.Phones%5B0%5D.ContactPhoneId=3907&Contacts%5B1%5D.PhoneModel.Phones%5B0%5D.EmailPhoneTypeId=1&..........

My model never has any values in my controller (I've abbreviated ContactPhoneModel to PhoneModel in the above code)...

enter image description here

Pakk
  • 1,299
  • 2
  • 18
  • 36
  • You should be using `EditorTemplates` not partials to ensure your form controls are correctly named. Your view appears to be based on `ContactModel` but you posting back `ContactPhoneModel` so the names of your form data do not match the names of your model properties so it cannot be bound –  Oct 09 '15 at 04:44
  • You also have a property `public PhoneModel PhoneModel` but I assume that's supposed to be `public ContactPhoneModel PhoneModel`? –  Oct 09 '15 at 04:46
  • @StephenMuecke As i've stated above `EditorTemplates` won't work for the variable type list I'm implementing with this view. Also I'm aware of the Model name differences I've tried to abbreviate them, and wil edit the post to fix them. – Pakk Oct 09 '15 at 04:47
  • This certainly does not :) And you should be using an `EditorTemplate`. The model in your view is `ContactPhoneModel` which mean your form data needs to be `ContactPhoneId=3907` not `Contacts[1].PhoneModel.Phones[0].ContactPhoneId=3907` which would never work anyway since indexers must start at zero and be consecutive. –  Oct 09 '15 at 04:51
  • @StephenMuecke I'm just saying I've had no luck finding a way to update an editor template with an additional `phoneModel` (recommended on many of the stack's posts, is the partial view). The log returned `Contacts[1]` as it is the second contact that i clicked on to add the `phoneModel` too. – Pakk Oct 09 '15 at 04:56
  • All you doing is writing extra code to do what an `EditorTemplate` would have done anyway. The name/values pairs that you post must match your model, which it currently does not, so bind will fail and all the values will be their default. Your form data needs to be `ContactPhoneId=3907&IsPrimaryPhoneNumberId=true&EmailPhoneTypes[0].someProperty=someValue&EmailPhoneTypes[1].someProperty=anotherValue&...etc` –  Oct 09 '15 at 05:01
  • You either need to (1) make the method parameter `ContactModel` and use `for` loops with hidden input for each indexer (or use the `BeginCollectionItem` helper) or (2) manually generate the form data with the correct names –  Oct 09 '15 at 05:03
  • @StephenMuecke Ok Editor Template aside, as it will work for binding but NOT for variable list adding and deleting from the view. I'll look into the `BeginCollectionItem` as well as your (1) solution, i don't remember exactly why, but i thought because of the variable list solution as well you were supposed to run the for i ... loop. - Thanks for the replies – Pakk Oct 09 '15 at 05:07
  • If you want to understand how to do the _adding and deleting from the view_, look at the answers [here](http://stackoverflow.com/questions/29161481/post-a-form-array-without-successful/29161796#29161796) and [here](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308). But that's not what you code appears to be doing anyway because your only posting back one object at a time. –  Oct 09 '15 at 05:10

2 Answers2

1

This is more pseudo code than actual code, you will need to tidy this up a bit.

Since you aren't posting back the full page, it seems overkill to jump through the Mvc model binders hoops when binding to a collection. If it was me doing this, I'd alter your click handler to the following:

var model = {};
$('.phone' + '@Model.ContactId' + ' :input').each(function(){
    model[/[^\.]+$/.exec($(this).prop("name"))[0]] = $(this).val();
};
console.log('model', model);

$.ajax({
   url: '/Contact/AddPhone',
   type: 'POST',
   data: model,
   contentType: "application/json; charset=UTF-8",
   success: function (data) {
      console.log(data.length);
    }

I would look at using editortemplates as mentioned in the comments, they will take some of the pain away of managing indexes and the like

Slicksim
  • 7,054
  • 28
  • 32
  • That's actually really cool, and I've tested this out, and 'modified' works as far as only this partial goes, however binding any parenting 'partial' model will get increasingly harder just managing the code to grab the data, using this methodology. I appreciate the time you took for the reply and the code. – Pakk Oct 10 '15 at 03:52
0

Here's a bit of (unrefined, and early) code that I've written, that will take properly formatted serializeArray() data and re base the arrays for a good post to the MVC controller.

Top 2 lines will show how to call it once in your project.

Pakk
  • 1,299
  • 2
  • 18
  • 36