Arbitrary page navigation for Multi-Page Forms using Javascript gform.submission

After upgrading from Gravity Forms 2.8 to 2.9 my custom multi-page form navigation has stopped working, giving the following errors in the browser console:

Warning. Unsupported submission flow detected for form [#ID].

Aborting submission. Active button not found for form [#ID].

My page navigation allows users to jump between certain pages (eg: from page 9 to page 3), and not between directly adjacent pages via next / previous.

Previously I was doing this by modifying the value of the gform_target_page_number_[id] input field to the value of whichever page number I wanted, then using the JS requestSubmit() method.

Looking through the Gravity Forms JS docs, it seems the correct way to do this now is using the gform.submission object, with the SUBMISSION_TYPE_PREVIOUS or SUBMISSION_TYPE_NEXT argument eg:

await submission.submitForm(form, submission.SUBMISSION_TYPE_PREVIOUS, submission.getSubmissionMethod(form));

This however only moves to directly adjacent pages. What’s the correct approach using gform.submission to navigate between arbitrarily defined form pages?

I don’t need to worry about form page validation in my case as I’ve already got checks in place to ensure users can only jump forward to a page once they’ve already completed it.

Our Multi-page Navigation add-on does the same thing (updates the value of the #gform_target_page_number_[id] hidden input and submits the forms and we’re not seeing this issue. Here’s the function we use:

static postToPage(page: string | number, formId: string | number, bypassValidation?: boolean): void {
	const $form = $('form#gform_' + formId);
	const $targetPageInput = $form.find('input#gform_target_page_number_' + formId);
	
	// We need to get the instance to access its methods
	const instance = (window as any)[`gpmpn_${formId}`] as GPMultiPageNavigation | undefined;
	const currentPage = instance ? instance.getCurrentPage() : null;

	$targetPageInput.val(page);

	// Handle GPPT Soft Validation differently. Posting the form will do some weird stuff!
	const gppt = (window as any)[`GPPageTransitions_${formId}`];

	if (typeof gppt !== 'undefined' && gppt.enableSoftValidation) {
		if (!bypassValidation && !gppt.validate()) {
			return;
		}

		/*
		 * If the page that we're submitting to is 0 (end of the form), go straight to submit handler.
		 *
		 * GPPT will not do any handling for the resubmit/next page with errors buttons.
		 */
		if (page == 0) {
			$form.submit();
			return;
		}

		// Get the instance
		const self = (window as any)[`gpmpn_${formId}`] as GPMultiPageNavigation | undefined;
		
		if (!self) {
			return;
		}

		// Get the index for the next page.
		const $activeSlides = self.$formElem
			.find('.swiper-slide:not(.swiper-slide-disabled)');

		const $targetSlide = self.$formElem.find('#gform_page_' + self.formId + '_' + page);

		// Transition to the slide.
		gppt.swiper.slideTo($activeSlides.index($targetSlide));

		// Update page numbers, the naming is admittedly kind of confusing.
		gppt.currentPage = page;
		gppt.sourcePage = currentPage;

		gppt.updateProgressIndicator(currentPage);

		self.$formElem.trigger('softValidationPageLoad.gppt', [
			page,
			currentPage,
			formId,
		]);

		return;
	}

	if (bypassValidation) {
		const $bypassValidationInput = $('<input type="hidden" name="gw_bypass_validation" id="gw_bypass_validation" value="1" />');
		$form.append($bypassValidationInput);
	}

	/**
	 * If submit buttons are hidden via conditional logic (next/prev/submit), form will not be able to submit; this code finds
	 * all hidden submit inputs and hides them in a way that will still enable submission.
	 */
	$form.find('.gform_page_footer:visible').find('input[type="submit"], input[type="button"]').not(':visible').css({ display: 'block', visibility: 'hidden', position: 'absolute' });

	/**
	 * If attempting to submit the form (page = 0, happens w/ "Next Page with Errors" button), move the Submit
	 * button to the current page so Gravity Forms will not abort the submission.
	 */
	if (parseInt(page as string) === 0) {
		$('#gform_submit_button_' + formId).appendTo('.gform_page_footer:visible').css({ display: 'block', visibility: 'hidden', position: 'absolute' });
		/**
		 * GF adds spinners to all Submit and Next buttons when the form is submitted as only one of these button
		 * types is visible for each page. GPMPN moves the submit button to the current page when submitting the
		 * form early (i.e. Next Page with Errors button). This results in multiple spinners showing as the
		 * Submit and Next buttons are on the same page. To resolve this, we set our custom buttons as the spinner
		 * target so only a single spinner is displayed.
		 */
		window.gform.addFilter('gform_spinner_target_elem', function($target) {
			// GF doesn't provide a way to check if a function is already bound to a filter so let's remove it
			// as part of the function and it will be rebound when it is applicable again.
			window.gform.removeFilter('gform_spinner_target_elem', 10, 'gpmpn_set_spinner_target');
			const $nextPage = $('.gform_next_page_errors_button:visible, .gform_resubmit_button:visible');
			return $nextPage.length ? $nextPage : $target;
		}, 10, 'gpmpn_set_spinner_target');
	}

	$form.submit();
}

There’s a lot of stuff in there specific to GPMPN (and it’s sibling GP Page Transitions) but maybe you’ll find it helpful. :folded_hands: