Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom date picker and "js show if" #603

Closed
myaptad opened this issue Jan 4, 2023 · 9 comments
Closed

custom date picker and "js show if" #603

myaptad opened this issue Jan 4, 2023 · 9 comments

Comments

@myaptad
Copy link

myaptad commented Jan 4, 2023

Problem: Date dependent controls show and hide perfectly with DA's built-in date picker using "js show if" but stop to show/hide with datereplace.js included in the interview.
Question: Is there anything specific that needs to be taken into account while using "js show if" with variable set by custom date picker like as demonstrated here https://docassemble.org/docs/recipes.html#customdate . I see note that datereplace.js adds nameless elements to DOM but for "js show if" DA matches element name mentioned in javascript expression. I also tried to trigger onchange on the hidden element but that does not seem to help. Thanks

@myaptad myaptad changed the title custom date picker using "js show if" custom date picker and "js show if" Jan 4, 2023
@jhpyle
Copy link
Owner

jhpyle commented Jan 6, 2023

I think you were on the right track by triggering change on the hidden element. I got it to work with:

$(document).on("daPageLoad", function () {
  $('input[type="date"]').each(function () {
    var dateElement = this;
    $(dateElement).attr("type", "hidden");
    var parentElement = $('<div class="row px-1">');
    var monthParent = $('<div class="col-sm-5 px-0">');
    var dayParent = $('<div class="col-sm-4 px-0">');
    var yearParent = $('<div class="col-sm-3 px-0">');
    var monthElement = $('<select class="form-select">');
    var dayElement = $('<select class="form-select">');
    var yearElement = $('<select class="form-select">');
    var today = new Date();
    var dateEntered;
    if ($(dateElement).val()) {
      var utcDate = new Date($(dateElement).val());
      dateEntered = new Date(
        utcDate.getUTCFullYear(),
        utcDate.getUTCMonth(),
        utcDate.getUTCDate()
      );
    } else {
      dateEntered = null;
    }
    var opt = $("<option>");
    opt.val("");
    opt.text("Month");
    monthElement.append(opt);
    for (var month = 0; month < 12; month++) {
      opt = $("<option>");
      if (month < 9) {
        opt.val("0" + (month + 1));
      } else {
        opt.val(month + 1);
      }
      var dt = new Date(1970, month, 15);
      opt.text(dt.toLocaleString("default", { month: "long" }));
      if (dateEntered && month == dateEntered.getMonth()) {
        opt.attr("selected", "selected");
      }
      monthElement.append(opt);
    }
    opt = $("<option>");
    opt.val("");
    opt.text("Day");
    dayElement.append(opt);
    for (var day = 1; day <= 31; day++) {
      var opt = $("<option>");
      if (day < 10) {
        opt.val("0" + day);
      } else {
        opt.val(day);
      }
      opt.text(day);
      if (dateEntered && day == dateEntered.getDate()) {
        opt.attr("selected", "selected");
      }
      dayElement.append(opt);
    }
    opt = $("<option>");
    opt.val("");
    opt.text("Year");
    yearElement.append(opt);
    for (
      var year = today.getFullYear();
      year > today.getFullYear() - 50;
      year--
    ) {
      opt = $("<option>");
      opt.val(year);
      opt.text(year);
      if (dateEntered && year == dateEntered.getFullYear()) {
        opt.attr("selected", "selected");
      }
      yearElement.append(opt);
    }
    function updateDate() {
      if (
        $(yearElement).val() != "" &&
        $(monthElement).val() != "" &&
        $(dayElement).val() != ""
      ) {
        $(dateElement).val(
          $(yearElement).val() +
            "-" +
            $(monthElement).val() +
            "-" +
            $(dayElement).val()
        );
        $(dateElement).trigger("change");
      }
    }
    $(dateElement).before(parentElement);
    $(monthParent).append(monthElement);
    $(parentElement).append(monthParent);
    $(dayParent).append(dayElement);
    $(parentElement).append(dayParent);
    $(yearParent).append(yearElement);
    $(parentElement).append(yearParent);
    yearElement.on("change", updateDate);
    monthElement.on("change", updateDate);
    dayElement.on("change", updateDate);
    updateDate();
  });
});

and

features:
  javascript: datereplace.js
---
question: |
  When were you born?
