Took some time off from this to let the dust settle on the Gutenberg ecosystem. Got back into it recently and am having pretty good results with this approach. Just a warning, it’s mostly Javascript-based. I strongly suggest you grab @wordpress/scripts (https://github.com/WordPress/gutenberg/blob/master/packages/scripts/README.md) so you can write your React code in es2015.
First, I removed all of the extra “settings” field groups I was cloning, leaving each block field group with ONLY the specific fields needed to get content into the view. So instead of like: Heading, Text, Clone of Spacing settings field group, Clone of Background settings field group, it’s only Heading and Text.
Then we get into the JS. First we add a filter for the block setings that will let us add attributes for all of the settings we want to customize: backgroundColor, paddingTop, whatever.
import { assign } from 'lodash';
const { addFilter } = wp.hooks;
const customBlockAttributes = {
backgroundColor: {
type: 'string'
},
paddingTop: {
type: 'string'
},
whatever: {
type: 'string'
},
};
addFilter('blocks.registerBlockType', 'my_domain', (settings, name) => {
/**
* If this is an acf block, add our custom attributes!
if (name.match(/^acf/)) {
settings.attributes = assign(settings.attributes, customBlockAttributes);
}
return settings;
});
Then we add another filter on editor.BlockEdit. If it’s an ACF block, we return a Fragment that wraps up the BlockEdit PLUS an InspectorControls component that has whatever React stuff we want!
addFilter('editor.BlockEdit', 'my_domain', createHigherOrderComponent(BlockEdit => {
return props => {
if (!props.name.match(/^acf/)) {
return <BlockEdit {...props} />;
} else {
return (
<Fragment>
<BlockEdit {...props} />
<InspectorControls>
{/* add react components here! */}
</InspectorControls>
</Fragment>
);
}
};
}, 'withCustomBlockSettings'));
Took some time off from this to let the dust settle on the Gutenberg ecosystem. Got back into it recently and am having pretty good results with this approach. Just a warning, it’s mostly Javascript-based. I strongly suggest you grab @wordpress/scripts (https://github.com/WordPress/gutenberg/blob/master/packages/scripts/README.md) so you can write your React code in es2015.
First, I removed all of the extra “settings” field groups I was cloning, leaving each block field group with ONLY the specific fields needed to get content into the view. So instead of like: Heading, Text, Clone of Spacing settings field group, Clone of Background settings field group, it’s only Heading and Text.
Then we get into the JS. First we add a filter for the block setings that will let us add attributes for all of the settings we want to customize: backgroundColor, paddingTop, whatever.
import { assign } from 'lodash';
const { addFilter } = wp.hooks;
const customBlockAttributes = {
backgroundColor: {
type: 'string'
},
paddingTop: {
type: 'string'
},
whatever: {
type: 'string'
},
};
addFilter('blocks.registerBlockType', 'my_domain', (settings, name) => {
/**
* If this is an acf block, add our custom attributes!
if (name.match(/^acf/)) {
settings.attributes = assign(settings.attributes, customBlockAttributes);
}
return settings;
});
Then we add another filter on editor.BlockEdit. If it’s an ACF block, we return a Fragment that wraps up the BlockEdit PLUS an InspectorControls component that has whatever React stuff we want!
addFilter('editor.BlockEdit', 'my_domain', createHigherOrderComponent(BlockEdit => {
return props => {
if (!props.name.match(/^acf/)) {
return <BlockEdit {...props} />;
} else {
return (
<Fragment>
<BlockEdit {...props} />
<InspectorControls>
{/* add react components here! */}
</InspectorControls>
</Fragment>
);
}
};
}, 'withCustomBlockSettings'));
After doing more digging it seems that there’s nothing built into the api for this. So instead I had to write a bunch of code to take care of it. It’s just a start, and might not work for some field configurations (like cases where an image field returns just an id instead of an object), I’m not sure I’d paste this straight into production, but it’s a decent starting point. At the very least it could use a refactor to not be a single big, ugly function.
function setFieldValue(field, value) {
switch (field.data.type) {
case 'repeater':
//Clean out any row there by default
var rows = field.$rows();
if (rows.length > 0) {
field.remove(field.$rows().eq(0));
}
for (var rowIndex in value) {
var row = value[rowIndex];
var rowEl = field.add();
for (var rowName in row) {
var rowVal = row[rowName];
var repeaterSubField = acf.getField(rowEl.find('[data-name="' + rowName + '"]'));
setFieldValue(repeaterSubField, rowVal);
}
}
break;
case 'clone':
for (var cloneKey in value) {
var cloneValue = value[cloneKey];
var cloneSubField = acf.getField(field.$el.find('[data-name="' + cloneKey + '"]'));
setFieldValue(cloneSubField, cloneValue);
}
break;
case 'image':
case 'file':
var atts = {};
for (var k in value) {
atts[k] = value[k];
}
value.attributes = atts;
field.render(value);
break;
case 'wysiwyg':
var $wrap = field.$control();
if ($wrap.hasClass('delay')) {
$wrap.removeClass('delay');
$wrap.find('.acf-editor-toolbar').remove();
field.initializeEditor();
}
var editorWrap = field.$el.find('.wp-editor-wrap');
if (editorWrap.length > 0) {
var editorID = editorWrap.eq(0).attr('id').replace(/^wp-/, '').replace(/-wrap$/, '')
window.tinyMCE.get(editorID).setContent(value);
}
break;
case 'oembed':
var url = $(value).attr('src').split('?').shift();
field.$search().val(url);
field.val(url);
field.$el.find('.canvas-media').html(value);
break;
case 'gallery':
for (var attIndex in value) {
var att = value[attIndex];
field.appendAttachment(att, attIndex);
}
break;
case 'checkbox':
field.$inputs().each(function (i, checkbox) {
if (value.indexOf(checkbox.value) >= 0) {
checkbox.setAttribute('checked', 'checked');
}
});
break;
case 'radio':
field.$control().find('[value="' + value + '"]').prop('checked', 'checked');
break;
case 'button_group':
field.val(value);
field.$el.find('input[value="' + value + '"]').prop('checked', 'checked').trigger('click');
break;
case 'true_false':
if (value) {
field.$input().prop('checked', 'checked');
}
break;
case 'post_object':
setSelect2FieldValue(field, value.ID, value.post_title);
break;
case 'page_link':
setSelect2FieldValue(field, value, value);
break;
case 'relationship':
var loadCheck = setInterval(function () {
if (!field.get('loading')) {
clearInterval(loadCheck);
for (var relIndex in value) {
var post = value[relIndex];
field.$el.find('[data-id=' + post.ID + ']').trigger('click');
}
}
}, 100);
break;
case 'taxonomy':
for (var taxIndex in value) {
field.$el
.find('[value=' + value[taxIndex] + ']')
.prop('checked', 'checked');
}
break;
case 'user':
var userLabel = value.user_nicename + ' (' + value.nickname + ')';
setSelect2FieldValue(field, value.ID, userLabel);
break;
case 'google_map':
break;
case 'date_picker':
case 'date_time_picker':
case 'time_picker':
field.val(value);
field.$el.find('[type=text]').val(value);
break;
default:
field.val(value);
}
}
I think you’re looking for the acf/fields/flexible_content/layout_title
filter. Assuming the name on that Title dropdown is just ‘title’ I think this should do it?
add_filter('acf/fields/flexible_content/layout_title', function($title) {
$ret = $title;
if ($custom_title = get_sub_field('title')) {
$ret = sprintf('<strong>%s</strong> <em style="font-size: 80%; opacity: 0.5">%s</em>', $custom_title, $title);
}
return $ret;
});
Oh my approach here was SO WRONG!
Somehow I’ve gotten along just fine for so long without knowing about acf_add_local_field_group()! Instead of juggling json files and synchronization, all I had to do was rewrite this to build the field groups from code in the first place. Once that function clicked, this all fell into place.
https://www.advancedcustomfields.com/resources/register-fields-via-php/
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.