Error management in grails - javascript

I am quite new to Grails and in an application I need to check dates. In a former Java program I have used two javascript functions with different detail granularity. Both accept dates from 1970-01-01 to 2099-12-31. One demands a correct date and (optionally) time and just tells the user he/she made an erroneous entry:
function okdate1(dtstr) {
var ok = true;
// First trim off leading and trailing white space
var trimPattern = /(?:\b(.*)\b)/;
dtstr = (dtstr.match(trimPattern))[1];
// Verify that input is within range and correct
var pat = /^((?:19[7-9][0-9])|(?:20[0-9][0-9]))-((?:(?:0)?[1-9])|(?:1[0-2]))-((?:(?:0)?[1-9])|(?:[1-2][0-9])|(?:3[01]))(?: ((?:(?:0|1)[0-9])|(?:2[0-3])):([0-5][0-9]))?$/;
var dtm = dtstr.match(pat);
if (!dtm) {
ok = false;
} else { // Verify that day in in range for the given month
var days = Array(31,28,31,30,31,30,31,31,30,31,30,31);
// Compensate for leap year
if ((((dtm[1] % 4) === 0) && !((dtm[1] % 100) === 0)) || ((dtm[1] % 400) === 0)) {
days[1] = 29;
}
if (dtm[3] > days[dtm[2] - 1]) ok = false;
}
if (!ok) alert("Enter date and (optionally) time on the form 'yyyy-MM-dd HH:mm'");
return ok;
}
and the other that checks exactly what went wrong by accepting a wider range on the numeric parts of the input string:
function okdate2(dtstr) {
// First trim off leading and trailing white space
var trimPattern = /(?:\b(.*)\b)/;
dtstr = (dtstr.match(trimPattern))[1];
// If nothing then skip the rest
if (!dtstr) return datetimealert(0);
// Pattern to recognize any 'dddd-dd-dd[ dd:dd]' pattern
var pat = /^(?:(\d{4})-(\d{1,2})-(\d{1,2}))(?: (\d{1,2}):(\d{2}))?$/;
var dtm = dtstr.match(pat);
// If this is does not follow the pattern: get out
if (!dtm) return datetimealert(0);
// convert each group to a number
// if no time notation the corresponding groups become NaN
for (var i = 1; i < dtm.length; i++) {
dtm[i] = Number(dtm[i]);
}
// Check for correct year interval
if (dtm[1] < 1970 || dtm[1] > 2099) return datetimealert(1);
// Check for correct month notation
if (dtm[2] < 1 || dtm[2] > 12) return datetimealert(2);
// Array with correct numer of days for each month
var mdays = Array(31,28,31,30,31,30,31,31,30,31,30,31);
// Compensate for leap year
if ((((dtm[1] % 4) === 0) && !((dtm[1] % 100) === 0)) || ((dtm[1] % 400) === 0)) {
mdays[1] = 29;
}
// Check the day for the given month
if (dtm[3] < 1 || mdays[dtm[2] - 1] < dtm[3]) return datetimealert(3);
// If only date was given and no time, we are OK
if (isNaN(dtm[4]) && isNaN(dtm[5])) return true;
// This can not happen according to pattern, but ...
if (isNaN(dtm[4]) || isNaN(dtm[5])) return datetimealert(4);
// check given hour
if (dtm[4] > 23) return datetimealert(5);
// Check given minutes
if (dtm[5] > 59) return datetimealert(6);
// If no error
return true;
}
where the function datetimealert puts out an alert with a (hopefully) good error message and returns false. The 'trimpattern' in both function strip leading and trailing whitespace.
I used them in my forms where I made calls to them in an "onsubmit" function. My objective here is not to discuss the two functions but comments on them are, of course, welcome.
In my Grails application I use jQuery datepicker extended with Trent Richardsons jQuery timepicker addon, so I get a text string as a result. I call the datetimepicker in a form:
<form ...
<dl ...
<dt>Start date <span class="required-indicator">*</span></dt>
<dd>
<div class="fieldcontain ${hasErrors(bean: todoInstance, field: 'startdate', 'error')} required">
<g:textField name="startdate" id="datepicker" class="datepicker"
value="${formatDate(format:'yyyy-MM-dd HH:mm',date:todoInstance?.startad)}" required="" />
</div>
</dd>
...
For all the other 'required' fields I get a little 'tooltip'-like message telling me to
enter a value into the field.
Now, I want to use my two datetime javascript in my grails application but I don't want alert boxes popping up, I want to use them in the static-constraints section in the domain classes and get my messages in the same manner as for the other fields. How do I integrate them into the error management system i grails?

