How to create a set of fields dynamically on page load?

Hi, I’m trying to find out if it’s possible to have a form that has a set of default questions, and programmatically append a set of basic fields (input, textarea, radio buttons) based on a 3rd party service’s API response with the field data.

The set of fields can change regularly and also depends on a hidden field that is populated with the ID of the question set (through a query var).

What’s a good place to start? Is it possible to append a few fields on pre_render which gets removed once the form is submitted? The forms destination is a 3rd party API as well

1 Like

The gform_pre_render filter can be used to manipulate the contents of the form: https://docs.gravityforms.com/gform_pre_render/

Since the fields wouldn’t be added through the form editor, those programmatically added fields can’t be used for feeds etc. But from what I get from your question, that wouldn’t be an issue in this case :slight_smile:

1 Like

Thank you Hiranthi. Hmm, that would be an issue since I’m putting together a Feed add on to handle the submission part.

I was thinking of these possible scenarios (but all of them seem “hacky” and it feels like I’m missing the obvious simple solution):

  1. Setting up a hidden field or administrative text area which stores the data from all the programmatically added fields in JSON format.
    • That should give me the data I need and I can process it within the feed.
    • The difficulty I’m guessing here would be that using “Save & Continue” would need some logic which can map the values of that JSON to the fields pulled in whenever someone continues a form. If fields are not there anymore that the user has previously filled it, it would have to deal with that.
    • Multi-page form validation could be a tricky thing to do as well if new fields are added between the user starting a form and using “Save & Continue” returns to it later.
  2. Setting up a number of hidden fields which get populated by the fields pulled in from the 3rd party service.
    • I don’t like this one. Feels like it’s similar to #1 but more of a headache to maintain and I’d need to make sure there’s a hidden field for x number of fields
  3. Ditch the idea of pulling in the fields dynamically and create an “importer” which can build a static form from the third party service
  4. Schedule a build/update of the form every x minutes/hours through the GF API and actually have all the fields in there
    • I’m guessing I’d have to find a way to also update the mapping of the Feed add-on here.

I’m currently leaning towards the last 2 options but I’m a bit unsure if I want to go down the path of a completely automated updater (looks like a recipe for sync errors that can bring down a form completely).

Pulling in fields from another service just adds an extra layer of complexity and slows down loading time (and then you deal with caching the requests, deal with whatever happens if the API is down etc).

Am I just trying too desperately to make gravityforms the answer to my problems? It usually is…

You mentioned the fields can change regularly. Could you elaborate on that? Would it be hourly, daily?

And if the fields change, is there some sort of webhook that could be used?

Definitely not a related situation, but with my Moneybird WP I’m caching part of the data through transients. The transients will be built again when they expire, but I’ve also added a ‘delete transients’ option to have them rebuild when needed. For when stuff changes in the Moneybird account and it’s necessary those changes are reflected immediately in WP too.

Would something like that be possible (and perhaps with some sort of webhook construction)?

2 Likes

Thanks Hiranthi. Realistically, I think the changes to the fields would be closer to weekly/monthly than daily. That’s part of the reason why I’m leaning towards a more manual field update instead of constantly checking the API for updates. The only issue is that if the updated field is a required field, it’ll break the integration until the gravity form is updated. So there needs to be some manual updating and/or solid notification about failures.

There’s no webhook as far as I know. It would have to just be pulled in and compared with the previous version field by field.

The transients idea is fantastic, thank you!! I haven’t worked with them before so I’ll put that high on the reading list. It would massively simplify the caching problem by the sounds of it.

thanks again for your help!

Working with transients is (once you know how :wink: ) not that hard and they can be extremely useful!

Is there a specific time (or time before) those field-changes would occur? Would that always be in the night, or at 3PM (or something like that)? Or is it a less set-time (like between 9AM and 5PM)?

If you know roughly at what time the new fields would definitely be known, I think I’d setup a cron (through WP-Cron, I tend to deactivate it in WP itself and setup a set cronjob for wp-cron, to reduce the load etc.) to pull in the fields daily, save that in a transient and use those fields for the day. With the possibility of doing a manual ‘pull’ when wanted/needed and with that also delete & re-add the transient(s) for the fields.

When forms give errors (because the fields have changed & the transient(s) aren’t reflecting those changes yet), I guess I’d save the form in some way (timestamp-option or something?), delete & re-add the transient(s) as with a manual pull and depending on the kind of changes: figure out if there’s a way to use the saved form and process it with the new fields or (if that’s not possible) send an email to the admin and/or person that submitted the form to give a heads up (and perhaps request them to fill it in again?).

Handy links for getting the hang of transients:

Good luck! :slight_smile:

Haha “once you know how” :smiley: Thanks for the links, I’m checking them out now.

The updates are likely to be at a less set time since the people who edit those can be in different timezones. I think the transients with manual override is a good start.

I was also thinking to store the fields as an option and regularly compare them to see if anything changes (and notify the admin if it did) so they can review. But I might be over-thinking it again so I’ll park that for now. It’s been keeping me up at night for too long! :joy:

The initial idea was to go through GF so that it can silently fail without notifying the user (for example if the API is down). I’ve started to put together an email notification setup that emails errors and notifications though. So that manual fixing by the admin should be possible.

Hmmm, I’ll get started with the transients first and go from there :slight_smile:

thanks again for your input!

Hi there @hiranthi not hijack this, but I’ve gone down that path and fields created in gform_pre_render doesn’t save or seem to send the new fields to the next steps.

I have a similar requirement, except that I’m basing the created fields off of WordPress data. I’ve added a test field but it doesn’t get added to the fields in the {all_fields} placeholder notifications or to the database.

Am I missing a step?

A simple test output the field, but nothing gets saved:

add_filter( 'gform_pre_render_1', 'fg_add_product_fields' );

function fg_add_product_fields( $form ){
	$props = array( 
		'id' => 123,
		'label' => 'My Field Label',
		'type' => 'text'
	);
	$field = GF_Fields::create( $props );
	array_push( $form['fields'], $field );
	return $form;
}

I’m stuck on the same step. Found something in the docs saying you have to use pre_render, pre_validation and pre_submission filters together.

When I try, the validation of required fields is skipped too.

I’m not sure if I have to get the $POST variables and somehow pass them through to GF_Fields::create so it can do the validation and then the submission as well?

Not sure if there’s a built in method for this

Or how this setup would look if you use hidden fields to submit the values. How does the passing of values happen between the dynamically created fields and the hidden fields.

Just started reading through GF_Fields but my understanding of it is too limited…

To add to my previous comment, this is an example of where I’m stuck (using Gary’s post as starting point). The value gets posted but it doesn’t go back into the field and doesn’t get saved… I thought about setting the defaultValue but that doesn’t make any sense.

add_filter( 'gform_pre_render_1', 'fg_add_product_fields' );
add_filter( 'gform_pre_validation_1', 'fg_add_product_fields' );
add_filter( 'gform_pre_submission_1', 'fg_add_product_fields' );

function fg_add_product_fields( $form ){
	$field_id 		= 123;
	
	//get the POST value for the field???
	$field_value 	= rgpost('input_'.$field_id);
	
	$props = array( 
		'id' => $field_id,
		'label' => 'My Field Label',
		'type' => 'text',
		'isRequired' => true,
		//'defaultValue' => $field_value,
	);
	//checking if the field already exists (otherwise on validation failure, we'll get duplicate fields)
	if (!in_array($field_id,array_column($form['fields'], 'id'))) {
		$field = GF_Fields::create( $props );
		array_push( $form['fields'], $field );
	}
	return $form;
}

@hiranthi, I too am adding fields to the form during gform_pre_render, et. al. And the fields are not being saved in my paypal feed, nor in the entry. Anyone find a solution???

Any solutions here? Having the same issue.
Want to store a dynamically added field into my entries and send via mail.

Have you guys tried to use this? https://docs.gravityforms.com/how-to-add-field-to-form-using-gfapi/

I did and it creates the fields every time you refresh the page, so I’m not sure what action hooks should be used to execute that code.

I created a hidden text field where i put the data in within the gform_pre_submission hook. It’s a workaround but it works.

In this example i query my custom ‘brochures’ post type and create for every post a product field. The values are captured in the hidden textarea field.

add_filter('gform_pre_render_1', 'fg_add_product_fields');
add_filter('gform_pre_validation_1', 'fg_add_product_fields');
add_filter('gform_pre_submission_1', 'fg_add_product_fields_to_hidden_field');

function fg_add_product_fields_to_hidden_field($form)
    {
        $products     = [];
        $hidden_field = 'input_22';

        $args = array(
            'post_type' => 'brochures'
        );

        $query = new WP_Query($args);

        while ($query->have_posts()) {
            $query->the_post();

            $field_id = get_the_ID();

            $product_title    = $_POST[sprintf('input_%d_1', $field_id)];
            $product_price    = $_POST[sprintf('input_%d_2', $field_id)];
            $product_quantity = $_POST[sprintf('input_%d_3', $field_id)];

            if ( ! empty($product_quantity)) {
                $products[] = sprintf('%dx %s - € %s', $product_quantity, $product_title, $product_price);
            }
        }

        if (count($products)) {
            $_POST[$hidden_field] = implode(PHP_EOL, $products);
        }
    }

function fg_add_product_fields($form)
    {
        $args = array(
            'post_type' => 'brochures'
        );

        $query = new WP_Query($args);

        while ($query->have_posts()) {
            $query->the_post();

            $field_id = get_the_ID();
            
            if (is_user_logged_in()) {
                $prijs = floatval(get_field('prijs_leden'));
            } else {
                $prijs = floatval(get_field('prijs'));
            }

            $props = array(
                'id'                => $field_id,
                'label'             => get_the_title(),
                'type'              => 'product',
                'inputType'         => 'singleproduct',
                'isRequired'        => false,
                'basePrice'         => $prijs,
                'enableCalculation' => true,
                'allowsPrepopulate' => true,
                'inputs'            => [
                    0 => [
                        'id'    => $field_id . '.1',
                        'label' => 'Naam',
                        'name'  => ''
                    ],
                    1 => [
                        'id'    => $field_id . '.2',
                        'label' => 'Prijs',
                        'name'  => ''
                    ],
                    2 => [
                        'id'    => $field_id . '.3',
                        'label' => 'Aantal',
                        'name'  => $field_id
                    ]
                ]
            );

            //checking if the field already exists (otherwise on validation failure, we'll get duplicate fields)
            if ( ! in_array($field_id, array_column($form['fields'], 'id'))) {
                $field = GF_Fields::create($props);
                array_unshift($form['fields'], $field);
            }
        }
        
        return $form;
    }

Dynamic generated form creation based on API results of separate system? That’s a very neat concept. If I’m understanding your use case and description of where currently hitting a wall correctly, using the GFAPI is definitely the way to go. You may have more success if you can separate the process for updating the form from the submission itself. For example:

Form A would be the form your end users submit that, to them, would always be the latest and greatest. Form B would be one you (or limited staff) submit whenever you want Form A to be updated. It could be submit via API call via a cron job if you wanted to scheduled. This might be a scenario where Gravity Flow could allow you to structure the different activities Form B performs to modify Form A:

Good luck! Look forward to hearing about any implementation pattern you find success with as that’s a really neat edge case.

Jamie

1 Like