• Skip to main content

Victor Font Consulting Group, LLC

The DEX Intranet Specialists

Call Us:

+1 919-604-5828

  • Home
  • Care Plans
    • Care Articles
    • Optional Subscriptions
  • Consultations
  • Products
    • Code Snippets
    • Public GitHub Repositories
    • Gist Snippets
    • Pastebin Snippets (Free)
    • Free Plugins
  • FAQs
  • Support
    • Graphic Design
  • Contact
    • Speakers
    • Portfolio
  • Resources
    • Free WordPress Video Training
    • Tutorials
    • Articles
    • Cybersecurity
    • EU Referral Network
You are here: Home / Computers and Internet / Programming / WordPress / Formidable Forms / Formidable Forms Repeaters and Complex jQuery Operations

Formidable Forms Repeaters and Complex jQuery Operations

By Victor M. Font Jr.
March 1, 2019Leave a Comment

Contents
  • Snapshot
  • How Repeaters Work
    • Formidable Perspective
    • DOM Perspective
  • Writing code that works
  • Working Example
    • Repeater Section: id=511

Snapshot

"To Infinity and Beyond"—Buzz Lightyear

Formidable Forms repeater fields are a great product feature. While the repeaters work fine with standard math calculations available through Formidable's shortcodes, more complex operations, such as we're addressing in this tutorial, require custom jQuery code. Getting the code to work correctly can be somewhat elusive though. This tutorial demonstrates how to get Formidable's repeater field to work with jQuery calculations as new rows are added to the form.

An example of why this is needful is posted by Howard Jennings through the Formidable Community Forum. Mr. Jennings asked how he could hide certain dropdown options based on the value selected in another dropdown.

This is something that needs to be done in jQuery because it requires manipulating CSS in the browser in real-time, which can't be done from the server in PHP. He provided some code that he had cobbled together to demonstrate his requirement. I refactored the code and reached out to him via email. Click here to access the pastebin with the refactored code.

After Mr. Jennings received the code and verified it worked well, he then asked how to apply it to each row in a repeater. This question has more challenges than solutions. After conducting some research, I couldn't find any clear examples online to share with him.

Mr. Jennings asked a good question though, and I didn't have an answer ready at hand. After thinking about it for awhile, I decided to learn how to do this myself and write this tutorial while I studied the problem

In this tutorial, you'll learn:

  • How repeaters work from both the Formidable and Document Object Model (DOM) perspectives
  • How to overcome repeater challenges in the DOM
  • Writing code that works
  • There's also an example repeater form for you to play with that does not save entries to the database.

How Repeaters Work

Formidable Perspective

First, let's understand what repeaters are from a Formidable perspective. Architecturally, repeaters are embedded forms. Whenever a user clicks on the add or remove button to add or remove a row on the form, the repeater is actually adding or removing a form entry for the embedded form.

In this article, we'll sometimes reference the repeater as the child form. The parent form is the form that displays the repeater's content.

Repeaters consist of a form section that functions as the container for the child form. The form section is a permanent part of the parent form. It has its own field id and key.

The data capture fields you add to the repeater section are actually being added to the child form. They aren't part of the parent form, other than in appearance. So in essence, when you add a repeater to your form, you are actually building two forms at once. Maybe more, if you have multiple repeaters.

When you view a form's source code in your browser, the id of the child form is stored in a hidden field beneath the section container and section title:

Formidable Form source code as displayed in browser
A Formidable Form's Source Code as Displayed in Firefox Inspector

Let's take a closer look at the source code below. The hidden field is on line 3. The child form's id is 6 (value="6"). You would use this form id if you search for data directly in the database with phpMyAdmin. Other than being found in this hidden field, the child form ID is not referenced again in the source code. It's only used with Formidable's back-end functions

Further examination of the source code reveals that repeater data rows are appear as multidimensional arrays. A multidimensional array is an array containing one or more arrays. PHP understands multidimensional arrays that are two, three, four, five, or more levels deep. With our repeaters, each field in a repeater row is notated as a multidimensional array 3-levels deep.

As an example, let's say I have a repeatable section with an id of 87. There is a text field in this repeater with an id of 89. The source code for this input field as displayed in the browser is:

