0

I have a form that requires user to enter their digital signature before signing up. It looks like below:

enter image description here

So prior to signing up, user MUST enter their signature on the canvas box provided. I use jquery validation to validate my other fields before coming to this final page for signature.

I can validate all the fields except for the signature field. Any idea what I can do?

<div class="row">
  <div class="col-12 col-md-8 offset-md-2 pl-3 pr-3 pt-2 mb-0">
    <canvas class="display-block signature-pad" style="touch-action: none;"></canvas>
    <p id="signatureError" name="signatureError" style="color: red; display: none;">Please provide your signature.</p>
    <div class="p-1 text-right">
      <button id="resetSignature" class="btn btn-sm" style="background-color: lightblue;">Reset</button>
      <button id="saveSignature" class="btn btn-sm" style="background-color: #fbcc34;">Save</button>
    </div>
    <input type="hidden" name="signature" id="signatureInput">
  </div>
</div>
<div class="row">
  <div class="col-12 mb-0 pt-2">
    <div class="text-right">
      <input type="hidden" name="registrationFor" value="customer">
      <button type="submit" id="submit" class=" btn next-button bjsh-btn-gradient text-right">Sign Up</button>
    </div>
  </div>
</div>
 var canvas = document.querySelector("canvas");
    const signatureSaveButton = document.getElementById("saveSignature");
    const signatureResetButton = document.getElementById("resetSignature");
    const signatureError = document.getElementById("signatureError");
    const signatureInput = document.getElementById("signatureInput");

    // Initialize a new signaturePad instance.
    var signaturePad = new SignaturePad(canvas);

    // Clear signature pad.
    signatureResetButton.addEventListener("click", function(event) {
      signaturePad.clear();
    });

    // Save signature pad as data url.
    signatureSaveButton.addEventListener("click", function(event) {
      if (signaturePad.isEmpty()) {
        signatureError.style.display = "block";
      } else {
        signatureUrl = signaturePad.toDataURL();
        signatureInput.value = signatureUrl;
      }
    });

    // Validate registration tab before moving to the next tab
    $("#register-form").validate({
      rules: {
        email: {
          required: true,
          // Specify that email should be validated
          // by the built-in "email" rule
          email: true
        },
        password: {
          required: true,
          minlength: 8,
        },
        password_confirmation: {
          required: true,
          minlength: 8,
          equalTo: "#password"
        },
        full_name: {
          required: true
        },
        nric: {
          required: true
        },
        address_1: {
          required: true
        },
        address_2: {
          required: true
        },
        address_3: {
          required: true
        },
        postcode: {
          required: true
        },
        city: {
          required: true
        },
        state: {
          required: true
        },
        contact_number_home: {
          required: true
        },
        contact_number_mobile: {
          required: true
        },
        existing_customer: {
          required: true
        },
        signatureError: {
          required: true
        },
      },
      messages: {
        email: {
          required: "Please enter an email",
          email: "The email is not valid"
        },
        password: {
          required: "Please enter a password",
          minlength: "Password must be minimum of 8 characters"
        },
        password_confirmation: {
          required: "Please confirm your password",
          minlength: "Passmust must be minimum of 8 characters",
          equalTo: "Password must be same as above"
        },
        full_name: {
          required: "Please enter your full name"
        },
        nric: {
          required: "Please enter your identity card number"
        },
        address_1: {
          required: "Please enter your address"
        },
        address_2: {
          required: "Please enter your address"
        },
        address_3: {
          required: "Please enter your address"
        },
        postcode: {
          required: "Please enter your postcode"
        },
        city: {
          required: "Please select your city"
        },
        state: {
          required: "Please select your state"
        },
        contact_number_home: {
          required: "Please enter your home number"
        },
        contact_number_mobile: {
          required: "Please enter your mobile number"
        },
        signatureError: {
          required: "Please provide your signature"
        },
      }
    });

    // validate fields in 1st tab
    $('#next-btn').click(function() {
      if ($("#register-form").validate().element('#email') && $("#register-form").validate().element('#password') && $("#register-form").validate().element('#password-confirm')) {
        nextTab.find('a').trigger('click');
      } else {}
    });

    // validate fields in 2nd tab
    $('#next-btn2').click(function() {
      if ($("#register-form").validate().element('#full_name') && $("#register-form").validate().element('#nric') && $("#register-form").validate().element('#address_1') && $("#register-form").validate().element('#address_2') && $("#register-form").validate().element('#address_3') && $("#register-form").validate().element('#postcode') &&
        $("#register-form").validate().element('#city') && $("#register-form").validate().element('#state') && $("#register-form").validate().element('#contact_number_home') &&
        $("#register-form").validate().element('#contact_number_mobile') && $("#register-form").validate().element('#existing_customer')
      ) {
        nextTab.find('a').trigger('click');
      } else {}
    });

    // validate signature input in 3rd tab
    $('#submit').click(function() {
      if ($("#register-form").validate().element('#signatureError')) {
        alert("Success");
      } else {
        alert("Failure");
      }
    });
Lord Jesus
  • 150
  • 3
  • 24
  • 1
    Maybe check that it’s not all white? I can’t see any other possible validation. – evolutionxbox Mar 22 '20 at 12:12
  • 1
    Check the documentation for `SignatuePad`. I would assume it exposes some events you can hook to, such as a notification that the user has filled in their signature. Then you can set a flag on the field which set it as valid. You may also need to write a custom validator for the validate library to read that flag, but there's lots of documentation on how to do that already; it's just 2 lines of code. – Rory McCrossan Mar 22 '20 at 12:23
  • Yeah I am having a hard time as this form is done by my colleague so I need to understand how he declared the signature pad. – Lord Jesus Mar 22 '20 at 12:42

