1

I have the form that looks like this:

<form method="post" action="">
   <label for="id_count-count">Count:</label>
   <input id="id_count-count" type="number" name="count-count">
   <div class="extrafieldWrapper"></div>
   <input type="submit" value="Submit">
</form>

Depending on number of field count i add or delete new couple of fields item and item2. For example: if value of field count will be 2, it will generate two couples of fields item and item2.

There is this fields:

<label for="id_items-0-item">Item:</label>
<input id="id_items-0-item" type="number" name="items-0-item">
<label for="id_items-0-item2">Item2:</label>
<input id="id_items-0-item2" class="children_age" type="number" name="items-0-item2" value="0">
<div class="extrafieldWrapperChAge"></div>

Then, depending on each value of field item2 i add new fields that calls childrenage. There is how looks childrenage field:

<label for="id_childrenage-0-childrenage">ChildrenAge 1</label>
<input id="id_childrenage-0-childrenage" type="number" name="childrenage-0-childrenage" value="0">

Here is fiddle link . And here is imgur of what i want and what i've got link.

Depending of value of item2 the id of childrenage is changing too. For example if i have '3' as value of item2, then i have id_childrenage-0-childrenage, id_childrenage-1-childrenage and id_childrenage-2-childrenage. And if i have several item2 i have new examples of childrenage that count their id from the start for each item2:

<label for="id_items-0-item2">Item2:</label>
<input id="id_items-0-item2" class="children_age" type="number" name="items-0-item2" value="2">
<div class="extrafieldWrapperChAge">
   <label for="id_childrenage-0-childrenage">ChildrenAge</label>
   <input id="id_childrenage-0-childrenage" type="number" name="childrenage-0-childrenage" value="0">
   <label for="id_childrenage-1-childrenage">ChildrenAge</label>
   <input id="id_childrenage-1-childrenage" type="number" name="childrenage-1-childrenage" value="0">
</div>
<label for="id_items-0-item2">Item2:</label>
<input id="id_items-0-item2" class="children_age" type="number" name="items-0-item2" value="1">
<div class="extrafieldWrapperChAge">
   <label for="id_childrenage-0-childrenage">ChildrenAge</label>
   <input id="id_childrenage-0-childrenage" type="number" name="childrenage-0-childrenage" value="0">
</div>

In example above i have 2 fields item2, on one of it value '2' and on another value '1'. According to this i have two fields of childrenage for the first item2 with id id_childrenage-0-childrenage and id_childrenage-1-childrenage, and one field childrenage for the second with id id_childrenage-0-childrenage. But i need that count of id childrenage go on from the first item2 to the last, this is example:

<label for="id_items-0-item2">Item2:</label>
<input id="id_items-0-item2" class="children_age" type="number" name="items-0-item2" value="2">
<div class="extrafieldWrapperChAge">
   <label for="id_childrenage-0-childrenage">ChildrenAge</label>
   <input id="id_childrenage-0-childrenage" type="number" name="childrenage-0-childrenage" value="0">
   <label for="id_childrenage-1-childrenage">ChildrenAge</label>
   <input id="id_childrenage-1-childrenage" type="number" name="childrenage-1-childrenage" value="0">
</div>
<label for="id_items-0-item2">Item2:</label>
<input id="id_items-0-item2" class="children_age" type="number" name="items-0-item2" value="1">
<div class="extrafieldWrapperChAge">
   <label for="id_childrenage-2-childrenage">ChildrenAge</label>
   <input id="id_childrenage-2-childrenage" type="number" name="childrenage-2-childrenage" value="0">
</div>

Here is js code that realize this:

$(function(){
   $('#id_count-count').on('change', function(e){
      var n = $('#id_count-count').val() || 0;
      var html = "<input id='id_items-TOTAL_FORMS' type='hidden' value='" + n + "' name='items-TOTAL_FORMS'>"
         + "<input id='id_items-INITIAL_FORMS' type='hidden' value='0' name='items-INITIAL_FORMS'>"
         + "<input id='id_items-MIN_NUM_FORMS' type='hidden' value='0' name='items-MIN_NUM_FORMS'>"
         + "<input id='id_items-MAX_NUM_FORMS' type='hidden' value='15' name='items-MAX_NUM_FORMS'>";

      for (var i = 0; i < n; i++) {
         html += "<div>Items" + (i + 1) + "</div>"
            + "<br/><label for='id_items-" + i + "-item'>Item:</label>"
            + "<input id='id_items-" + i + "-item' type='number' name='items-" + i + "-item'/>"
            + "<label for='id_items-" + i + "-item2'>Item2:</label>"
            + "<input id='id_items-" + i + "-item2' type='number' value='0' name='items-" + i + "-item2' class='children_age'/>"
            + "<div class='extrafieldWrapperChAge'></div>";
      }
      $(".extrafieldWrapper").html(html);
   });
   $(".extrafieldWrapper").on('change', '.children_age', function(e){
      var n = $(this).val() || 0;
      var html = "<input id='id_childrenage-TOTAL_FORMS' type='hidden' value='" + n + "' name='childrenage-TOTAL_FORMS'>"
         + "<input id='id_childrenage-INITIAL_FORMS' type='hidden' value='0' name='childrenage-INITIAL_FORMS'>"
         + "<input id='id_childrenage-MIN_NUM_FORMS' type='hidden' value='0' name='childrenage-MIN_NUM_FORMS'>"
         + "<input id='id_childrenage-MAX_NUM_FORMS' type='hidden' value='15' name='childrenage-MAX_NUM_FORMS'>"; 

      for (var i = 0; i < n; i++) {
         html += "<br/><label for='id_childrenage-" + i + "-childrenage'>ChildrenAge "+(i+1)+"</label>"
            + "<input id='id_childrenage-" + i + "-childrenage' type='number' value='0' name='childrenage-" + i + "-childrenage' />";
      }
      $(this).next('.extrafieldWrapperChAge').html(html);
   });

});

Hope you you understand what i mean. I am newbie in java-script, can you help me to write the code in the right way. Thanks a lot!

6
  • This is simply my personal opinion but holy crap that is a lot of text. Let's see if we can clarify a few things: 1 what do you start with? 2 given very explicit instructions, what do you end up with? 3 could you possibly add your code to the snippet editor, JsFiddle, or Codepen with a working example of what you've got? Commented May 8, 2016 at 6:17
  • 1
    I'm already edit the post and add JsFiddle. Sorry, but i don't know how to do text smaller. Commented May 8, 2016 at 6:33
  • so what is your end goal -- what goal are you looking to accomplish here Commented May 8, 2016 at 6:37
  • A add new imgur link of what i have and what need, it is a simple example. Commented May 8, 2016 at 6:39
  • Okay. I think I have a bit more of an idea as to what you want in particular. Give me a minute. I'm going to attempt to clean up your code (It's a little messy for my tastes and it is making it hard to read) Commented May 8, 2016 at 6:43

1 Answer 1

2

This particular answer was getting needlessly long, so I've decided to truncate it to just the current answer.

The Code

