Introducing Custom Validations for Start-End DateTime scenarios on Open Event Frontend
Several modules of Open Event Frontend involve start and end date-times. While for simple type fields like text, dropdowns or radio buttons, default semantic UI validations are available, which are used inside the app via the form mixin. Any component which extends the form mixin can make specify the validations required using the getValidationRules hook. For instance, this set of rules will enforce validations on the field called ticket_price which will prohibit it from being left empty, or something other than a real number. getValidationRules() { ticketPrice: { identifier : 'ticket_price', rules : [ { type : 'empty', prompt : this.l10n.t('Please give your ticket a price') }, { type : 'number', prompt : this.l10n.t('Please give a proper price for you ticket') }, { type : 'decimal[0..]', prompt : this.l10n.t('Ticket price should be greater than 0') } ] }, } The validations provided by semantic UI only extend to single fields, and are independent of each other. However, in our use case we have four fields: Start dateStart timeEnd dateEnd time The general requirement is that the DateTime object formed by joining the start date and the start time should be before the DateTime object obtained by joining the end date and time. Also, these four distributed fields exist only on the frontend, on the server they are actually just two fields startsAt and endsAt each carrying UTC time values of DateTime objects. To split them into date and time a new computed function is created which is invoked in the model definitions of various resources. Consider the two following complex fields defined in the model according to the schema on the server. startsAt : attr('moment'), endsAt : attr('moment') In order to split them into two that helper is used as follows: startsAtDate : computedDateTimeSplit.bind(this)('startsAt', 'date', 'endsAt'), startsAtTime : computedDateTimeSplit.bind(this)('startsAt', 'time', 'endsAt'), endsAtDate : computedDateTimeSplit.bind(this)('endsAt', 'date') endsAtTime : computedDateTimeSplit.bind(this)('endsAt', 'time'), These values can then be used inside individual fields. To enhance the user experience jquery Calendar module is used to allow the user to enter the date and time values using a calendar and time picker as shown below. The computedDateTimeSplit helper, takes in the property whose part it is splitting, along with the specification of the part it will split. It also takes an optional endProperty argument, which is passed if it is being called for a start property. This function returns a pair of getters and setters,the get function returns the part of datetime object requested like, date or time where as the setter sets these values each time this function is called. export const computedDateTimeSplit = function(property, segmentFormat, endProperty) { return computed(property, { get() { return moment(this.get(property)).format(getFormat(segmentFormat)); }, set(key, value) { const newDate = moment(value, getFormat(segmentFormat)); let oldDate = newDate; if (segmentFormat === 'time') { oldDate.hour(newDate.hour()); oldDate.minute(newDate.minute()); } else if (segmentFormat === 'date') { oldDate.date(newDate.date()); oldDate.month(newDate.month()); oldDate.year(newDate.year()); } else { oldDate = newDate; } this.set(property, oldDate); } return value; } }); }; With this complex set up it is not possible to use semantic UI validations, hence we…
