Javascript 如何处理嵌套表单的条件验证?

Javascript 如何处理嵌套表单的条件验证?,javascript,ruby-on-rails,cocoon-gem,flatpickr,Javascript,Ruby On Rails,Cocoon Gem,Flatpickr,我似乎无法理解以下几点: 我将cocoon用于嵌套表单,在每个表单中,通过FlatPicker请求开始日期和结束日期。我想做两件事中的一件(以最简单的为准) 验证提交的日期范围之间是否没有重叠,但由于日期尚未在数据库中,我无法在验证中验证它们 不要通过禁用在日历中显示不可用的日期 形式 @年龄\表\列表,值\方法::id,标签\方法:false%> 每期客人的价格: 再加一个句号 $(文档).ready(函数(){ //$(文档).find(“#第一个#日期”)).flatpickr();

我似乎无法理解以下几点:

我将cocoon用于嵌套表单,在每个表单中,通过FlatPicker请求开始日期和结束日期。我想做两件事中的一件(以最简单的为准)

  • 验证提交的日期范围之间是否没有重叠,但由于日期尚未在数据库中,我无法在验证中验证它们
  • 不要通过禁用在日历中显示不可用的日期
形式


@年龄\表\列表,值\方法::id,标签\方法:false%>
每期客人的价格:
再加一个句号
$(文档).ready(函数(){
//$(文档).find(“#第一个#日期”)).flatpickr();
const startDateInput=$(document.find(“.first\u date”)
const endDateInput=$(document.find(“.second_date”)
if(startDateInput&&endDateInput){
const unvailableDates=JSON.parse(document.querySelector(“.widget content”).dataset.unavailable)
FlatPicker(起始日期输入{
//minDate:“今天”,
//日期格式:“d-m-Y”,
禁用:不可用日期,
格式:“d-m-Y”,
altFormat:“d-m-Y”,
altInput:是的,
onChange:函数(selectedDates,selectedDate){
如果(selectedDate==''){
endDateInput.disabled=true;
}
让minDate=selectedDates[0];
minDate.setDate(minDate.getDate()+1);
endDateCalendar.set('minDate',minDate);
endDateInput.disabled=false;
}
});
常量结束日期日历=
FlatPicker(endDateInput{
//日期格式:“d-m-Y”,
禁用:不可用日期,
格式:“d-m-Y”,
altFormat:“d-m-Y”,
altInput:是的,
},
);
};
});
嵌套形式

  <div class="nested-fields border-bottom">
    <div class="row">
      <div class="col col-sm-6"><%= f.input :price, placeholder: "e.g. 12.99" %></div>
    </div>

    <div class="row">
      <div class="col col-sm-6"><%= f.input :start_date,
          as: :string,
          label:"Start date",
          placeholder: "From",
          wrapper_html: { class: "inline_field_wrapper" },
          input_html:{ class: "first_date"} %></div>
      <div class="col col-sm-6"><%= f.input :end_date,
          as: :string,
          label:"End date",
          placeholder: "to",
          wrapper_html: { class: "inline_field_wrapper" },
          input_html:{ class: "second_date"} %></div>
    </div>
    <div hidden class= "widget-content" data-unavailable="<%= @extra_guest.unavailable_dates.to_json %>"></div>

    <div class="col col-sm-6 option-price-delete">
      <%= link_to_remove_association f do %>
      <i class="fas fa-trash"> Delete price</i>
      <% end %>
    </div>
</div>


<script>
  $(document).on('cocoon:after-insert', function(e, added_guest_price_form){
    // $(added_guest_price_form.find("#first_date")).flatpickr();

  const startDateInput = $(added_guest_price_form.find(".first_date"))
  const endDateInput = $(added_guest_price_form.find(".second_date"))

  if (startDateInput && endDateInput) {
    const unavailableDates = JSON.parse(document.querySelector('.widget-content').dataset.unavailable)
    // console.log(unvailableDates)

    flatpickr(startDateInput, {
      format: "d-m-Y",
      altFormat: "d-m-Y",
      altInput: true,
    // minDate: 'today',
    // dateFormat: 'd-m-Y',
    disable: unavailableDates,
    onChange: function(selectedDates, selectedDate) {
      if (selectedDate === '') {
        endDateInput.disabled = true;
      }
      let minDate = selectedDates[0];
      minDate.setDate(minDate.getDate() + 1);
      endDateCalendar.set('minDate', minDate);
      endDateInput.disabled = false;
    }
  });
    const endDateCalendar =
      flatpickr(endDateInput, {
        format: "d-m-Y",
        altFormat: "d-m-Y",
        altInput: true,
        // dateFormat: 'd-m-Y',
        disable: unavailableDates,
        },
      );

};
  });
</script>

删除价格
$(文档).on('cocoon:after insert',函数(e,添加了\u guest\u price\u表单){
//$(添加了_guest_price_form.find(#first_date)).flatpickr();
const startDateInput=$(添加了\u guest\u price\u form.find(“.first\u date”))
const endDateInput=$(添加了\u guest\u price\u form.find(“.second\u date”))
if(startDateInput&&endDateInput){
const unavailableDates=JSON.parse(document.querySelector('.widget content').dataset.unavailable)
//console.log(不可用日期)
FlatPicker(起始日期输入{
格式:“d-m-Y”,
altFormat:“d-m-Y”,
altInput:是的,
//minDate:“今天”,
//日期格式:“d-m-Y”,
禁用:不可用日期,
onChange:函数(selectedDates,selectedDate){
如果(selectedDate==''){
endDateInput.disabled=true;
}
让minDate=selectedDates[0];
minDate.setDate(minDate.getDate()+1);
endDateCalendar.set('minDate',minDate);
endDateInput.disabled=false;
}
});
常量结束日期日历=
FlatPicker(endDateInput{
格式:“d-m-Y”,
altFormat:“d-m-Y”,
altInput:是的,
//日期格式:“d-m-Y”,
禁用:不可用日期,
},
);
};
});
验证器

class ExtraGuestPriceAvailabilityValidator < ActiveModel::EachValidator

  def validate_each(record, attribute, value)
    extra_guest_prices = ExtraGuestPrice.where("extra_guest_id =?", record.extra_guest_id)
    extra_guest_price = ExtraGuestPrice.where("id=?", record.id)

    if extra_guest_price.empty?
      date_ranges = extra_guest_prices.map { |b| b.start_date..b.end_date }
      date_ranges.each do |range|
        if range.include? value
          record.errors.add(attribute, "is overlapping with another period")
        end
      end
    else
      date_ranges = extra_guest_prices.where.not('id=?', record.id).map { |b| b.start_date..b.end_date }
      date_ranges.each do |range|
        if range.include? value
          record.errors.add(attribute, "is overlapping with another period")
        end
      end
    end
  end
end

类ExtraGuestPriceAvailabilityValidator
您如何设置验证器?我认为您正在验证每个ExtraGuestPrice,您应该立即验证整个集合

差不多

class ExtraGuest
  validate :no_dates_overlap

  def no_dates_overlap
    ranges = [] # cache proccessed date ranges

    # iterate over all extra_guest_prices
    extra_guest_prices.each do |egp|
      if ranges.any? { |rng| rng.include?(egp.start_date) || rng.include?(egp.end_date) }
        # if a previous range includes this dates, add the error and break the loop
        errors.add(:extra_guest_prices, "Dates overlap")
        break
      else
        # else, cache it and check the next
        ranges << (egp.start_date..egp.end_date)
      end
    end
  end
类外部来宾
验证:没有日期重叠
def无日期重叠
范围=[]#缓存已处理的日期范围
#迭代所有额外的客人价格
额外的客人价格。每个都是埃及镑|
如果有的话?{| rng | rng.include?(egp.start_date)| rng.include?(egp.end_date)}
#如果以前的范围包括此日期,则添加错误并中断循环
错误。添加(:额外的客人价格,“日期重叠”)
打破
其他的
#否则,缓存它并检查下一个

非常感谢你!这确实正确地触发了验证:)现在,我只需要找到一种显示错误消息的方法,因为它不会显示在表单中(在幕后,它不会在
无日期重叠时进行验证
,但在表单中,它显示了额外客人价格.开始日期和额外客人价格.结束时的积极验证。)_date@techquestion有点离题了,我希望你们能尝试一下我自己的gem用于嵌套表单,它类似于cocoon,但没有gem,也没有jquery依赖项,是吗不太难尝试,因为它非常相似。结束离题的hahatypo,“在幕后,它确实在没有日期重叠的情况下进行验证。@arieljuod,很酷,你做了一块宝石,我一定会看一看;)如果你不想在日期上添加验证,而不是
错误。添加(:额外客人价格,…)
do
egp.errors.add(:开始日期,…)
class ExtraGuest
  validate :no_dates_overlap

  def no_dates_overlap
    ranges = [] # cache proccessed date ranges

    # iterate over all extra_guest_prices
    extra_guest_prices.each do |egp|
      if ranges.any? { |rng| rng.include?(egp.start_date) || rng.include?(egp.end_date) }
        # if a previous range includes this dates, add the error and break the loop
        errors.add(:extra_guest_prices, "Dates overlap")
        break
      else
        # else, cache it and check the next
        ranges << (egp.start_date..egp.end_date)
      end
    end
  end