使用事件克隆 Eonasdan DateTimePicker

Cloning Eonasdan DateTimePicker with Events

我正在创建一个具有两个选择器的表单来计算日期之间的差异。

用户可以创建更多的日期间隔,这些应该计算特定的间隔。

我遇到的问题是:无论我做什么,克隆选择器后,所有其他克隆的选择器都会激活第一个。

计算发生在页面加载和选择器的隐藏事件之后。

我知道我必须在创建时初始化新的选择器,我这样做了,但我不确定它是否有效。

我怎样才能完成这项工作?我知道如果我从 clone() 中删除 true ,选择器会工作,但克隆无法激活计算事件。

var tomorrow = moment().add(1, 'day');
var startOfYearMoment = moment(tomorrow)
var endOfYearMoment = moment(tomorrow.add(1, 'year'));

function destroyPickers(nrOfSiblings) {
  // console.log("DELETED");

  for (let i = 0; i < nrOfSiblings; i++) {
    // console.log(i);
    $("#inicio" + i).data("DateTimePicker").destroy();
    $("#fim" + i).data("DateTimePicker").destroy();
  }
}

function updatePickers(nrOfSiblings) {
  // console.log("UPDATED");

  for (let i = 0; i < nrOfSiblings + 1; i++) {
    // console.log(i);
    initDatePickers("inicio" + i, "fim" + i);
  }
}

function initDatePickers(idInicio, idFim) { //, parentInicio, parentFim) {

  $("#" + idInicio).datetimepicker({
    locale: 'pt',
    ignoreReadonly: true,
    focusOnShow: false,
    viewMode: 'months',
    format: 'DD/MM/YYYY',
    defaultDate: startOfYearMoment,
    // widgetParent: $(parentInicio),
    widgetPositioning: {
      horizontal: 'auto',
      vertical: 'auto'
    },
    icons: {
      time: 'calendarIcon time',
      date: 'calendarIcon date',
      up: 'calendarIcon up',
      down: 'calendarIcon down',
      previous: 'calendarIcon prev',
      next: 'calendarIcon next',
      today: 'calendarIcon today',
      clear: 'calendarIcon clear',
      close: 'calendarIcon close'
    }
  });

  $("#" + idFim).datetimepicker({
    locale: 'pt',
    ignoreReadonly: true,
    focusOnShow: false,
    viewMode: 'months',
    format: 'DD/MM/YYYY',
    defaultDate: endOfYearMoment,
    //  widgetParent: $(parentFim),
    widgetPositioning: {
      horizontal: 'auto',
      vertical: 'auto'
    },
    icons: {
      time: 'calendarIcon time',
      date: 'calendarIcon date',
      up: 'calendarIcon up',
      down: 'calendarIcon down',
      previous: 'calendarIcon prev',
      next: 'calendarIcon next',
      today: 'calendarIcon today',
      clear: 'calendarIcon clear',
      close: 'calendarIcon close'
    }
  });

}


function getDisponibilidade(startDate, endDate, targetDiv) {

  if (endDate.isSameOrBefore(startDate, 'day month year')) {
    $("#" + targetDiv + " .fim").popover('show');
  } else $("#" + targetDiv + " .fim").popover('hide');

  //console.log(moment.duration(endDate.diff(startDate)).as('days'));


  var duration = Math.round(moment.duration(endDate.diff(startDate)).as('days'));
  if (duration <= 0) {
    $("#" + targetDiv + " .dias").replaceWith('<span class="dias">0 days</span>');

  } else if (duration > 1) {
    $("#" + targetDiv + " .dias").replaceWith('<span class="dias">' + duration + ' days</span>');
  } else $("#" + targetDiv + " .dias").replaceWith('<span class="dias">' + duration + ' day</span>');

  //console.log(duration);
  return duration
}

$(document).ready(function() {
  initDatePickers("inicio0", "fim0");
  var startDate = $("#inicio0").data("DateTimePicker").date();
  var endDate = $("#fim0").data("DateTimePicker").date();
  getDisponibilidade(startDate, endDate, "dispRow0")
});


$("input.dispDtp").on("dp.hide", function(event) {
  if (event.target.id.match(/inicio\d/)) {
    var startDateInpt = event.target.id;
    var endDateInpt = $(this).parent().siblings().children("input[id^='fim']")[0].id;

    /*     console.log(event);
        console.log($(this)); */

  } else {
    var startDateInpt = $(this).parent().siblings().children("input[id^='inicio']")[0].id;
    var endDateInpt = event.target.id;
  }
  var targetDiv = $(this).parents("div[id^='dispRow']")[0].attributes[1].value;

  /*   console.log(startDateInpt);
    console.log(endDateInpt); */

  var startDate = $("#" + targetDiv + " #" + startDateInpt).data("DateTimePicker").date();
  var endDate = $("#" + targetDiv + " #" + endDateInpt).data("DateTimePicker").date();

  getDisponibilidade(startDate, endDate, targetDiv);

});


$(".addDisp button").click(function(event) {

  $("#dispRow0").clone(true,true).appendTo(".disp").prop("id", "newDisp");


  var sibLength = $("#dispRow0").siblings().length;

  $("#newDisp div[id^='dispDtpDivInicio']").attr("class", "dispDtpDivInicio" + sibLength);
  $("#newDisp input[id^='inicio']").attr("name", "dataInicio" + sibLength).prop("id", "inicio" + sibLength);

  $("#newDisp div[id^='dispDtpDivFim']").attr("class", "dispDtpDivFim" + sibLength);
  $("#newDisp input[id^='fim']").attr("name", "dataInicio" + sibLength).prop("id", "fim" + sibLength);

  $("#newDisp .remDisp button").prop("id", "remDispBtn" + sibLength);

  destroyPickers(sibLength);
  updatePickers(sibLength);

  $("#newDisp").attr("id", "dispRow" + sibLength);
  // console.log($("#dispRow0").siblings());


})
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.css" />

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>

