Hide Previously Selected Dynamic Checkboxes in Repeaters

Problem

I have two forms that have a repeater with a dynamic field (checkbox) inside it. When a checkbox is checked, I want them to be hidden in the future dynamic fields. Basically, I need every checkbox to only be checked once. If it gets unchecked, I want it to reappear. Once all checkboxes are checked throughout the repeaters, I would like to disable the repeater and not allow for more repeater rows to be created.

Shawn Pery, Formidable Project Application

Shawn provided the following images as examples:

image of repeater dynamic checkboxes
second image of repeater dynamic checkboxes

This demo solves the example on the left. Once the code is written for the form on the left, it's easy to edit it to make it also work for the form on the right.

Solution

Demo Form

Hide Dynamic Checkboxes Demo Form

Repeater

The dynamic checkboxes are created from a look up form.

Approach

The solution requires complex jQuery. As a prerequisite, please review Repeaters and Complex jQuery Operations.

Coding this required us to break this down into 6-steps:

  1. Start with previously developed library code. (Why invent the wheel?)
  2. Disable and hide the previously selected checkboxes when a new repeater row is added.
  3. Update the current repeaters when a checkbox is unchecked in any row.
  4. Disable the ability to Add Rows to the repeater when all checkboxes are checked.
  5. If a selected checkbox is unchecked, enable the repeater to Add Rows again.
  6. If a row is removed, show any values that were checked in the removed row in the other rows.

Start with Your Library Code

As a starting point, we're going to use the code developed for the Repeaters and Complex jQuery Operations article.

This code was developed for use with dropdowns (select). As it is, it is unsuitable for this project without modification because checkboxes use a different naming convention. However, starting with library code provides us with a working function outline and some of the code may be usable without modification.

The first code modifications to make are these lines executed in the document ready event:

var repeater_sections = ["137"];

and

if (active_elem == '137') {
    repeater_row_init( repeater_section );
}

We've renamed the repeater_sections variable to _repeater_section and changed it to a string instead of array. We also added an additional variable: _field_id. The underscore at the beginning of these variables is our naming convention for jQuery/JavaScript variables with global scope.

Scope determines the accessibility of variables, objects, and functions from different parts of the code. Variables declared outside of any function have Global Scope. All scripts and functions on a web page can access it. 

Variables defined inside a function are not visible from outside the function. These variables have Local or Function Scope. Since local variables are only recognized inside their functions, variables with the same name can be used in different functions. Local variables are created when a function starts, and deleted when the function is completed.

The new global variable declaration is:

var _repeater_section = "295",
    _field_id = "297";

"295" is the field id for the repeater section and "297" is the field id for the dynamic checkbox field on our demo form.

Since we're using global variables, the second block of changed code is:

if ( active_elem == _repeater_section ) {
    repeater_row_init( _repeater_section );
    remove_row_init( _repeater_section );
}

We've also added a second init function, remove_row_init( _repeater_section ) to support the 6th line item, handling the checkbox display when a user removes a repeater row that has checked values.

Everything from lines 24 through 49 and the code between the curly braces in function init_new_137_repeater_row( row_id ) can be deleted.

After those lines are deleted, we're also going to search and replace [137] with [' + _repeater_section + '] wherever else it is written into the code.

Easy so far, right?

Field Naming Conventions: Select vs. Checkbox Inputs

The library code was written for dropdowns. The input for a dropdown is the select element. Throughout the library code, we reference fields with:

$('select[name="item_meta[137][' + row_id + '][136]"]')

For the demo form, we're referencing checkboxes. The naming convention is completely different:

$('input[name="item_meta[295][' + row_id + '][297][]"]')

This is the naming convention for checkboxes used within Formidable's repeaters. In a standard checkbox, not used within a repeater, the naming convention is:

$('input[name="item_meta[214][]"]')

Notice the empty square brackets '[]' at the end of the checkbox names. It's easy to get tripped up writing code like this by omitting those square brackets.

We find that the best way to make sure the checkbox names are correct is to copy and paste the field name property directly from Formidable's generated source code when viewing the form's source with your browser tools.

In contrast to a standard checkbox, the repeater checkbox has two additional dimensions in its name: the repeater and row ids. The row id in our code is rendered as a variable to reduce the amount of code we need to write and to allow the use of jQuery's .each() function to drive the code's loops.

The final numbered dimension in the checkbox name before the empty square brackets, '[297]', is the checkbox's field id.

Because we have global variables for the repeater section and field id, we can use [' + _repeater_section + '] and [' + _field_id + '] to replace the hard coded 295 and 297. The inputs referenced throughout our code are now:

$('input[name="item_meta[' + _repeater_section + '][' + row_id + '][[' + _field_id + '][]"]')

Completed Code

This is the fully completed source code. As with any jQuery used with Formidable, this code is stored in the form's After Fields section on the Customize HTML page.

This is a good stopping point for most readers looking to solve a similar requirement to Shawn's. You've got our source code and can probably run with it. But, if you want to thoroughly understand why our code is written like this, continue with the code walk through.

Code Walk Through

Initialization

While testing this code we discovered that the Safari browser has an incomplete jQuery implementation. The code as originally written works in all browsers except Safari, including Microsoft Edge. (Microsoft besting Apple at anything, that's a first!) We've developed a Safari work around that is explained in this section and included in the full source code above.

Here's what we discovered about Safari. Our library source code initializes the active_elem variable at line 101 as:

var active_elem = complete_event[0]['target']['activeElement']['dataset']['parent']

The complete event array is created at line 100:

var complete_event = jQuery.makeArray(event);

"event" is a parameter passed by $( document ).ajaxComplete() to its callback function. Every browser we've tested populates the ['dataset']['parent'] object with the ID of the element that triggered the Ajax except Safari. Safari does not populate the ['dataset'] object. Therefore the ['parent'] object doesn't exist in Safari. This is why the code fails in Safari only. This is the workaround we developed.

  1. Added a new global variable: _safari_workaround. This variable is initialized as an empty string.
  2. Added a new init function: add_row_init( _repeater_section ). When the add row button is clicked, the _safari_workaround variable is populated with the buttons data-parent value.
  3. Created is_safari() function that reads the browser's navigator.userAgent element and returns true if Safari and false otherwise.
  4. Initialize active_elem to test for Safari first
  5. Reset _safari_workaround to empty string if populated from Add Row button click.

Here's the new active_elem init:

var active_elem = (is_safari) ? _safari_workaround : complete_event[0]['target']['activeElement']['dataset']['parent'];

Going forward, we could probably just skip reading the jQuery event altogether in favor of the Add Row click, which will work for all browsers.

Reader Interactions

Leave a Reply

Your email address will not be published. Required fields are marked *