This error messages are provided by the validation API. To inplement your own customized validations you can use the constraint validator.
But I'm assuming that you already declared your field as java.util.Date in the domain class, so you need a Date object in the validator. By default, Grails handle dates with g:datePicker, that will split the date in day, month and year fields.
To bind a single String with some format to a date object, you can register a custom Date Property Editor, like this example.
The Grails validation API is for server side validation. In your case one option is the JQuery Validation UI Plugin, that provides client side validation through JQuery. The plugin supports all standard constraints and you can create your own validations (like your date validations), checkout the extensibility docs session.

Related

Is there a better way to update html text box string format to HH:MM:SS using jQuery

Alright, after digging through several sites...I am sure there is a better way to get the result I am looking for. Users are entering data in a text box on an HTML form and I want the format to change from 152000 (HHMMSS) to 15:20:00 (HH:MM:SS)
I was able to Frankenstein the jQuery below and it does work but I'm sure there is a better way to achieve the same result. I know I could handle the data after submission but would prefer to use jQuery to update it as they type. From what I read, I could use some type of time format but everything was focused on time as a date and I just need this to be a string that adds a colon after every two digits and limits the length to 8. Any thoughts?
$('#amount').keypress(function() {
// limits the charachters allowed
var regex = new RegExp("^[0-9:]");
var key = String.fromCharCode(event.charCode ? event.which : event.charCode);
if (!regex.test(key)) {
event.preventDefault();
return false;
}
//adds a colon after 2 digits typed
if(this.value.length == 2){
this.value = this.value+':';
}
//adds a colon after 5 character
if(this.value.length == 5){
this.value = this.value+':';
}
//limit to 8 total characters
if(this.value.length > 7) {
return false;
}
});
$('#amount').keypress(function() {
let $input = $(this);
let value = $input.val();
let update = value.replace(/(\d{2})(\d{2})(\d{2})/, '$1:$2:$3');
// 120000 --> 12:00:00
$input.val(update)
})

IE Failing to evaluate second if condition (&&)

So I have this script:
function makeActive() {
var element, name, arr;
element = document.getElementById("liveChat");
name = "active";
arr = element.className.split(" ");
if (arr.indexOf(name) == -1) {
element.className += " " + name;
}
}
var currentTime = new Date();
var currentTimeFormatted = currentTime.toLocaleTimeString();
if(currentTimeFormatted >= '08:00:00' && currentTimeFormatted <= '16:30:00'){
makeActive();
}
Which works perfectly in Chrome, however in IE the class doesn't get added.
If I remove the
&& currentTimeFormatted <= '16:30:00'
IE also adds the class. Why would adding a second condition, break this script within IE?
To make this a tad easier than having to use && and || mix, or if your values are stored somewhere in a static file etc. You could create a kind of pseudo time, by multiply each section.
eg.
const cTime = new Date();
const ptime =
cTime.getHours() * 10000 +
cTime.getMinutes() * 100 +
cTime.getSeconds();
if (ptime >= 80000 && ptime <= 163000) {
console.log("Active");
} else {
console.log("InActive");
}
You are doing string comparisons, which means that the browser and locale dependent output of toLocaleTimeString() screws your code in IE, and possibly also in other browsers or regions, because this function is solely intended for producing a human-readable time representation.
So you should either:
(1) Use a string representation that is standardized, e.g. invoking toISOString(). This will also get rid of time zone problems, because the result will always be in UTC time:
var currentTimeFormatted = new Date().toISOString(); // 2018-11-07T12:28:12.448Z'
currentTimeFormatted = currentTimeFormatted.substr(currentTimeFormatted.indexOf('T') + 1, 8); // 12:27:12
Now the rest of your code will work (assuming you 08:00:00 and 16:30:00 are UTC times).
(2) Extract the hour and minute parts of the new Date() and compare those to integers:
var currentTime = new Date();
if(currentTime.getHours() >= 8
&& // similarly a comparison to < 16:30
) {
makeActive();
}
(3) Use the great solution by Keith (see below), which I think is the best way to go
IE's implementation of date.toLocaleTimeString() adds non-printable characters into the string. The easiest way to deal with them is to trim them from the string;
currentTimeFormatted = currentTime.toLocaleTimeString().replace(/[^ -~]/g,'')
When dealing with localized timezones and timezone comparison, it might be worth trying a library like moment.js which can also deal with comparing values using the isBetween funciton
Edit
As the other solutions have suggested - using toLocaleTimeString() is not a safe method of performing date comparison and should be avoided.