fields:
  - Date of birth: date_of_birth
    datatype: date
  - "Do you remember the dinosaurs?": remembers_dinosaurs
    datatype: yesnoradio
    js show if: |
      val('date_of_birth') && Date.parse(val('date_of_birth')) < Date.parse('1980-01-01')
---
mandatory: True
question: |
  You were born on ${ date_of_birth }.

The Bootstrap classes in the JavaScript file were out of date so I changed them.

@myaptad
Copy link
Author

myaptad commented Jan 8, 2023

Hello, Thanks alot for looking into this. I confirm this issue to have been fixed. I am marking this ticket to closed. Much appreciated. Thanks!

@myaptad myaptad closed this as completed Jan 8, 2023
@myaptad
Copy link
Author

myaptad commented Jan 12, 2023

Hello,

  • I am not sure if there is a quick workaround to pass min & max attributes to custom datepicker from the hidden control? The getAttribute function inside .js does not catch any values set in .yml for the hidden date field for min/max attributes.

  • Alternatively, is it possible for user to type the date instead of using date picker as only input option on android devices. Default date picker seems to be somewhat confusing in that it does not lend well for the dates such as date-of-birth which are far back. Although it allows quick year selection but year drop down initiation is not obvious to the user on android.

These are related to the date control so re-opening the ticket instead of new issue.

Thanks

@jhpyle
Copy link
Owner

jhpyle commented Jan 13, 2023

The min and max validators work for me when I add them to the recipe.

question: |
  When were you born?
fields:
  - Date of birth: date_of_birth
    datatype: date
    min: 1970-01-01
    max: 2020-01-01

The validators work via the jQuery Validation plugin, so the max and min values aren't in the <input> element itself. They are in daValidator.settings.rules.

You could write JavaScript that detects if the device is Android and then converts the field to text:

$(document).on("daPageLoad", function () {
  var isAndroid = // your code here;
  if (isAndroid){
    $('input[type="date"]').each(function () {
      $(this).attr('type', 'text');
    });
  }
});

so that the browser treats the field as plain text.

@myaptad
Copy link
Author

myaptad commented Jan 13, 2023

Thank you for reverting back on this. I should have been bit more clear earlier. Let's see if I can clarify further:

  • min and max do work on default "date" type. However, with datereplace.js included in the interview, there does seem to be break in this functionality as there is no mechanism for the hidden control to enforce these min and max for validation on the nameless control.
  • yes that is doable, however, changing the field to simple text will remove other benefits such as validation etc of having type as "date". It seems like behavior of datepicker is different on desktop than how it is on android (mobile devices). On desktop, datepicker shows up only when user clicks on the calendar icon, however, in mobile version datepicker shows up on click anywhere in the input box. I guess, the functionality as on desktop works great as it allows user to choose how they want to enter the date. Having the same behavior for date entry field on mobile devices, if possible, will allow for best of both worlds.

@jhpyle
Copy link
Owner

jhpyle commented Jan 13, 2023

I am not seeing what you are seeing. When I use my datereplace.js, the standard docassemble input validation functionality based on the jQuery Validation plugin works as normal, and I get the warning messages if the date is outside the min or max. Also when I use JavaScript to change the type of the input element to text, the jQuery Validation plugin makes sure that I enter a valid date.

@myaptad
Copy link
Author

myaptad commented Jan 16, 2023

Hello there, I can confirm that setting the field to text in javascript does work along with its proper validation.

However, after playing around a bit, it seems like there is a usage specific behavior or possibly a bug in the code. We can see the behavior using code below which has minor changes to your example. The validation stops working when the last field is commented/removed as shown. Do you see some issue with usage as such?

features:
  javascript: datereplace.js
---
question: |
  When were you born?
fields:
  - Date of birth: date_of_birth
    datatype: date
    max: ${ today() }
  # - "Do you remember the dinosaurs?": remembers_dinosaurs
  #   datatype: yesnoradio
  #   js show if: |
  #     val('date_of_birth') && Date.parse(val('date_of_birth')) < Date.parse('1980-01-01')
---
mandatory: True
question: |
  You were born on ${ date_of_birth }.

@jhpyle
Copy link
Owner

jhpyle commented Jan 17, 2023

In 1.4.27 I changed the jQuery Validation Plugin settings so that this will work more consistently.

@myaptad
Copy link
Author

myaptad commented Jan 20, 2023

Works now, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants