jqueryeonasdan-datetimepicker

Javascript Datetimepicker in form with dynamic elements


I use http://eonasdan.github.io/bootstrap-datetimepicker/ in my project. It is very fine.

On one page I have a form, where rows of input fields can dynamically added or removed. In each row is a "startdate" and an "enddate" input called #substitute_0_start and #substitute_0_end. The IDs are incremented with javascript when cloneNode() runs. I.e. after one clone there is one more row with #substitute_1_start and #substitute_1_end in it.

I need to adjust the settings of the "startdate" and "enddate" inputs. I use a for-loop to go over each row and adjust the settings. This works like expected, instead if I try to set minDate or maxDate of the corresponding input.

                 <div class="form-group col-md-3 col-xs-6">
                    <label for="substitute_0_start" class="control-label">ab dem</label>
                    <div class='input-group date substitute_start' id='substitute_0_start'>
                        <input type="text" class="form-control datepicker subst_start" name="substitute[0][start]" readonly="readonly"/>
                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-calendar"></span>
                        </span>
                    </div>
                </div>
                <div class="form-group col-md-3 col-xs-6">
                    <label for="substitute_0_end" class="control-label">bis zum</label>
                    <div class='input-group date substitute_end' id='substitute_0_end'>
                        <input type="text" class="form-control datepicker subst_end" name="substitute[0][end]" readonly="readonly"/>
                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-calendar"></span>
                        </span>
                    </div>
                </div>
          function callDatepickerAndSelect2(){
            for (a = 0; a <= i; a++){
                // WORKS!
                $('#substitute_'+a+'_start').datetimepicker({
                    format: "DD.MM.YYYY",
                    minDate: moment().add(1, 'minutes')
                });
                $('#substitute_'+a+'_end').datetimepicker({
                    format: "DD.MM.YYYY",
                    minDate: moment().add(1, 'days')
                });
                // WORKS NOT FOR CLONED ITEMS
                $("#interval_start").on("dp.change", function (e) {
                    $('.substitute_start').data("DateTimePicker").minDate(e.date);
                    $('.substitute_end').data("DateTimePicker").minDate(e.date);
                });
                $("#interval_end").on("dp.change", function (e) {
                    $('.substitute_start').data("DateTimePicker").maxDate(e.date);
                    $('.substitute_end').data("DateTimePicker").maxDate(e.date);
                });
                // THIS DOES NOT WORK!
                $("#substitute_"+a+"_start").on("dp.change", function (e) {
                    $("#substitute_"+ a +"_end").data("DateTimePicker").minDate(e.date);
                });
                $("#substitute_"+a+"_end").on("dp.change", function (e) {
                    $("#substitute_"+ a +"_start").data("DateTimePicker").maxDate(e.date);
                });
            };
        }

Everytime on of the two last dp.change() are fired, I get an error Cannot read property 'minDate' of undefined or Cannot read property 'maxDate' of undefined .

I can't find a solution how to set maxDate of #substitute_X_end if #substitute_X_start is changed (and of curse the other direction).

Does anyone have an idea about set maxDate/minDate to the corresponding inputs initially and if I did a cloneNode()??

