0

Good day, i have dynamically created form elements with jquery that looks like this:

<label for="fname" class="fname_label col-sm-2 control-label">First Name</label>
<div class="col-sm-4">
    <input type="text" class="fname_input form-control" id="fname" placeholder="First Name" name="firstnames">
</div>

<label for="uemail" class="uemail_label col-sm-2 control-label">Email</label>
<div class="col-sm-4">
    <input type="text" class="uemail_input form-control" id="uemail" placeholder="Email" name="emailaddresses">

A user can create multiple elements like this with same name and ids. I mean if a user should click on add more, the same elements are created with the same name and id with jquery. A user can create multiple elements say 10 or more. My question is how can i post or insert the values of the dynamically create elements to the database. i am using c# in MVC. Thank You.

  • 1
    Duplicate `id` attributes are invalid html. And to bind to a collection, your inputs need collection indexers. Refer [this answer](http://stackoverflow.com/questions/28019793/submit-same-partial-view-called-multiple-times-data-to-controller/28081308#28081308) for some examples –  Apr 02 '17 at 13:00
  • Start by reading the specification to understand why this is not a valid scenario https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#the-id-attribute This covers the name attribute https://www.w3.org/TR/html401/interact/forms.html#h-17.2 – Mark Schultheiss Apr 02 '17 at 13:12
  • Note that what you describe are called "successful controls" in the specifications as well: https://www.w3.org/TR/html401/interact/forms.html#successful-controls – Mark Schultheiss Apr 02 '17 at 13:18
  • thank you @MarkSchultheiss, so is it not possible to post a form that has multiple controls with the same name? – Stanley Ekpunobi Apr 02 '17 at 13:25
  • Some elements WILL have the same name such as input type radio. Text type for your example will pair the name with the value on the form submission - thus the name will be unique within a FORM - as noted in the form documentation for a "successful" control. Either way, duplicate ID's are invalid within the document (not just the form) and will produce unpredictable results. – Mark Schultheiss Apr 02 '17 at 14:36
  • Note there IS a way to do this, just not directly posting a form with dynamic elements. – Mark Schultheiss Apr 02 '17 at 14:49
  • @MarkSchultheiss, It can easily be done by _directly posting a form with dynamic elements_. The name attributes just need to contain collection indexers (refer the examples in the first comment) –  Apr 02 '17 at 23:00
  • @StephenMuecke yes, as I up-voted your comment. Perhaps I should have clarified with the caveat of "with this markup" and added reference to yours as well :) i.e. the difference between "dynamic elements" and simply "copied elements" with the same id etc. I simply added a possible answer as one alternative, but not the ONLY answer possible here also. – Mark Schultheiss Apr 03 '17 at 17:48

1 Answers1

1

Had a bit of time and put this together. I created a JavaScript namespace to hold my functions, data etc; kept the jQuery part separate for the event (submit and add rows) management. You could easily add capability to delete new entry groups (row) as well, just need ONE to stay as I used .clone() to get that new row.

Sample markup using some bootstrap stuff (not required for the functional part). Note I used jQuery for the ajax stuff, you would not have to but it made the sample a bit smaller perhaps.

<div class="container">
  <form id="myform">
    <div class="inputs-holder">
      <fieldset class="entry-group">
        <legend class="col-form-legend col-xm-2">
          one input
        </legend>
        <div class="form-group row">
          <label class="col-xs-2 col-form-label col-form-label-sm">Name</label>
          <div class="col-xs-7">
            <input required="true" class="form-control form-control-xs name-field" type="text" />
          </div>
        </div>
        <div class="form-group row">
          <label class="col-xs-2 col-form-label col-form-label-sm">Email</label>
          <div class="col-xs-7">
            <input required="true" class="form-control form-control-xs email-field" type="email" placeholder="enter email" value="testme@example.com" />
          </div>
        </div>
      </fieldset>
    </div>
    <div class="form-group row">
      <div class="offset-xs-2 col-xs-5">
        <button id="submitme" type="submit" class="btn btn-primary btn-xs">Submit Me</button>
      </div>
      <div class="offset-xs-2 col-xs-5">
        <button id="addnewgroup" type="button" class="btn btn-xs">Add new group</button>
      </div>
    </div>
  </form>
</div>
<div id="results">
  Entries
</div>

Some script to process and push data via ajax to server:

/* Latest compiled and minified JavaScript included as External Resource */

var myApp = myApp || {};
myApp.arrayObj = {
  // some stuff clipped out not used here...
  // use to lookup duplicates
  lookup: function(myArray, searchTerm, property, firstOnly) {
    var found = [];
    var i = myArray.length;
    while (i--) {
      if (myArray[i][property] === searchTerm) {
        found.push(myArray[i]);
        if (firstOnly) break; //if only the first 
      }
    }
    return found;
  },
  // could be used to validate duplicates for example
  lookupAll: function(myArray, property, searchTerm) {
    return this.lookup(myArray, searchTerm, property, false);
  }
};
myApp.data = {
  entries: [],
  saveUrl: "/Home/SaveEmails" this COULD be from server/MVC
};
myApp.func = {
  addEmailRow: function(myArray, item, allowdups, uniquekey) {
    // matches the  POCO object class names
    var entry = {
      "name": item.name,
      "email": item.email
    };
    if (allowdups || (!allowdups && !myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length)) {
      myArray.push(entry);
    } else if (allowdups && myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length) {
      myApp.data.entries[uniquekey] = item[uniquekey];
    } else if (allowdups && !myApp.arrayObj.lookup(myArray, entry[uniquekey], uniquekey, true).length) {
      myArray.push(entry);
    }
  },
  // just something to show what is entered/being submitted
  showEntries: function(list) {
    $.each(list, function(index, value) {
      $('#results').append("<div>" + value.name + " " + value.email + "</div>");
    });
  },
  // the important part
  saveEntries: function(list) {
    var entries = JSON.stringify({
      'Entries': list
    });

    $.ajax({
        contentType: 'application/json;',
        dataType: 'json',
        type: 'POST',
        url: myApp.data.saveUrl,
        data: entries
      }).done(function() {
        $('#results').html('"SaveEmails()" successfully called.');
      })
      .fail(function(response) {
        $('#results').html(response);
      });
  }
};
$(document).ready(function() {
  // add new "group" row
  $('#addnewgroup').on('click', function() {
    var holder = $('.inputs-holder');
    // clone that first row
    var newRow = holder.find('.entry-group').eq(0).clone();
    // clear any values entered and append it
    newRow.find('input').val("");
    newRow.appendTo(holder);
  });
  // a bit verbose for clarity here
  $('#myform').on('submit', function(e) {
    e.preventDefault();
    e.stopPropagation();
    // clear entries
    myApp.data.entries = [];
    var allowdups = false,
      uniquekey = "name";
    var holder = $('.inputs-holder');
    // get entries
    holder.find('.entry-group').each(function() {
      var email = $(this).find('.email-field').val();
      var name = $(this).find('.name-field').val();
      var item = {
        "email": email,
        "name": name
      };
      myApp.func.addEmailRow(myApp.data.entries, item, allowdups, uniquekey);
    });
    $('#results').html("<div>Results:</div>");
    myApp.func.showEntries(myApp.data.entries);
    myApp.func.saveEntries(myApp.data.entries);

    // supress default submit form
    return false;
  });
});

Now the server side:

/* MVC for this: */
// POCO object: - reference this whereever you put it.
public class EmailEntry
{
    public String name { get; set; }
    public String email { get; set; }
}

// controller/ method: used IEnumerable instead of List as simpler
public class HomeController : Controller
{
   [HttpPost]
    public void SaveEmails(IEnumerable<EmailEntry> entries)
    {
        // do stuff with email entries here...validate/save for example
    }
}

ALL this is untested and my contain small errors but I believe it to be pretty bug free.

Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100