<div class="row">
  <div class="col-12 disp">
    <div class="form-row" id="dispRow0">
      <div class="formGroup col-2 dispDtpDivInicio0">
        <label for="dataInicio0" class=" ">start</label>
        <input name="dataInicio0" type="text" class="form-control dispDtp" id="inicio0" readonly required style="position:relative;">
      </div>
      <div class="formGroup col-2 dispDtpDivFim0">
        <label for=" dataFim0" class=" ">end</label>
        <input name="dataFim0" type="text " class="form-control dispDtp" id="fim0" readonly required style="position:relative;">
        <small class="form-text text-muted ">
          <span class="dias ">0 days</span>
        </small>
      </div>
    </div>
  </div>
</div>

<div class="row ">
  <div class="col-12 addDisp">
    <button type="button">
      <h5>
         Add
      </h5>
    </button>
  </div>
</div>

我进行了搜索,发现了几个问题,但是 none 可以满足我的需要

Question 1

编辑:JSFiddle 如果需要:

将此作为组织、重用代码和关注点分离方面的一课。

每当你处理模块化组件时,你都希望将它们隔离开,使它们不相互依赖。因此,首先,我建议不要克隆已初始化的对象,也不要克隆带有事件的对象。

所以,我创建了一个 "view" 模板。如您所知,需要特别注意的一点是,您必须匹配 forname 属性……我只是为此使用了一个 uuid,这将确保两者永远不会相同。索引会像您所做的那样正常工作,但是您必须跟踪该索引。由你决定。

每次有人单击“添加”时都会初始化视图模板,并在加载文档时最初调用一次。

初始化准备模板,调用日期选择器初始化,并添加事件处理程序。

希望这对您有所帮助。

var tomorrow = moment().add(1, 'day');
var startOfYearMoment = moment(tomorrow)
var endOfYearMoment = moment(tomorrow.add(1, 'year'));

function calculateDays($inicio,$fim){
  return function(event) {
    var startDate = $inicio.data("DateTimePicker").date();
    var endDate = $fim.data("DateTimePicker").date();
    var $span = $fim.parent().find('.dias');
    getDisponibilidade(startDate, endDate, $span);
  };
}

function initDatePickers($inicio,$fim) { //, parentInicio, parentFim) {
  var options = {
    locale: 'pt',
    ignoreReadonly: true,
    focusOnShow: false,
    viewMode: 'months',
    format: 'DD/MM/YYYY',
    // widgetParent: $(parentInicio),
    widgetPositioning: {
      horizontal: 'auto',
      vertical: 'auto'
    },
    icons: {
      time: 'calendarIcon time',
      date: 'calendarIcon date',
      up: 'calendarIcon up',
      down: 'calendarIcon down',
      previous: 'calendarIcon prev',
      next: 'calendarIcon next',
      today: 'calendarIcon today',
      clear: 'calendarIcon clear',
      close: 'calendarIcon close'
    }
  };
  options.defaultDate = startOfYearMoment,
  $inicio.datetimepicker(options);
  options.defaultDate = endOfYearMoment;
  $fim.datetimepicker(options);
  var hideHandler = calculateDays($inicio,$fim);
  $inicio.on("dp.hide",hideHandler);
  $fim.on("dp.hide",hideHandler);
  hideHandler(); // run once.
  
}


function getDisponibilidade(startDate, endDate, $span) {
  var duration = Math.round(moment.duration(endDate.diff(startDate)).as('days'));
  if (duration <= 0) {
    $span.text("0");
  }else{
    $span.text(duration);
  }
  return duration
}

$(document).ready(function() {
  addDateDurationWidget(); // Add an initial date widgit.
});



function addDateDurationWidget(){
  var $template = $("#view-templates .view").clone()
  var uu1 = uuid();
  var uu2 = uuid();
  var replaceUuid = function(uu){
    return function(i,s){return s.replace(/{{uuid}}/,uu)}
  }
  $template.find('.dispDtpDivInicio label').attr("for",replaceUuid(uu1))
  $template.find('.dispDtpDivInicio input').attr("name",replaceUuid(uu1))
  $template.find('.dispDtpDivFim label').attr("for",replaceUuid(uu2))
  $template.find('.dispDtpDivFim input').attr("name",replaceUuid(uu2))
  $template.appendTo(".disp").prop("id", "newDisp");
  $inicio = $template.find('input.inicio');
  $fim = $template.find('input.fim');
  initDatePickers($inicio,$fim);
}

$(".addDisp button").click(function(event) {
  addDateDurationWidget();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.css" />

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>

<div class="row">
  <div class="col-12 disp">
    
  </div>
</div>

<div class="row ">
  <div class="col-12 addDisp">
    <button type="button">
      <h5>
         Add
      </h5>
    </button>
  </div>
</div>
<div id="view-templates" style="display:none;">
  <div class="form-row view">
    <div class="formGroup col-2 dispDtpDivInicio">
      <label for="dataInicio-{{uuid}}" class=" ">start</label>
      <input name="dataInicio-{{uuid}}" type="text" class="form-control inicio dispDtp" readonly required style="position:relative;">
    </div>
    <div class="formGroup col-2 dispDtpDivFim">
      <label for="dataFim-{{uuid}}" class=" ">end</label>
      <input name="dataFim-{{uuid}" type="text " class="form-control fim dispDtp" readonly required style="position:relative;">
      <small class="form-text text-muted ">
        <span class="dias">0</span><span> days</span>
      </small>
    </div>
  </div>
</div>