This demo shows how you can use jQuery UI to apply click & drag functionality to Formidable Forms Repeaters and re-order the rows as required. We have combined this with a row counter to add extra functionality when reviewing your data in the back end.
You can read the full tutorial, and see a demo, HERE.
How to achieve this:
The solution to this is actually quite simple and uses the jQuery UI plugin that we also used in our drag & drop fields demo.
The first thing to do is to build your form:
- Add a repeater field to your form and select the formatting of your choice: Default, Inline or Grid.
- Add some fields to your repeater.
- Add a hidden or text field to your repeater that will be used as the row counter.
- Save your form.
- Go to the form Settings > Customise HTML > Before fields section and add the following:
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="/wp-includes/js/jquery/jquery.ui.touch-punch.js"></script>
What this does: This adds the jQuery UI plugin to your form and also includes the jQuery UI touch punch script so the drag and drop feature will work on touch enabled devices like smart phones and tablets.
Next, add this script directly underneath:
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", updateRepeatCountFields );
jQuery(document).on('frmAfterAddRow', updateRepeatCountFields );
jQuery(document).on('frmAfterRemoveRow', updateRepeatCountFields );
function updateRepeatCountFields(){
var countFields = document.querySelectorAll( '.frm_field_2064_container input');
for ( var i = 0, len=countFields.length; i<len; i++ ) {
countFields[i].value = i+1;
}
}
jQuery(document).ready(function($){
jQuery(".frm_add_form_row").click(function(){
$( '#frm_field_2059_container' ).sortable({
cursor: "grabbing",
stop: function () {
var countFields = document.querySelectorAll( '.frm_field_2064_container input');
for ( var i = 0, len=countFields.length; i<len; i++ ) {
countFields[i].value = i+1;
}
}
});
});
});
</script>
Change 2059
to the ID of your repeater field and 2064
to the ID of your hidden or text field in 2 places.
Next scroll down and find the HTML for the repeater field you added and replace all of it with this:
<div class="ListContainer">
<h3 class="frm_pos_[label_position][collapse_class]">[field_name]</h3>
<div id="frm_field_[id]_container" class="frm_form_field frm_section_heading form-field[error_class]">
[collapse_this]
</div>
[if description]
<div class="frm_description">[description]</div>
[/if description]
</div>
Now save the form.
View your form on the front end and you will see the default behaviour of the repeater row. At this stage the jQuiry UI script hasn’t been called yet as your repeater only has 1 row but when you click on the ‘Add‘ button for the first time the script will be called and you will then be able to click and drag your rows into any order you wish.
You will also see the row number field updating as you re-order the rows as per the examples above.
The final thing to do is to apply some styling to make it look a bit nicer and also work well with your theme.
Here is some example CSS that we used for the above demo:
.ListContainer h3 {
font-size: 20px !important;
line-height: 28px !important;
}
.divider-block {
display: block;
width: 100%;
border-top: 2px dashed #eaeaea;
margin-top: 50px;
padding-top: 20px;
}
.ListContainer {
overflow: hidden;
}
.ListContainer>div {
white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
}
.frm_repeat_sec,
.frm_repeat_inline {
background-color: #fff;
border: 1px solid #eaeaea !important;
padding: 20px !important;
margin: -1px 0 auto !important;
}
.frm_repeat_grid {
background-color: #fff;
border: 1px solid #eaeaea !important;
padding: 20px 20px 0 20px !important;
margin: -1px 0 auto !important;
}
#sortable {
margin: 0 !important;
}
.ui-sortable>div {
cursor: move;
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
}
.ui-sortable>div:before {
content: "f047";
display: block;
position: absolute;
top: 5px;
right: 10px;
font-size: 22px;
color: #999;
font-family: FontAwesome;
}
.ListContainer .form-field {
animation-name: none !important;
}
.ui-sortable-helper {
box-shadow: 0 10px 50px rgba(0, 0, 0, 0.08), 0 13px 45px rgba(0, 0, 0, 0.08);
}
.frm_repeat_sec:first-of-type,
.frm_repeat_inline:first-of-type,
.frm_repeat_grid:first-of-type {
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
.frm_repeat_sec:last-child,
.frm_repeat_inline:last-child,
.frm_repeat_grid:last-child {
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
.ListContainer>div {
padding-top: 1px;
}
You will probably need to adjust the CSS above to work with your theme and website styling. In particular the use of the FontAwesome icon might not work with your website but this can be adjusted as required.
PLEASE NOTE: This is an experimental feature and might not work well in all browsers / environments so i'd suggest testing it out before trying it on a live form.
Tutorial Gallery
×Warning: This tutorial was created 2368 days ago. Some of the information may be out of date with more recent versions of Formidable. Please proceed with caution and always perform a backup before adding custom code.
Hi Everyone,
We have a long and complex form defined where the very first field is unique. This creates a challenge when, after entering all the subsequent data, you receive a "duplicate value" error. In order to eliminate the problem, we attached a jQuery autocomplete to the input field, added a PHP function to be called via AJAX to retrieve the suggestions, and added a call to the record's edit link when the user selects on of the selections.
Before you start
- PLEASE, PLEASE, PLEASE make a backup of your site and DB.
- Decide if you want to put your code into a child theme or a snippet plug-in. We like Code Snippets by Shea Bunge.
- Our needs are for multi-site so this example code is multi-site ready.
Step by step
- Use your browser's inspect feature to see what the input field's ID actually is when displaying the edit or input form. As of 2018-03-15, it will be "field_" & [form field name]. For example if you have a form field named "company_name", then it will be "field_company_name". Make a note of the ID as we will be using it in the jQuery Autocomplete and OnSelect section later.
- You have to enqueue the jQuery UI or autocomplete modules depending on your needs. You can download whatever version you like from jQuery.com or Google hosted libraries and load if from your own server or you can reference the online libraries directly. Either way, be sure to match the CSS and JS modules or you could end up chasing down non-existent bugs. See the Enqueue jQuery UI section below for the example code.
- Add a PHP function to retrieve the autocomplete suggestions along with the ID of the suggested record. See Get Suggestions for the code.
- Add an HTML field to the top your Formidable form. I don't think, strictly speaking, that you have to put it at the top, but it servers as a reminder that you're doing something unusual in the form, especially if another developer comes in behind you.
- Add the code from the jQuery Autocomplete and OnSelect into the HTML field making sure to surround it with script tags.
- Save your Formidable form and open or refresh your edit page.
- You should now be able to type in a partial value and have a list show up under your edit field. If you select a suggestion, either by clicking or using the keyboard arrows and pressing enter, you will go to the suggestion's edit page. Otherwise, you can enter a new unique value and complete the entry assured that your value is unique.
There's a lot of room for improvement, but this will get you going!
I hope you find this code useful.
Enqueue jQuery UI
function load_jquery_ui() {
wp_enqueue_script('jquery-ui', 'https://code.jquery.com/ui/1.12.1/jquery-ui.js', array('jquery'));
wp_enqueue_style('jquery-ui.css', 'https://code.jquery.com/ui/1.12.1/themes/ui-lightness/jquery-ui.css');
}
add_action('wp_enqueue_scripts', 'load_jquery_ui');
Get Suggestions
Important: The nopriv action could be a different routine if the functionality must be different for logged in v. not logged in users. Optionally, you could also leave it undefined if you want unauthenticated users to get no results at all. Also, please remember to replace [Formidable FORM FIELD NAME] with the name of your field as shown in the Formidable form editor.
add_action('wp_ajax_nopriv_get_field_suggestions_list', 'get_field_suggestions_list');
add_action('wp_ajax_get_field_suggestions_list', 'get_field_suggestions_list');
//
function get_field_suggestions_list() {
global $wpdb;
$frm_item_metas = $wpdb->prefix . 'frm_item_metas'; //Formidable records
$frm_fields = $wpdb->prefix . 'frm_fields'; //Formidable fields
//
//Convert the search value to something safe for a query
$find_search_value = "%" . $wpdb->esc_like( stripslashes( $_REQUEST['search_value'] ) ) . "%";
$suggestions = array();
//
//If there is a value in the Name parameter then get the suggestions otherwise leave
if ( strlen( $find_search_value ) > 0 ) {
$sql = "select item_id, meta_value from {$frm_item_metas} where field_id = ( select id from {$frm_fields} where field_key = '[Formidable FORM FIELD NAME]' limit 1) and meta_value like '%s' order by meta_value";
$prepared_sql = $wpdb->prepare( $sql, $find_search_value );
$results = $wpdb->get_results( $prepared_sql );
foreach( $results as $result ) {
$suggestion = array();
$suggestion['item_id'] = $result->item_id;
$suggestion['meta_value'] = $result->meta_value;
$suggestions[] = $suggestion;
}
}
echo json_encode( $suggestions );
die();
}
jQuery Autocomplete and OnSelect
Important: Please enclose the code in the HTML field with script tags, replace [ID FROM STEP 1] with the field ID you retrieved in step 1, and replace [PAGE SLUG] with the value of your edit page's slug.
Here are some clarifications for the code below:
label: Shows on the drop down list
value: Is left in the field on blur or selection
id: Hidden but used for the edit link
delay: Amount of ms to wait before initiating the AJAX call
url: WordPress funnels the AJAX processing through admin-ajax.php for very good reasons, if you need more information look here.
item.reference_names must match what's put in the JSON as the index for the data
jQuery( document ).ready(function( $ ) {
$( '#[ID FROM STEP 1]' ).autocomplete( {
source: function( search_value, response ) {
$.ajax({
type: 'POST',
dataType: 'json',
delay: 200,
url: '/wp-admin/admin-ajax.php',
data: 'action=get_field_suggestions_list&search_value='+search_value.term,
success: function( data ) {
response( $.map ( data, function( item ) {
return {
label: item.meta_value + ' (' + item.item_id + ')',
value: item.meta_value,
id: item.item_id
};
}));
}
});
},
select: function( event, ui ) {
window.open( window.location.href + "[PAGE SLUG]/?frm_action=edit&entry=" + ui.item.id, '_self');
}
});
});