Home › Forums › Add-ons › Repeater Field › Disable reordering of the repeater field items
Hi. Has anybody attempted to disable reordering of the repeater field items? I have a situation where this would be nice to disable. I tried with some script similar to this:
field.$el[0].sortable(‘disable’);
I could not get it to work.
Could someone help me explain the Javascript API?
I’ve tried this, but am looking for an action that runs after Sortable is set. “ready_field” runs before.
acf.addAction('ready_field', function( field ){
field.$el.addClass('new-way');
});
Is there more documentation on Field Actions?
Disabling this feature has come up in the past and there is no real solution. Ideas include removing the ACF action or simply hiding the control. You will find this discussion here https://support.advancedcustomfields.com/forums/topic/how-to-disable-dragdrop-ordering-function-on-a-specific-repeaters-field/, I do not know if any of those solutions work.
I can’t give you all of the details, I can just give you some ideas. You will need to add custom JS and you will need to add an action for both the ready and append. The first is used when the page is loaded and the second will be used when a new row is added to the repeater because ACF will automatically add the class to the new row and since the row did not exist during the ready action it will not be removed.
The following is just an untested example and may need to be corrected.
In your ready action you will need to find all of the repeater rows and remove the “order” class from the handle, something like this
// the key is for the repeater
// find all rows of the repeater
// not that the selector will need to be more specific if you have nested repeater
// where you don't want to affect the nested repeter
$('[data-key="field_63597ed1ce1cb"] tr.acf-row').each(function(index, element) {
// this loops over each repeater row
// convert the element to a jQuery object
var target = $(element);
// remove the "order" class
target.removeClass('order');
}
You can do the same thing as above on the append actions, in fact you could set both the ready and append actions to the same function. However, the append action probably passes the element, but I don’t know if this is the repeater element or the row element or something else. I would likely just use the same function.
Here is an example from a project I worked on a while ago that auto generates unique ID for flexible content so that you can see what you need to do to add the JS to ACF
(function($){
if (typeof acf == 'undefined') {
return;
}
acf.add_action('ready append', function($el) {
var panel_ids = [];
$('[data-key="field_63597ed1ce1cb"] input').each(function(index, element) {
if (element.value == '' || panel_ids.indexOf(element.value) != -1) {
var new_id = acf.get_uniqid('pnl-');
element.value = new_id;
panel_ids.push(new_id);
} else {
panel_ids.push(element.value);
}
});
});
})(jQuery);
Hey @hube2 Thanks a lot!! 🙂
It’s funny because I just found the same solution :-))
Here is what I got:
(function($) {
jQuery( document ).ready( function( $ ) {
if ( typeof acf !== 'undefined' ) {
acf.addAction('load_field', function( field ) {
if ( field.data.name === 'locations') {
const locationsField = field.$el[0];
const acfTable = locationsField.querySelector('.acf-table');
const acfTableRows = acfTable.querySelectorAll('.acf-table .acf-row');
acfTableRows.forEach((row, index) => {
const handle = row.querySelector('.acf-row-handle');
handle.classList.remove('order');
});
}
});
}
});
})(jQuery);
I will have a look at what you did as well…
Hello
I tried another way to prevent sortable feature, in my case on a flexible_content field.
I started work using this thread, which no more accepts replies.
acf.addAction( <code>ready_field/key=${fieldId.slice( -1 )}</code>, ( field ) => {
// Prevent sorting
// TODO was unable to find a way to prevent the sortable feature before starting to drag element
field.on( 'sortstop', ( e ) => e.preventDefault() )
// Target the right sort handle elements, according to each field type HTML structure
// Selectors must be precise not to disable sorting in subfield that has to keep its sortable feature
switch( field.type ) {
case 'flexible_content':
// Up to element, then down to handle
$firstMatching.closest( '.acf-flexible-content' ).find( '> .values > .layout > .acf-fc-layout-handle' )
.css( 'cursor', 'pointer' )
.attr( 'title', 'Reorder disabled' )
break
}
} )
This solution abort the sort forcing to keep same order, but not perfect since ACF field is flagged changed so leaving the page prompts to confirm change loss.
I tried to disable the feature before it begins, for instance using sortstart
event with a simple e.preventDefault()
but it ends with a JS error.
I also tried to remove sort
or ui-sortable
classes. That could work since onHoverSortable()
func in wp-content/plugins/advanced-custom-fields-pro/assets/build/js/acf-field-group.js
is testing for ui-sortable
class to initialize sortable, but a mouseover or mouseenterr event put the class back.
These is more in responsive to the thread about disabling dragging only on certain items, but that thread doesn’t allow new replies. I needed to disable dragging/ordering only certain items in a flexible content field. We create these items dynamically when a post is created and want them to remain always at the top. The only solution that worked well was destroying the jQuery UI sortable instance and creating a new one. I’ll post the code for that below, but first here’s what would answer the OP’s question:
acf.addAction( 'ready_field/key=field_5f6de18aefdd9', ( field ) => {
let list = $( '.acf-flexible-content:first > .values', field.$el[0] );
list.on( 'sortcreate', ( event, ui ) => {
list.sortable( 'destroy' );
} );
} );
Here’s what I did to exclude certain items from being dragging and also from being drop targets:
acf.addAction( 'ready_field/key=field_5f6de18aefdd9', ( field ) => {
let list = $( '.acf-flexible-content:first > .values', field.$el[0] );
list.on( 'sortcreate', ( event, ui ) => {
if ( list.data( 'reinitializing-sortable' ) === true ) {
list.data( 'reinitializing-sortable', false );
return;
}
list.data( 'reinitializing-sortable', true );
list.sortable( 'destroy' );
list.sortable( {
items: '> .layout:not([data-layout="first_name"]):not([data-layout="last_name"]):not([data-layout="email"])',
handle: '> .acf-fc-layout-handle',
forceHelperSize: true,
forcePlaceholderSize: true,
scroll: true,
stop: ( event, ui ) => {
field.render();
},
update: ( event, ui ) => {
field.$input().trigger( 'change' );
}
} );
$( '> .layout[data-layout="first_name"], > .layout[data-layout="last_name"], > .layout[data-layout="email"]', list ).each( function() {
$( '.acf-fc-layout-handle', this ).css( 'cursor', 'default' );
} );
} );
} );
Technically, based on the docs, I shouldn’t have to destroy and recreate the sortable instance. I should just be able to do this:
list.sortable( 'option', 'items', '> .layout:not([data-layout="first_name"]):not([data-layout="last_name"]):not([data-layout="email"])' );
But that wouldn’t work, so instead I had to use the hacky solution above.
You must be logged in to reply to this topic.
Welcome to the Advanced Custom Fields community forum.
Browse through ideas, snippets of code, questions and answers between fellow ACF users
Helping others is a great way to earn karma, gain badges and help ACF development!
We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Privacy Policy. If you continue to use this site, you consent to our use of cookies.