$(function() {
  var fieldset = $('<fieldset>');
  var legend = $('<legend>');
  var input = $('<input>').prop('type', 'number');
  var hidden = $('<input>').prop('type', 'hidden');
  var label = $('<label>');
  var child_wrapper = $('<div class="child-wrapper">');
  /*
    Here we create a couple of new HTML elements. These elements are not a part of the 
    HTML DOM yet and can therefore be manipulated without any visual changes.
  */
  var create_hidden_fields = function(str, fields_arr) {
    var ret = [];
    //return array;
    $.each(fields_arr, function(i, obj) {
      //Loops through each field to set up the hidden values
      var h = hidden.clone();
      //Clones the hidden fields
      h.prop('id', 'id_' + str + '-' + obj.name).prop('name', str + '-' + obj.name).val(obj.value);
      //Sets the ID, name, and value.
      ret.push(h);
    });
    return ret;
  };

  $('#id_count-count').on('change', function(e) {
    var n = $(this).val() || 0;
    //Gets the id count value, or 0;

    var hidden_fields = [{
      name: 'TOTAL_FORMS',
      value: n
    }, {
      name: 'INITIAL_FORMS',
      value: 0
    }, {
      name: 'MIN_NUM_FORMS',
      value: 0
    }, {
      name: 'MAX_NUM_FORMS',
      value: 15
    }];
    //Hidden fields pre-build, makes life easier, since there seems to be a pattern

    var h_arr = create_hidden_fields('items', hidden_fields);
    //Hidden Array created
    if ($(this.form).children(':hidden')) {
      $(this.form).children(':hidden').remove();
      //Removes all the current hidden fields, because lazy.
    }
    $(this.form).prepend(h_arr); //adds in our created hidden fields.

    var form = $(this.form).children('.extra-field-wrapper');
    //Gets the fieldset wrapper.
    form.empty();
    //Empties any children there already. Otherwise extra children are added.

    for (var i = 0; i < n; i++) {
      var fs_clone = fieldset.clone(); //clones the fieldset element
      var l_clone = legend.clone().text("Item " + (i + 1));
      // clones the legend element and adds text
      var la_clone_1 = label.clone();
      //label clone 1
      var input_clone_1 = input.clone();
      //Input clone 1
      var child_wrapper_clone = child_wrapper.clone().prop('id', 'parent-' + (1 + i));
      //We use clones to keep our initial values safe. This way we can alter the clones without changing our defaults

      fs_clone.append(l_clone);
      //Adds our legend up top for readability;      
      la_clone_1.prop('for', 'id-items-' + i + '-item').html('Parent ' + (i + 1));
      //Adds the 'for' property with the correct ID, then sets the HTML. Item and Item2 were getting confusing
      input_clone_1.prop('value', 0).prop('id', 'id-items-' + i + '-item');
      //Sets the default value to 0, and the necessary ID for our label to work
      var la_clone_2 = label.clone().html('# of Children').prop('for', 'id-items-' + i + '-num-children');
      /*
       This is a lot in one line, it clones the label element, adds the HTML, and sets the property all in one.
      */
      var input_clone_2 = input.clone().prop('id', 'id-items-' + i + '-num-children').prop('value', 0).addClass('children_age');
      //See above comment. One difference is that this adds our class for our next function to work

      fs_clone.append([la_clone_1, input_clone_1, la_clone_2, input_clone_2, child_wrapper_clone]);
      //This could probably be cleaned up, but for now it works.

      form.append(fs_clone); //adds the fieldset clone to the form.
    } //End for

  });

  $(".extra-field-wrapper").on('change', '.children_age', function(e) {

    var n = $(this).val() || 0;
    // the current value of the item
    var append_array = [];
    //Add an HTML append array, lessens calls to the $(this).next().append();

    $(this).before(h_arr);
    //Adds the hidden elements before this element

    $(this).next('.child-wrapper').empty();
    //Clears the child elements

    for (var i = 0; i < n; i++) {
      var l_clone = label.clone().html('Children Age' + (i + 1));
      var i_clone = input.clone().addClass('child-age').prop('value', 0);

      append_array.push(l_clone);
      append_array.push(i_clone);
      //Adds the elements to the append_array since we are now done with them.
    }

    $(this).next('.child-wrapper').append(append_array);
    //Adds all created elements;

    var children_age = $('input.child-age').each(function(i, el) {
      var self = $(el);
      //Lazy handle
      var label = $(el).prev();
      //The label is the element in front of our current element.
      label.text("Child Age " + (i + 1)).prop('for', 'id_childrenage_' + i + '_childrenage');
      //Gives correct label number /re does the for property;
      self.prop('id', 'id_childrenage_' + i + '_childrenage');
      //give correct id now that changes have been made to the dom.
    });


    var hidden_fields = [{
      name: 'TOTAL_FORMS',
      value: children_age.length
    }, {
      name: 'INITIAL_FORMS',
      value: 0
    }, {
      name: 'MIN_NUM_FORMS',
      value: 0
    }, {
      name: 'MAX_NUM_FORMS',
      value: 15
    }];
    //We have to redeclare this function here in order for the value: n  to work correctly.
    if ($('#id_childrenage-TOTAL_FORMS').length == 0) {
      //This element does not exist yet in the DOM
      var hidden_fields = [{
        name: 'TOTAL_FORMS',
        value: children_age.length
      }, {
        name: 'INITIAL_FORMS',
        value: 0
      }, {
        name: 'MIN_NUM_FORMS',
        value: 0
      }, {
        name: 'MAX_NUM_FORMS',
        value: 15
      }];
      //We have to redeclare this function here in order for the value: n  to work correctly.

      var h_arr = create_hidden_fields('childrenage', hidden_fields);
      //creates the hidden fields
      $(this.form).prepend(h_arr);
    } else {
      $('#id_childrenage-TOTAL_FORMS').val(children_age.length);
    }


  });

});
label {
  font-weight: 800;
}
input[type="number"] {
  width: 50px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form method="post" action="">
  <label for="id_count-count">Count:</label>
  <input id="id_count-count" type="number" name="count-count">
  <div class="extra-field-wrapper"></div>
  <input type="submit" value="Submit">
</form>

The Explanation

I did a few things here, and there is more than likely a better way, but I did the best with my own time constraints.

  1. I create the items to copy into the with jQuery because I'm old school and I just do not remember anything about how the template tag works so I stay away from it's particular variety of magics. The fieldset and legend are there solely for readability. In your actual code you can just delete their references as well as fs_clone and l_clone, instead attach the items directly to the form element.
  2. I created a function that exists solely within the realm of our wrapping anonymous function called create_hidden_fields, as a programmer, mathematician, and all around lazy person I noticed the pattern in the naming convention and this appealed to me more than having to create and copy a bunch of items by hand every time I wanted to create a field.
  3. I have the id_childrenage-TOTAL_FORMS (which is, by the way, the weirdest naming convention I've ever come across) to the form itself. Since we don't need more than 1 copy of the input.
  4. The reason the hidden_fields array is redeclared in both functions is fairly simple: if it is declared before the variable n is, the TOTAL_FORMS input won't have the correct value. Not good.
  5. Hopefully everything else can be answered by my comments in the code itself.

Happy Coding.

Sign up to request clarification or add additional context in comments.

12 Comments

Thank you very much for your answer, it works very fine! But I have two remarks, I need to count id-items-n-item, id-items-n-num-children and id_childrenage_n_childrenage from '0'. And second i need to have <input id='id_items-TOTAL_FORMS' type='hidden' value='" + n + "' name='items-TOTAL_FORMS'> where n is number of filled forms(parents and children), and <input id='id_items-TOTAL_FORMS' type='hidden' value='" + n + "' name='childrenage-TOTAL_FORMS'>, where n is number of filled childrenage fields. Its for my django framework. Sory for my pure js. And thank you once again
I've never used Django, but I must say that seems like a real pain to have to do that.
I've made an edit @ZagorodniyOlexiy, let me know if that helps you out. I'd be lying if I said I totally understood what all of this was for.
Thank you very @Jhecht it works very fine! I'm very Thakfull to you!
i'm sorry that disturbing you. But i found the bug can you help me with it? <input id='id_items-TOTAL_FORMS' type='hidden' value='" + n + "' name='items-TOTAL_FORMS'> need to count how many couples of Parent and Children(only couples! without childrenage), and <input id='id_items-TOTAL_FORMS' type='hidden' value='" + n + "' name='childrenage-TOTAL_FORMS'> needs to count a sum of all Childrenage fields. Sorry very much! And thak you once again
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.