Support

Account

Home Forums Front-end Issues Sending user to Tab on validation errors

Solving

Sending user to Tab on validation errors

    • edir

    • October 15, 2016 at 11:21 am

    I found a missing feature this time, when you use Tabs to organize a large form, when validating, the message is too poor to indicate an error in a field behind a hidden Tab. I found a solution. I don’t know if it will work for every situation, but I think so!

    
    acf.add_filter('validation_complete', function( json, $form ){
    	if(json.errors) {
    		var field = jQuery('[name="' + json.errors[0].input + '"]', $form).parents('.acf-field');
    			field = field[field.length - 1];
    		var tab = jQuery(field, $form).prev('.acf-field-tab').attr('data-key');
    		jQuery('.acf-tab-wrap a[data-key=' + tab + ']', $form).click();
    	}
    	return json;
    });
    
  • Nice solution, I’m sure that will come in handy for others.

    • diguno

    • December 2, 2016 at 6:26 pm

    Thanks @edir worked like a charm!

  • I’ve expanded on this to just highlight the field group and the tab in which fields below have errors. I haven’t tested this code extensively but it should work with all of the simple fields (everything except repeater, flexible content, etc). Those may work as well but I haven’t tested them.

    This is sort of the approach I’ve been needing when I start getting a lot of fields and a lot of tabs on a page template.

    
    (function($) {
    
    	$(function() {
    
    		var TabErrorMarker = {
    			errorClass: 'acf-tab-validation-errors',
    			errors: [],
    			fields: [],
    			init: function($form) {
    				var self = this;
    				this.$form = $form;
    				this.$tabs = this.$form.find('.acf-tab-wrap a');
    
    				var $groups = this.$form.find('.acf-postbox');
    
    				// Find all Field groups in form and then find all tabs in form.
    				// Then build a tabs array where each tab keeps track of its parent field group as well as its child fields.
    
    				$groups.each(function(gIndex) {
    					var $group = $(this),
    					gKey = $group.attr('id').replace('acf-', ''),
    					$tabs = $group.find('.acf-tab-wrap a');
    
    					$tabs.each(function(index) {
    						self.addTabObject($(this), {
    							index: $group.index(),
    							$el: $group
    						});
    					});
    				});
    
    				// ACF Hooks for adding/removing error classes on tabs and field groups of error fields hidden behind tabs.
    
    				acf.add_filter('validation_complete', this.proxy(this.validateTabs));
    
    				acf.add_action('validation_begin', this.proxy(this.removeErrors));
    
    				acf.add_action('invalid_field', this.proxy(this.addTabError));
    				return this;
    			},
    			addTabObject($tabLink, group) {
    				// Build the tab object before handing it off to the 'this.fields' array.
    				var self = this,
    				key = $tabLink.attr('data-key'),
    				$label = group.$el.find('.acf-field-tab').filter('[data-key="' + key + '"]'),
    				$fields = $label.nextUntil('.acf-field-tab'),
    
    				tab = {
    					index: $tabLink.index(),
    					key: key,
    					$link: $tabLink,
    					$label: $label
    				};
    
    				$fields.each(function(fIndex) {
    					self.addFieldObject($(this), tab, group);
    				});
    
    				return tab;
    			},
    			addFieldObject($field, tab, group) {
    				var key = $field.attr('data-key'),
    				type = $field.attr('data-type'),
    				$input = $field.find('#acf-' + key),
    				field = {
    					index: $field.index(),
    					key: key,
    					type: type,
    					$input: $input,
    					tab: tab,
    					group: group
    				};
    
    				this.fields.push(field);
    				return field;
    			},
    			validateTabs: function(json, $form){
    
    				// Check for validation errors and begin looping through all tabs in the form if any.
    				
    				this.errors = (json.errors || []);
    				if(this.errors.length) {
    					this.addErrors();
    				} else {
    					this.removeErrors();
    				}
    				return json;
    			},
    			addErrors: function() {
    
    				// Loop over each error, find the field related to the error and mark the parent tab with an error class.
    						
    				this.errors.forEach(this.proxy(function(err, index) {
    					var key = this.errors[index].input.replace('acf[', '').replace(']', ''),
    					$field = this.$form.find('.acf-field[data-key="' + key + '"]');
    
    					this.addTabError($field, this.$form);
    				}));
    				return this;
    			},
    			removeErrors: function () {
    
    				// Loop over each tab and check if all fields below that tab are error free.
    				// If so, remove the error class from the tab.
    						
    				var self = this;
    
    				this.$tabs.each(function(i) {
    					self.removeTabError($(this));
    				});
    				return this;
    			},
    			addTabError: function($field) {
    
    				// Add the error class to the tab and field group of any field that has validation errors
    
    				var index = $field.index();
    				key = $field.attr('data-key'),
    				$input = $field.find('[id="acf-' + key + '"]'),
    				$tabLabels = $($field.get(0), this.$form).prevAll('.acf-field-tab'),
    				tabKey = $tabLabels.eq(0).attr('data-key'),
    				$tab = $('.acf-tab-wrap a[data-key=' + tabKey + ']', this.$form),
    				$group = $tab.closest('.acf-postbox');
    
    				$tab.addClass(this.errorClass);
    				$group.addClass(this.errorClass);
    				return this;
    			},
    			removeTabError: function($tabLink) {
    
    				// Remove the error class to the tab and field group of any field that has validation errors
    				
    				var self = this,
    				$tabLinks = $tabLink.parents('ul').find('li a');
    
    				// Loop over each tab link, find all fields with an error class 
    				$tabLinks.each(function(i) {
    					var $tab = $(this),
    					$group = $tab.closest('.acf-postbox'),
    					$errorFields = $tab.parents('.acf-fields').children('.acf-field.acf-error');
    
    					if ($errorFields.length === 0) {
    						$tab.removeClass(self.errorClass);
    						$group.removeClass(self.errorClass);
    					}
    				});
    				return this;
    			},
    			proxy: function(func) {
    
    				// Simple proxy function to ensure the context of a callback function is this object's instance.
    				
    				var self = this;
            return function() {
                return func.apply(self, arguments);
            };
    			}
    		};
    
    		
    
    		$(function () {
    
    			var $form = $('#wpbody form');
    
    			TabErrorMarker.init($form);
    		});
    
    	});
    
    })(jQuery);
    

    And here is the CSS to highlight the tabs and field groups that have errors.

    
    .acf-tab-button.acf-tab-validation-errors {
    	border-color: #F55E4F !important;
    	color: #F55E4F !important;
    }
    .acf-postbox.acf-tab-validation-errors > .hndle {
    	border-color: #F55E4F !important;
    	color: #F55E4F !important;
    }
    .acf-postbox.acf-tab-validation-errors > .handlediv {
    	color: #F55E4F !important;
    }
    

    The result looks like the image below.

    ACF Tab Error Highlight

  • When you have a flexible content area and layouts withs tabs:

    acf.add_filter('validation_complete', function(json, $form){
    	if(json.errors) {
        $.each(json.errors, function(key) {
          const field = $('[name="' + json.errors[key].input + '"]', $form).parents('.acf-field');
          const parent_field = field[field.length - 2];
          const tab = $(parent_field, $form).prev('.acf-field-tab').attr('data-key');
          $('.acf-tab-wrap a[data-key=' + tab + ']', $form).click();
        });
    	}
    
    	return json;
    });
Viewing 5 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic.

We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Cookie Policy. If you continue to use this site, you consent to our use of cookies.