1

I have a form in ASP.Net MVC where contacts can be one or many

for example: a project can have one or many contact persons. I created my form it it has a div with two textboxes like blow:

<div class="form-group">
        <label class="control-label col-md-2">Focal Points</label>
        <div class="col-md-10">
            <div class="input_fields_wrap">
                <button class="add_field_button btn">Add More Focal Points</button>

                <div style="margin:4px;">
                  Name: <input type="text" name="contact_name[]" />

                  Phone <input type="text" name="contact_phone[]" />
              </div>
            </div>
            </div>
        </div>

when I post my form how can I get the array of contact names and phones as a single array of key and values.

script that generates textboxes dynamically:

<script>

$(document).ready(function () {
    var max_fields = 10; //maximum input boxes allowed
    var wrapper = $(".input_fields_wrap"); //Fields wrapper
    var add_button = $(".add_field_button"); //Add button ID

    var x = 1; //initlal text box count
    $(add_button).click(function (e) { //on add input button click
        e.preventDefault();
        if (x < max_fields) { //max input box allowed
            x++; //text box increment
            $(wrapper).append('<div style="margin:4px;">Name: <input type="text" name="contact_name[]"/> Phone <input type="text" name="contact_phone[]" /><a href="#" class="remove_field">Remove</a></div>'); //add input box
        }
    });

    $(wrapper).on("click", ".remove_field", function (e) { //user click on remove text
        e.preventDefault(); $(this).parent('div').remove(); x--;
    })
});

Benafsh Yalda
  • 396
  • 1
  • 5
  • 13
  • Your misunderstanding some basics of Http and MVC. In order for the value of ``to bind to a property, that property would need to be named `contact_name[]` which is illegal. –  Dec 07 '14 at 07:01
  • Than how can I add many contacts – Benafsh Yalda Dec 07 '14 at 07:08
  • I have button beside my textboxes when i click that it will create the same two textboxes – Benafsh Yalda Dec 07 '14 at 07:14
  • that is why i used array to store many records and post as one – Benafsh Yalda Dec 07 '14 at 07:14
  • [This answer](http://stackoverflow.com/questions/24026374/adding-another-pet-to-a-model-form/24027152#24027152) might point you in the right direction - you need to have correctly named controls with indexers to post back to a collection –  Dec 07 '14 at 07:20
  • @Stephen Muecke Do you know if its possible to use numberless indexes such as "red", "blue" or indeed a whole guid? – Isaac Bolinger Dec 07 '14 at 07:28
  • 1
    Yes, you can have ``, ``, but in this case you need to add another hidden input `` so that the `DefaultModelBinder` matches up the correct items –  Dec 07 '14 at 07:34
  • That might save me some code, I would choose a guid. Hopefully the model binder orders them the same as they were in the markup. I really hate having large javascript files for the purpose of renumbering things – Isaac Bolinger Dec 07 '14 at 07:43
  • 1
    @IsaacBolinger, Typically you would render existing items in a `for` loop and add `` but for dynamically added items (via javascript), the `Index` value could be `(new Date()).getTime()`. Because of the `Index` property, you can add or delete items and the collection will still be correctly bound on postback (no need to renumber), and the collection is ordered as its rendered in the view. –  Dec 07 '14 at 08:38
  • much appreciated, my javascript file is about half as big on the first screen I redid. Looking forward to eliminating more javascript! – Isaac Bolinger Dec 12 '14 at 04:05

2 Answers2

1

In order to post back to a model, the name attribute of the control must match the name name of the property, and for collections, the name attribute must have an indexer. Since <input type="text" name="contact_name[]" /> contains an illegal name, its value cannot be bound to your model.

Create a view model with the properties you want to edit

public class ContactVM
{
  public string Name { get; set; }
  public string Phone { get; set; }
}

In your controller, initialize a collection of any existing Contacts you want to display

public ActionResult Edit()
{
  List<ContactVM> model = new List<ContactVM>();
  // add any existing contacts to edit
  return View(model);
}

And in the view

@model List<ContactVM>
@using(Html.BeginForm())
{
  <div id="contactlist">
    for(int i = 0; i < Model.Count; i++)
    {
      <div class="contact">
        @Html.TextBoxFor(m => m[i].Name)
        @Html.TextBoxFor(m => m[i].Phone)
        // Add index property to allow dynamic addition and deletion of contacts
        <input type="hidden" name="Index" value="@i" />
        <button type="button" class="deletecontact">Delete</button>
      </div>
    }
  </div>
  <button type="button" id="addcontact">Add More Focal Points</button>
  // Add html for a new contact
  <div id="newcontact"> // style this as hidden
    <div class="contact">
      <input type="text" name="[#].Name value />
      <input type="text" name="[#].Phone value />
      <input type="hidden" name="Index" value ="%"/>
      <button type="button" class="deletecontact">Delete</button>
    </div>
  </div>
  <input type="submit" value="Save" />
}

Script

$('#addcontact).click(function() {
  var count = $('#contactlist').find('.contact').length;
  if (count < 10)
  {
    var index = (new Date()).getTime(); // unique indexer
    var clone = $('#newcontact').clone();
    clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
    clone.find('input[type="hidden"]').val(index);
    $('#contactlist').append(clone.html());
  } else {
    // cant add any more
  }
});
$('.deletecontact').click(function() {
  $(this).closest('.contact').remove();
}

Post method

public ActionResult Edit(List<ContactVM> model)
{
  //model now contains all the contacts with the name and phone
}
0

Assume:

public class contactVM
{
    public contactVM()
    {
      contacts = new List<individualContactVM>()
    }
    public List<individualContactVM> contacts {get;set;}
}

public class individualContactVM
{
    public string Name {get;set;}
    public string Phone {get;set;}
}

then you would have this group of input boxes: for instance 3 contacts:

@model Benafsh.Website.contactVM
@using (Html.BeginForm("SaveContact", "Contact", FormMethod.Post, new { name = "thisform", novalidate = "" }))
{    

<input type="text" name=contacts[0].Name/>
<input type="text" name=contacts[0].Phone/>



<input type="text" name=contacts[1].Name/>
<input type="text" name=contacts[1].Phone/>

<input type="text" name=contacts[2].Name/>
<input type="text" name=contacts[2].Phone/>
<input type="submit" value="Save"  />
}

and so on. You have to have a contiguous set of numbers from 0 to N or it won't work right.

Your controller would simply be:

[Authorize]
public class ContactController : Controller
{
    [HttpPost]
    public void SaveContact(contactVM input)
    {
    //write code here to take the info out of contactVM and put it wherever
    }
} 

Indeed, one of the troubles you will have is when your user wishes to delete one of them. Then you must renumber the indexes for the items following it. You will have to write some javascript to do such a thing. Say if they delete the first contact, then the rest of the contacts need their index subtracted by one.

Isaac Bolinger
  • 7,328
  • 11
  • 52
  • 90
  • Isaac. FYI, I've added an answer showing one technique to dynamically add and remove collection items using an `Index` property so that you don't need to re-number the indexers. –  Dec 08 '14 at 01:14
  • that's very cool. I think I read a bad tutorial somewhere that said it 'must' be done that way. I've got a really complex form I started on and thankfully didn't finish. The renumbering file is quite big. – Isaac Bolinger Dec 08 '14 at 01:42