Cloning form elements in Internet Explorer

So, I ran into an issue when cloning form elements. The scenario was as follows: For an insurance company, a customer was reporting that a damage had occurred to one or more insured items. After adding general information about the event (it happened at such and such a date, while riding a bicycle), the customer added multiple items of goods that was damaged during the event. As a part of the damage goods part of the form, was a dropdown:


Report a damage
Date: 
What happened? 
What was damaged? 
A 
...


The idea was, that clicking the button would duplicate a part of the form, which would effectively increase the collection of block items in the associated view model:


public class ReportViewModel {
  public List<BlockViewModel> Blocks { get; set; }
}
public class BlockViewModel {
  public string Title { get; set; }
  public List<QuestionViewModel> Questions { get; set; }
}

The solution worked pretty flawlessly too! For completeness, I wrote the following JavaScript helper methods to update the cloned objects' IDs:


function adjustIds(inSet, by) {
  inSet.each(function () {
    var div = $(this);
    increaseAttr(div, "data-item-index", 1);
    if (div.hasClass("field")) {
      increaseAttr(div.find("div.editor-label label"), "for", by);
      div.find("div.editor-field").each(function () {
        var editorField = $(this);
        increaseAttr(editorField.find("select, textarea, input, label"), ["name", "id", "for"], by);
        increaseAttr(editorField.find("span"), "data-valmsg-for", by);
        increaseAttr(editorField.find("div a"), "id", by);
      });
    }
}

function increaseAttr(elements, attr, by) {
  if (Object.prototype.toString.call(attr) !== '[object Array]')
    attr = [attr];

  $.each(elements, function () {
    var element = $(this);
    for (var i = 0; i < attr.length; i++) {
      var attributeValue = element.attr(attr[i]);
      if (typeof attributeValue !== 'undefined' && attributeValue !== false) {
        element.attr(attr[i], incrementFirstInteger(attributeValue, by));
      }
    }
  });
}

function incrementFirstInteger(str, by) {
  if (!by) by = 1;
  var pattern = /\d+/;
  var id = parseInt(str.match(pattern)) + by;
  return str.replace(pattern, id);
}

However, cloning the select elements, caused some weird behavior - the first cloned select would never retain its value! See various posts around this issue on the web.

To work around the issue, I finally found the following solution, effectively replacing the cloned select elements with new, near-identical, copies (the near-identical is essential, since copying *all* attributes, for example, would render the copy as useless as the clone):

$("#clone-button").click(function() {
  ...
  var set = parent.find("div.child:last");
  var newSet = $(set.clone(true));
  newSet.find("select").replaceWith(function() {
    var str = "<select name=\"" + this.name + "\">";
    $(this).find("option").each(function(){
      str += this.outerHTML;
    });
    return str + "</select>";
  });
  ...
  adjustIds(...);
  ...
});

So, when cloning forms containing selects, if you run into issues, replace the cloned selects with new instances.

Have a nice day!

Comments

Popular posts from this blog

Auto Mapper and Record Types - will they blend?

Unit testing your Azure functions - part 2: Queues and Blobs

Testing WCF services with user credentials and binary endpoints