Let's break this down so we can understand what we are seeing. The input type is standard for any text field. Next is the field's id as referenced by the DOM. The DOM id is different from the Formidable field id that you see when building a form. The id in the DOM is composed of the word "field" followed by an underscore "_" plus the Formidable field's key.

In the example above, the field key for field 89 is "97w00". Yet, you see there is also a "-0" appended to the end of the DOM id beginning with "field_97w00". What do you think this means?

DOM ids must be unique for all elements on a page. So, Formidable appends the repeater row id to the end of the DOM id to ensure its uniqueness.

Let's look at the input field name. This is the most critical attribute for us to understand. It lies at the heart of the jQuery function we're going to build. This is the reference to the multidimensional array that will be used on the back end. Each array element is wrapped in square brackets [].

The first dimension "[87]" is the link to the parent form. This is the field id of the repeatable section.

name="item_meta[87][0][89]"

The second dimension is the row id.

name="item_meta[87][0][89]"

The third dimension is the id of the field that stores the data.

name="item_meta[87][0][89]"

The first and third dimensions are static. They don't change as rows are added. The second dimension increments for every row that's added. Removing a row does not change row ids after they've been assigned in the DOM.

Arrays are always indexed from 0. The first row id is always 0 in an array. Now, if I add a row to the repeater, the text field on the new row is named:

name="item_meta[87][1][89]"

See how the row id increments?

The remaining properties for this input field are value, data-invmsg, and data-sectionid. Don't be concerned with these for now. They don't come into play with our jQuery function.

DOM Perspective

Repeaters are challenging to manipulate with jQuery because of how the DOM and jQuery work together. Normally, the first line in a jQuery function is:

jQuery(document).ready(function($) {});

Translated into English, this means, "Don't apply the functions in this script until the DOM is ready, then execute this code and bind it to whatever elements it affects."

A page can't be manipulated safely until the document is "ready." jQuery detects this state of readiness for you. Code included inside $( document ).ready() will only run once the DOM is ready for JavaScript code to execute.

When the DOM is loaded and ready for a new form's entry, additional repeater rows don't exist yet, only the first row does. Therefore, the jQuery function is only bound to the first row. The jQuery only runs once when the DOM has reached its ready state. You can't bind code to elements that don't exist yet.

Adding rows to a repeater does not change the DOM's ready state. Once the DOM is loaded, it's always ready until the page is refreshed and reloaded. Because new repeater rows are added through jQuery Ajax calls and Ajax does not reload the page, the DOM doesn't get reloaded. It remains in its initial "ready" state and any jQuery associated with document ready will not refire. This means that code meant to manipulate a repeater row, will only continue to work on the first row.

If the entire page was refreshed and the DOM reloaded, the code would rebind to the new rows assuming the jQuery function references the HTML elements with wild cards or variables instead of direct field names.

It's not possible to know from the onset how many rows a user may add to a repeater. So, we have to figure out some way of counting how many repeater rows we have so we can make them work with our jQuery function.

Understanding how this all fits together reveals that the greatest challenge with repeater rows can be phrased as, "If the page doesn't get refreshed, how do we get our jQuery to refire as rows are added to a repeater?"

Writing code that works

Just as jQuery can detect the DOM's ready state, it can also detect other global states and events. The event we're going to rely upon for new repeater rows is ajaxComplete(). This function fires globally whenever an ajax call completes its duty. And since it's global, we can bind code to it in document ready. But what code do we bind to it?

We've already learned that code added to document ready fires when the DOM is loaded and ready to execute JavaScript. But that code only fires once. ajaxComplete() fires every time an Ajax call finishes its job.

We want the same code to run every time a form with a repeater is loaded and when a new row is added to a repeater. Following good coding practices, we would place this code into its own function so it can be executed upon document ready and ajaxComplete(). We'll call this function repeater_row_init(). As we begin to build our functions, the basic shell looks like this:

Since it's possible that we could build a form with multiple repeater sections, it's a good idea to pass a repeater section id to the init function so we know which repeater to initialize. It's also a good idea to pass the section ids in an array so we can initialize all of the repeaters on our form at one time by looping through the array. Now our repeater_row_init function looks like this:

If you'll notice when you read through the code, the repeater_row_init function is calling another function named repeater_section_87_init. This function is an element of the callbacks array. Here is how you create the callbacks array:

Let's build out the callback. This is where we have to traverse the DOM and find the right repeater rows to which to apply our code. A big challenge here occurs when you delete a row from a repeater. Normally, rows are added in numerical sequence. The first row is always 0. If we add a new row, it's 1, then 2, then 3, etc.

If all we were doing were adding rows it's not difficult to construct a loop and run through it by incremental counter. However, lets say we delete row 2. Row ids don't adjust for this. Our row ids will have a gap. The 0, 1, 2, 3 now becomes 0, 1, 3. You can't depend on an incremental counter loop when there is a gap in the numbers.

Moreover, when you delete a row from a repeater, Ajax is not invoked. So, there's no ajaxComplete() event to hook into. It doesn't matter, though. We can safely ignore delete events and there's no impact to our code if we do. Our only concern then is adding new rows.

This is the source code from a repeater field where I've deleted a row in the middle. There are 2 rows remaining: 0 and 2.

As we examine this source, we have to find an element that will clue us into the actual row id so we can determine that second dimension in the field name. There's only one element in this source code that contains the row id and it repeats for every row:

<input type="hidden" name="item_meta[87][row_ids][]" value="2">

This means we have to loop through these hidden fields, store their values, and pass those values back to the repeater section callback init function. To loop through the hidden fields and return the rowids, I've created a helper function called get_row_ids().

Good coding practice teaches that functions should only do one thing. To make sure get_row_ids() works correctly across any repeater field, we pass the section id as a parameter. Now our call back code looks like this:

The final initialization step is applying the code we want to each of the newly added repeater rows. For this example, we'll use a version of Mr. Jennings refactored code from the pastebin modified for our example form.

The last thing we have to wrap up is hooking the init into the ajax.Complete() function. For this, we have to know which repeater section invoked the Ajax call so we can execute the correct init callback.

Since ajaxComplete() fires globally for all Ajax calls, there has to be a way for us to determine which element on our page invoked the call. This part is very technical and requires a much deeper explanation than I want to provide in this article, but suffice it to say that you can find the repeater row id in the ajax.Complete() "event" object. It's nested 5-levels deep and not easy to find. Rather than explaining it all here, here's the code tied to ajax.Complete():

The completed code as written for the demo form below is:

As with any other Formidable Forms custom jQuery scripts, insert your script into the After Fields section on your form's Customize HTML page.

Now, do you want to see it work?

Working Example

The Deductible dropdown as defined in the form has five values. When each row is initialized, you'll only see 3 options. If you select "Michigan" as the state, the Deductible dropdown will display 2 options. Choose any other state, and the options return to the original three. This will continue to work for all rows added to the repeater.

Demo for Repeaters and Complex jQuery Operations

Repeater Section: id=511

Add Remove

  • 30shares
  • Facebook29
  • Twitter0
  • Pinterest0
  • LinkedIn0
  • Print
  • SMS1

About Victor M. Font Jr.

Victor M. Font Jr. is an award winning author, entrepreneur, and Senior IT Executive. A Founding Board Member of the North Carolina Executive Roundtable, he has served on the Board of Advisors, of the North Carolina Technology Association, the International Institute of Business Analysis, Association of Information Technology Professionals, Toastmasters International, and the North Carolina Commission for Mental Health, Developmental Disabilities, and Substance Abuse Services. He is author of several books including The Ultimate Guide to the SDLC and Winning With WordPress Basics, and Cybersecurity.

Reader Interactions

VictorFont.com runs on the Genesis Framework

Genesis FrameworkThe Genesis Framework empowers you to quickly and easily build incredible websites with WordPress. Genesis provides the secure and search-engine-optimized foundation that takes WordPress to places you never thought it could go.

Check out the incredible features and the selection of designs. It's that simple—start using Genesis now!

Click here to download The Genesis Guide for Absolute Beginners (PDF - 1.4 MB)

Leave a Reply Cancel reply

Your email address and website will not be published. Required fields are marked *
Posting a comment means that you agree with and accept our Comment & Product Review Policy

Call: +1 919-604-5828

Send us an E-mail

Accessibility Statement | Affiliate Marketing Disclosure | Capability Statement

Cookie Policy | Comment & Product Review Policy | Privacy Policy | Site Map | Terms & Conditions

Copyright © 2003–2023 Victor M. Font Jr.

Return to top of page