How the Form Mixin Enhances Validations in Open Event Frontend
This blog article will illustrate how the various validations come together in Open Event Frontend, in a standard format, strongly reducing the code redundancy in declaring validations. Open Event Frontend, offers high flexibility in terms of validation options, and all are stored in a convenient object notation format as follows:
getValidationRules() { return { inline : true, delay : false, on : 'blur', fields : { identification: { identifier : 'email', rules : [ { type : 'empty', prompt : this.l10n.t('Please enter your email ID') }, { type : 'email', prompt : this.l10n.t('Please enter a valid email ID') } ] }, password: { identifier : 'password', rules : [ { type : 'empty', prompt : this.l10n.t('Please enter your password') } ] } } }; }
Thus the validations of a form are stored as objects, where the identifier attribute determines which field to apply the validation conditions to. The rules array contains all the rules to be applied to the determined object. Within rules, the type represents the kind of validation, whereas the prompt attribute determines what message shall be displayed in case there is a violation of the validation rule.
NOTE: In case an identifier is not specified, then the name of the rule object is itself considered as the identifier.
These validations are in turn implemented by the FormMixin. The various relevant sections of the mixin will be discussed in detail. Please check references for complete source code of the mixin.
getForm() { return this.get(‘$form’); }
The getForm function returns the calling component’s entire form object on which the validations are to be applied.
onValid(callback) { this.getForm().form('validate form'); if (this.getForm().form('is valid')) { callback(); } }
The onValid function serves as the boundary level function, and is used to specify what should happen when the validation is successful. It expects a function object as i’s argument. It makes use of the getForm() function to retrieve the form and using semantic UI, determines if all the validations have been successful. In case they have, it calls the callback method passed down to it as the argument.
Next transitioning into the actual implementation, certain behavioral traits of the validations are controlled by the two global variables autoScrollToErrors and autoScrollSpeed. The former is a boolean, which is set to true if the application is designed in such a way that once the user presses the submit button and a validation fails, the browser scrolls to the first field whose validation failed with a speed specified by the latter.
Next, comes the heart of the mixin. Since it needs to be continuously determined if a field’s validation has failed, the entire logic of checking is placed inside a debounce call which is supported by ember to continuously execute a function with a gap of a certain specified time (400 ms in this case). Since the validity can be checked only after all the fields have rendered, the debounce call is placed inside a didRender call. The didRender call ensures that any logic inside it is executed only after all the elements of the relevant component have been rendered and are a part of the DOM.
didRender() { . . . debounce(this, () => { const defaultFormRules = { onFailure: formErrors => { if (this.autoScrollToErrors) { // Scroll to the first error message if (formErrors.length > 0) { $('html,body').animate({ scrollTop: this.$(`div:contains('${formErrors[0]}')`).offset().top }, this.autoScrollSpeed); } } } }; }, 400); . . .}
onFailure is an ES6 syntax function, which takes all the form errors as its arguments. And in case the autoScrollToErrors global variable is set to true, it checks if there are any errors or the length of the formErrors array is more than 0. In case there are errors, it makes a call to animate callback, and scrolls to the very first field which has an error with the speed defined by the previously discussed global autoScrollSpeed . The fact that the very first field is selected for scrolling off to, is ensured by the index 0 specified (formErrors[0]). Thus role of the mixin is to continuously check for any validations and in case there are, scroll to them.
Resources
- Ember JS-Mixins – Official ember documentation
- Open Event Front End mixin – Complete source code of the mixin file