Column validation for past dates
-
07-02-2021 - |
Question
Looking at implementing column or list validation for a date field to stop them from entering past dates
The validation should do the following
- Validate only there is a value in the column
- Validate only if this particular field has changed. E.g. a user entered today's date and saved the item. They come back and change some other fields but not the date field in question they should be allowed to save
However if they change the date field it should validate that the date is not a past date
La solution
This is possible using JSLink/CSR client-side validation. That is, if you are on a version of SharePoint that still has JSLink/CSR available.
From my understanding, SP Online modern experience does not use the JSLink/CSR system from older versions. I am unsure if SP Online classic experience still has that (can someone who knows confirm in a comment?). SP 2013 / 2016 on-prem definitely has JSLink/CSR available to tap into (and I believe 2019 as well? Again, not sure on that one...)
Here's some example code (tested on 2013):
var MyNamespace = MyNamespace || {};
MyNamespace.PreviousFieldValue = null;
MyNamespace.MyFieldValidator = function myFieldValidator () {
MyNamespace.MyFieldValidator.prototype.Validate = function (fieldValue) {
var isError = false;
var errorMessage = '';
// only validate if the field has a value
if (fieldValue) {
// assume that we do need to check if it is a past date
var shouldCheckIfPastDate = true;
var enteredDate = new Date(fieldValue);
// if there was a previous value, check to see if it has changed
if (MyNamespace.PreviousFieldValue !== null) {
var previousDate = new Date(MyNamespace.PreviousFieldValue);
// if the date has not changed, don't check if it's a past date
// Date object can't do equality comparison natively, so we have to use getTime()
if (enteredDate.getTime() === previousDate.getTime()) {
shouldCheckIfPastDate = false;
}
}
if (shouldCheckIfPastDate) {
// get todays date and zero out the time so we are only comparing the date
var today = new Date();
today.setHours(0, 0, 0, 0);
// Date object can handle greater than/less than comparison natively
if (enteredDate < today) {
isError = true;
errorMessage = 'You cannot enter a past date.';
}
}
}
return new SPClientForms.ClientValidation.ValidationResult(isError, errorMessage);
}
}
MyNamespace.MyFieldFieldOverride = {
edit: function edit (ctx) {
// if it's not blank, store the current value of the field to check against later
if (ctx.CurrentFieldValue) {
MyNamespace.PreviousFieldValue = ctx.CurrentFieldValue;
}
// get the form context and create a new validator set
var formCtx = SPClientTemplates.Utility.GetFormContextForCurrentField(ctx);
var fieldValidators = new SPClientForms.ClientValidation.ValidatorSet();
// if the field has been marked as required, register the default required field validator
if (formCtx.fieldSchema.Required) {
fieldValidators.RegisterValidator(new SPClientForms.ClientValidation.RequiredValidator());
}
// register our custom validator
fieldValidators.RegisterValidator(new MyNamespace.MyFieldValidator());
// add the error callback and register the validator set with the form context
formCtx.registerValidationErrorCallback(formCtx.fieldName, MyNamespace.MyFieldFieldOverride.onError);
formCtx.registerClientValidator(formCtx.fieldName, fieldValidators);
// render the default form control for a date column
var returnHtml = SPFieldDateTime_Edit(ctx);
// add a spot for our custom error message
returnHtml += "<span id='MyFieldCustomError' class='ms-formvalidation ms-csrformvalidation'></span>";
return returnHtml;
},
onError: function onError (error) {
document.getElementById('MyFieldCustomError').innerHTML = "<span role='alert'>" + error.errorMessage + "</span>";
},
render: function render () {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
Templates: {
Fields: {
YourDateField: {
'NewForm': MyNamespace.MyFieldFieldOverride.edit,
'EditForm': MyNamespace.MyFieldFieldOverride.edit
}
}
}
});
}
}
// handle MDS-enabled or MDS-disabled situations
RegisterModuleInit(SPClientRenderer.ReplaceUrlTokens('~site/path/to/MyFieldValidator.js'), MyNamespace.MyFieldFieldOverride.render);
MyNamespace.MyFieldFieldOverride.render();
Autres conseils
As far as I know, it's impossible to validate only if particular field changes. It's by default that column validation would validate the data in this column when items are saved to this list.
I could achieve your first requirements using this formula:
=IF(ISBLANK([Date]),TRUE,[Date]>=TODAY())