1 Answers1

1

If you are using signature_pad by Szymon Nowak then it looks like you set it up correctly.

Edit: OK, I got the signature field to be part of validation. You need to not ignore hidden fields.

Do not validate the error message, LOL. Validate the actual field.

Also, I added a custom validator to handle validating the signature pad, but since it sets the value of the hidden signature field when you hit save, we only need to validate the signature.

Helpful links


Example

let $form = $("#register-form");
let canvas = document.querySelector('.signature-pad');
let signatureSaveButton = document.getElementById('saveSignature');
let signatureResetButton = document.getElementById('resetSignature');
let signatureInput = document.querySelector('input[name="signature"]');

// Initialize a new signaturePad instance.
let signaturePad = new SignaturePad(canvas);

// Clear signature pad.
signatureResetButton.addEventListener('click', function(event) {
  signaturePad.clear();
  signatureInput.value = '';
  event.preventDefault();
  return false; // prevent submission...
});

// Save signature pad as data url.
signatureSaveButton.addEventListener('click', function(event) {
  let signatureBlank = signaturePad.isEmpty();
  if (!signatureBlank) {
    signatureUrl = signaturePad.toDataURL();
    signatureInput.value = signatureUrl;
    $("div.error-messages span").html(''); // Clear messages
  }
  $(signatureInput).valid(); // Call validation on the field after hitting "Save"
  event.preventDefault();
  return false; // prevent submission...
});

// Not used, because this field has no name. Also, we want to use this
// to set the underlying (hidden) signature field...
$.validator.addMethod('signaturePresent', function(value, element) {
  console.log('Checking...');
  return this.optional(element) || signaturePad.isEmpty();
}, "Please provide your signature...");

// Validate registration tab before moving to the next tab
$form.validate({
  ignore: [], // This is important! We want to validate hidden fields.
  rules: {
    signature: {
      required: true
    }
  },
  messages: {
    signature: {
      required: "Please provide your signature"
    }
  },
  submitHandler: function(form) {
    $("div.error-messages span").html(''); // Clear messages
    console.log('Submitting form...');
    //form.submit(); <-- UNCOMMENT TO ACTUALLY SUBMIT
  },
  invalidHandler: function(event, validator) {
    console.log('INVALID!');
    // 'this' refers to the form
    var errors = validator.numberOfInvalids();
    if (errors) {
      var message = errors == 1
        ? 'You missed 1 field. It has been highlighted'
        : 'You missed ' + errors + ' fields. They have been highlighted';
      $("div.error-messages span").html(message);
      $("div.error").show();
    } else {
      $("div.error").hide();
    }
  }
});
body {
padding: 2em;
}

.signature-pad {
  display: block;
  border: thin solid grey;
  margin: 0 auto;
  margin-bottom: 0.5em;
}

.hidden {
  display: none !important;
}

form .error {
  color: #F00;
}

.error-messages {
  text-align: center;
  font-size: smaller;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.19.1/jquery.validate.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/signature_pad/1.5.3/signature_pad.min.js"></script>
<form id="register-form">
  <div class="row">
    <div class="col-md-6 text-center">
      <label for="signatureInput">Signature</label>
    </div>
    <div class="form-group text-center">
      <canvas class="display-block signature-pad" style="touch-action: none;"></canvas>
      <div>
        <button id="resetSignature" class="btn btn-sm" style="background-color: lightblue;">Reset</button>
        <button id="saveSignature" class="btn btn-sm" style="background-color: #fbcc34;">Save</button>
      </div>
      <input type="hidden" name="signature" id="signatureInput">
    </div>
  </div>
  <div class="row">
    <div class="col-md-6 text-right">
      <input type="hidden" name="registrationFor" value="customer">
      <button type="submit" id="submit" class=" btn next-button bjsh-btn-gradient text-right">Sign Up</button>
    </div>
  </div>
</form>
<div class="error-messages"><strong>Messages:</strong><br/><span></span></div>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • I used the pad from szmet – Lord Jesus Mar 22 '20 at 12:46
  • @LordJesus: Yes, that is his username. – Mr. Polywhirl Mar 22 '20 at 12:46
  • The code is a little bit long, I can't detect what new line that you added. Do you mind hightlighting it? – Lord Jesus Mar 22 '20 at 12:48
  • @LordJesus: Well, you did not use an [MCVE](https://stackoverflow.com/help/minimal-reproducible-example), so I am not entirely sure. But as long as you included the required libraries, your validations should work just fine. I updated the response to include changes. – Mr. Polywhirl Mar 22 '20 at 12:48
  • Oops , I did include the first few lines in my script originally. Please checked my edited post to confirm. – Lord Jesus Mar 22 '20 at 12:50
  • So I tested your answer, but the alert trigger is not fired in either case. My version of signature pad import is `2.3.2`. Not sure if it matters. – Lord Jesus Mar 22 '20 at 12:56
  • @LordJesus I believe I figured it out. I completely changed the code to only validate the signature portion of the form, since I do not have or care about any other field. – Mr. Polywhirl Mar 22 '20 at 13:52
  • I figured it out although it's not the same as yours. I basically just check `signaturePad.isEmpty()` and it will check if the pad is signed or not. – Lord Jesus Mar 23 '20 at 02:01
  • @LordJesus I added a custom `signaturePresent` validator, but I did not use it, because you can just validate if you saved a signature to the hidden field. – Mr. Polywhirl Mar 23 '20 at 02:24
  • I mean, my way is easier since `SignaturePad` is declared already. Just use it. But thanks for your help. Maybe change your answer to it and I'll mark it – Lord Jesus Mar 23 '20 at 02:47