Javascript use split on date range

I get a date range from some API (not in my control) which has the following possibilities of date format
dd-Mmm-yy
dd-Mmm-yyyy
dd/mm/yy
dd/mm/yyyy
mm/dd/yy
mm/dd/yyyy
yyyy-mm-dd
So I get the date range as
01-Dec-16-06-Dec-16 (as per dd-Mmm-yy) OR
01/12/16-06/12/16 (as per dd/mm/yy)
So hyphen (-) is the from/to date separator which the API uses (not in my control) & I get this single combined value
Now, in my code, I want to get the from & to dates separately.
So I use
range.split("-")
However, this does not work properly for 01-Dec-16-06-Dec-16
Not sure what is the best way to account for all the possibilities (considering that the from/to date separator would always be -)
Since this is a case of an ugly API, the only way to do this is by using "hacky" solutions.
Use #Rory McCrossan's suggestion: count the number of - in the string. If 1, split. If 5, split by the third.
Since the API uses the same format of date for both the left side and the right side, the total length of the string will always be ("left side" + "-" + "right side"). You can split the text on the middle character.
e.g.
let date = "01-Dec-16-06-Dec-16";
let idx = date.length / 2;
let start = date.substr(0, idx);
let end = date.substr(idx + 1);
Use regex.
From the formats provided, it looks like the from and to dates will always be the same length split by -. In that case, just do this:
var len = (yourDateRange.length - 1) / 2
var dateFrom = yourDateRange.substring(0, len)
var dateTo = yourDateRange.substring(len + 1)
If you have any format where the length is variable (Such as full name for month), this obviously won't work
It's a bit hacky, but gets the job done.
I used a chained indexOf call with the previous call as the fromIndex parameter (which is the 2nd parameter of indexOf). And seeing as there is either / in the string (then split by -) or not (then split by 3rd -), there was no need for any special checks.
function splitDates(date) {
// ~ is just a fancy way to turn indexOf into a
// boolean-equivalent check (-1 is 0, 0 is 1, etc)
if (~date.indexOf('/')) {
return date.split('-');
} else {
var idx = date.indexOf('-', 1 + date.indexOf('-', 1 + date.indexOf('-')));
return [date.slice(0, idx), date.slice(idx + 1)];
}
}
var dates = ['01-Dec-16-06-Dec-16', '01/12/16-06/12/16'];
dates.forEach((date) => {
alert(splitDates(date))
});

Amex & CC Formatting Javascript - Lets settle this once and for all

I am trying to format credit cards as users type them into the field. I've read every topic on the subject here on stack overflow, looked at tons of sites, lots of libraries and the code behind them. I want to create a simple function that will format credit cards as the user types them into the field using VANILLA JAVASCRIPT. Some of the following code comes from topics found here on Stack Overflow but none of the threads have solved the particular problem of doing this as the user is typing into the field.
PROBLEM: By default as the user is typing the into the given credit card field it changes the value by putting spaces in between the numbers, it will not validate as an American Express card until all the digits have been entered and thus not adjust the format until it is complete. I've tried casting the value without spaces and retesting it every cycle but to no avail.
function cc_format(v) {
//Strip the field value of spaces.
amextest = v.replace(/\s/g, '');
//Test if the card is an american express each cycle
if(/3[47]\d{2}[ -]*\d{6}[ -]*\d{5}/.test(amextest))
{
//This is some borrowed code to format american express cards - http://stackoverflow.com/questions/27322733/javascript-regex-format-string-containing-american-express-card-number
v.replace(/\b(\d{4})(\d{6})(\d{5})\b/, '$1-$2-$3');
return v;
}
else
{
//This properly formats every other card type as its being typed.
var v = v.replace(/[^\d]/g, '').match(/.{1,4}/g);
return v ? v.join(' ') : '';
}
}
//This binds the function to an input
document.getElementById('credit_card').oninput = function() {
this.value = cc_format(this.value)
}
I call upon the gods of stack overflow, please help me put this rest once and for all!
EDIT: Forgot the OP wanted plain JS. I'll leave this here for posterity, but it is obviously not an answer.
You could try this - match on the first two digits, and then automatically update the input after the 4th digit (and prevent an input greater than 17 characters (15 digits and 2 dashes):
$('#cc').on('keyup', function() {
var amexTest = $(this).val().replace(/ /g, '');
if (amexTest.match(/^3[47]\d{2}/)) {
if (amexTest.length == 4) {
amexTest += '-';
$('#cc').val(amexTest);
}
if (amexTest.length == 11) {
amexTest += '-';
$('#cc').val(amexTest);
}
if (amexTest.length > 17) {
val = $(this).val().substr(0, $(this).val().length - 1);
$(this).val(val);
}
} else {
if (amexTest.length > 16) {
val = $(this).val().substr(0, $(this).val().length - 1);
$(this).val(val);
}
if (amexTest.length == 16) {
var splits = amexTest.match(/\d{4}/g);
val = splits.join(' ');
$(this).val(val);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="cc">

Validate particular range using regex

I want validate text box with particular range having format like :
1-99
I am using regex :
/^(?:100|[1-9]\d|\d)-(?:100|[1-9]\d|\d)$/
It works for me but little problem that is it accept this:
55-50
And it shouldn't, this is wrong.
how can I correct this?
As it has been told early regexp is not the method for validating ranges. The better way is to use if/else statements. But you are not restricted in usage of regexp for validating input string on the particular format.
F.i., if you'd like to enable the end user to enter the range in the format number1-number2, you could check the string for compliance to this format and check its parts for complaince to the condition number1 <= number2. If all these checks are done you could do something useful or decline, if checks are fail.
function validRange(rangeStr, min, max) {
var m = rangeStr.match(/^([0-9]+)-([0-9]+)$/);
if ( m && m[1] >= min && m[2] <= max && m[1] <= m[2] ) {
return true;
}
return false;
}
var s = '1-99';
var s = '55-50';
if ( validRange(s, 1, 99) ) {
// do something useful
}
The code above is just skeleton for the further improvements but it can be used now. But the code could be too complicated, if you or your customers will request to implement something more complex like ability to enter single number, lists of numbers (separated with comma, semicolons etc), mixed ranges or any combination of all of them.
Because you need to check validation between the both number you have to use logical operations to check if the forst number is less than second, so you couldn't use regex in this case instead use if/else statement :
var input = "55-50";
if(input.indexOf('-')){
var input_arr = input.split('-');
if(input_arr.length==2 && parseInt(input_arr[0])<parseInt(input_arr[1]))
alert("Accepted");
else
alert("Not accepted");
}

Categories