How to make a dynamic multi-page form

I’ve made a form that uses a post type (question) to create form fields on gform_pre_render. Now I’d like to be able to separate each of these questions onto their own pages but the navigation is having issues. I think there might be some properties missing from the page field, but I don’t know which ones. I tried adding in the pageNumber, but it didn’t seem to help.

add_filter( 'gform_pre_render_1', 'add_assessment_questions');
add_filter( 'gform_admin_pre_render_1', 'add_assessment_questions'); //need this to see values in entries
add_filter( 'gform_pre_validation_1', 'add_assessment_questions');
//add_filter( 'gform_pre_submission_filter_1', 'add_assessment_questions'); //this is duplicating the questions in the notification email
function add_assessment_questions($form){
	if ( rgget( 'page' ) == 'gf_edit_forms' ) {
		//don't add the fields on the form edit page or it duplicates them if the form is saved
		return $form;
	}
	$scale = array(
        'Never',
        'Almost Never',
        'Sometimes',
        'Often',
        'Most of the Time',
        'Almost Always'
    );
    foreach($scale as $i => $option){
        $choices[] = array( 'text' => $option, 'value' => $i );
    }
    $pagen = 2;
	//get each of our questions
	$loop = new WP_Query( array( 'post_type' => 'question', 'posts_per_page' => -1, 'order' => 'ASC', 'orderby' => 'menu_order' ) );
	if($loop->have_posts()) {
	    while($loop->have_posts()) {
	        $loop->the_post();
	        $pagen++;
	        $title = get_the_title();
	        $id = get_the_ID();
	        $props = array( 
	            'id' => $id,
	            'label' => $title,
	            'type' => 'radio',
	            'choices' => $choices,
	        );
	        $field = GF_Fields::create( $props );
	        array_push( $form['fields'], $field );
	        
	        //now i'd like to make it one question per page
	        $page = GF_Fields::create( array('id' => intval(101 + $id), 'type' => 'page', 'pageNumber' => $pagen) );
	        array_push( $form['fields'], $page );
	    }
	}
	return $form;
}

For something custom like this, I recommend creating a multi-page form in the form builder UI, and then viewing the source of that. Then, compare that to the form you output with your custom gform_pre_render code to see what the differences are and what you need to change.

I actually did do that and dumped the fields array. The dynamic pages I tried to make didn’t have all of the properties of one made in the ui, so I thought there must be some step or extra property I’d have to add to mine.

Field made in ui:

[1] => GF_Field_Page Object
        (
            [type] => page
            [_is_entry_detail:GF_Field:private] => 
            [_context_properties:GF_Field:private] => Array
                (
                )

            [_merge_tag_modifiers:GF_Field:private] => Array
                (
                )

            [id] => 3
            [label] => 
            [adminLabel] => 
            [isRequired] => 
            [size] => medium
            [errorMessage] => 
            [visibility] => visible
            [inputs] => 
            [displayOnly] => 1
            [nextButton] => Array
                (
                    [type] => text
                    [text] => Next
                    [imageUrl] => 
                )

            [previousButton] => Array
                (
                    [type] => text
                    [text] => Previous
                    [imageUrl] => 
                )

            [formId] => 1
            [description] => 
            [allowsPrepopulate] => 
            [inputMask] => 
            [inputMaskValue] => 
            [inputMaskIsCustom] => 
            [maxLength] => 
            [inputType] => 
            [labelPlacement] => 
            [descriptionPlacement] => 
            [subLabelPlacement] => 
            [placeholder] => 
            [cssClass] => 
            [inputName] => 
            [noDuplicates] => 
            [defaultValue] => 
            [choices] => 
            [conditionalLogic] => 
            [productField] => 
            [pageNumber] => 2
            [fields] => 

Field made with code:

[4] => GF_Field_Page Object
        (
            [type] => page
            [_is_entry_detail:GF_Field:private] => 
            [_context_properties:GF_Field:private] => Array
                (
                )

            [_merge_tag_modifiers:GF_Field:private] => Array
                (
                )

            [id] => 122
            [pageNumber] => 3

I was able to get the multi-page pagination to work by using the gform_form_post_get_meta_1 hook instead of the other 4.

However, the values of the dynamic radio fields are not carried over from page to page. I tried setting up prepopulation without success

'allowsPrepopulate' => 1,
'inputName' => 'input_'.$id,

I had to do 2 more things and now it seems to be worKing so far

  1. I had put an empty hidden field at the beginning of the form to help test the paging. I think the empty value here was messing with the carrying over of other fields, so I removed it. (having a text field first and leaving it empty doesn’t cause an issue)

  2. The choices values I was using for the array seemed to get in the way:

    foreach($scale as $i => $option){
    $choices[] = array( ‘text’ => $option, ‘value’ => $i );
    }

Since that first $i would be 0, even if none of the questions were input with 0, it would cause the form to submit without all of the questions. I changed the values to just use the $option text.

I solved this in a different way, created a “shared fields” plugin that lets you reuse fields in multiple forms (like a set of forms to build up a profile, and that saved data is accessible to other forms as well). I have some demos of it and documentation.