EDIT Also I've investigated, that the middle part does not work for cloned elements. For me it feels like cloned elements are ownly viewable, but I can't access them with javascript. I think I need another solution for cloning the whole DIV :-(

EDIT 2 I setup a fiddle. Checkout here https://jsfiddle.net/qs8kf6wj/ (sorry, the datetimepicker does not work there, wasn't able to find out why)


Solution

  • A bit better, without trouble minDate and maxDate AND some more improvements!

    $(document).ready(function() {
      $("#interval_start").datetimepicker({
        format: "DD.MM.YYYY",
        allowInputToggle: true,
        ignoreReadonly: true
      });
    
      $("#interval_end").datetimepicker({
        format: "DD.MM.YYYY",
        allowInputToggle: true,
        ignoreReadonly: true
      });
    
      $("#interval_start").on("dp.change", function(e) {
        current_min = e.date.startOf("day");
        $("#interval_end")
          .data("DateTimePicker")
          .minDate(e.date);
        updateRangeMin(e.date);
      });
    
      $("#interval_end").on("dp.change", function(e) {
        current_max = e.date.endOf("day");
        $("#interval_start")
          .data("DateTimePicker")
          .maxDate(e.date);
    
        updateRangeMax(e.date);
      });
    
      callDatepickerAndSelect2(i);
    });
    
    function updateRangeMin(date) {
      for (let index = 0; index <= i; index++) {
        var temp = new moment(date);
        if($("#substitute_" + index + "_start").data("DateTimePicker").maxDate().isBefore(date)){
           //for fixing maxDate is before minDate error
           $("#substitute_" + index + "_start").data("DateTimePicker").maxDate(temp.add(1825, "days"));
        }
        $("#substitute_" + index + "_start")
          .data("DateTimePicker")
          .minDate(date);
        if($("#substitute_" + index + "_end").data("DateTimePicker").maxDate().isBefore(date)){
                    //for fixing maxDate is before minDate error
                    $("#substitute_" + index + "_end").data("DateTimePicker").maxDate(temp.add(1825, "days"));
         }
         $("#substitute_" + index + "_end")
                    .data("DateTimePicker")
                    .minDate(date);
      }
    }
    
    function updateRangeMax(date) {
      for (let index = 0; index <= i; index++) {
        $("#substitute_" + index + "_end")
          .data("DateTimePicker")
          .maxDate(date);
        $("#substitute_" + index + "_start")
                    .data("DateTimePicker")
                    .maxDate(date);
      }
    }
    var i = 0;
    var original = document.getElementById("substitute_0");
    let current_min = null;
    let current_max = null;
    
    function duplicateElement() {
      var clone = original.cloneNode(true); // "deep" clone
      i++;
      clone.id = "substitute_" + i; // there can only be one element with an ID
      clone.childNodes;
      for (var input of $(".substitute_start", clone)) {
        input.id = clone.id + "_start";
      }
      for (var input of $(".substitute_end", clone)) {
        input.id = clone.id + "_end";
      }
      for (var input of $(".subst_text", clone)) {
        input.name = "substitute[" + i + "][text]";
      }
      for (var input of $(".subst_start", clone)) {
        input.name = "substitute[" + i + "][start]";
      }
      for (var input of $(".subst_end", clone)) {
        input.name = "substitute[" + i + "][end]";
      }
      for (var input of $(".subst_text", clone)) {
        input.value = "";
      }
      for (var input of $(".subst_start", clone)) {
        input.value = "";
      }
      for (var input of $(".subst_end", clone)) {
        input.value = "";
      }
      original.parentNode.appendChild(clone);
      callDatepickerAndSelect2(i);
    }
    
    function removeElement(elementId) {
      var element = document.getElementById(elementId);
      element.parentNode.removeChild(element);
    }
    
    function callDatepickerAndSelect2(a) {
      let start_element = "#substitute_" + a + "_start";
      let end_element = "#substitute_" + a + "_end";
    
      $(start_element).datetimepicker({
        maxDate: current_max,
        minDate: current_min,
        format: "DD.MM.YYYY",
        allowInputToggle: true,
        ignoreReadonly: true
      });
    
      $(end_element).datetimepicker({
        maxDate: current_max,
        minDate: current_min,
        format: "DD.MM.YYYY",
        allowInputToggle: true,
        ignoreReadonly: true
      });
    
      $(start_element).on("dp.change", function(e) {
        $(end_element)
          .data("DateTimePicker")
          .minDate(e.date);
      });
      $(end_element).on("dp.change", function(e) {
        $(start_element)
          .data("DateTimePicker")
          .maxDate(e.date);
      });
    }
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/css/bootstrap-datetimepicker.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.37/js/bootstrap-datetimepicker.min.js"></script>
    
    
    <div class="container">
      <h2>
        Main Interval
      </h2>
      This two dates are minDate and maxDate for all Sub-Intervals!
      <div class="row">
        <div class="form-group col-sm-6">
          <label for="interval_start" class="control-label">Interval start</label
              >
              <div class="input-group date" id="interval_start">
                <input
                  type="text"
                  class="form-control datepicker"
                  name="interval_start"
                  readonly="readonly"
                />
                <span class="input-group-addon">
                  <span class="glyphicon glyphicon-calendar"></span>
                </span>
              </div>
            </div>
            <div class="form-group col-sm-6">
              <label for="interval_end" class="control-label">Interval end</label>
          <div class="input-group date" id="interval_end">
            <input type="text" class="form-control datepicker" name="interval_end" readonly="readonly" />
            <span class="input-group-addon">
                  <span class="glyphicon glyphicon-calendar"></span>
            </span>
          </div>
        </div>
      </div>
      <h2>
        Sub-Intervals (n-times)
      </h2>
      <div class="row" style="border: 2px solid #0F0;">
        <div id="substitute_0" class="">
          <div class="panel-body">
            Each interval has to fit in the Main-Interval and has to fit: startdate
            < enddate and enddate> startdate
              <div class="form-group col-md-3 col-xs-6">
                <label for="substitute_0_start" class="control-label">ab dem</label
                  >
                  <div
                    class="input-group date substitute_start"
                    id="substitute_0_start"
                  >
                    <input
                      type="text"
                      class="form-control datepicker subst_start"
                      name="substitute[0][start]"
                      readonly="readonly"
                    />
                    <span class="input-group-addon">
                      <span class="glyphicon glyphicon-calendar"></span>
                    </span>
                  </div>
                </div>
                <div class="form-group col-md-3 col-xs-6">
                  <label for="substitute_0_end" class="control-label"
                    >bis zum</label
                  >
                  <div
                    class="input-group date substitute_end"
                    id="substitute_0_end"
                  >
                    <input
                      type="text"
                      class="form-control datepicker subst_end"
                      name="substitute[0][end]"
                      readonly="readonly"
                    />
                    <span class="input-group-addon">
                      <span class="glyphicon glyphicon-calendar"></span>
                    </span>
                  </div>
                </div>
              </div>
              <hr />
            </div>
          </div>
          <div class="row">
            <div class="input-group pull-right">
              add sub-interval
              <button
                type="button"
                class="btn btn-default"
                onclick="duplicateElement();"
              >
                <span class="glyphicon glyphicon-plus"></span>
              </button>
            </div>
          </div>
        </div>