Skip to content

Flexible Content + Tab field: collapsing a newly-added (unsaved) layout throws in _acf-field-tab.js and aborts field init (ACF PRO 6.8.3 / WP 7.0) #1017

@RyanUniqueBV

Description

@RyanUniqueBV

Describe the bug

On WordPress 7.0 + ACF PRO 6.8.3, collapsing a Flexible Content layout that was added in the current (unsaved) editing session throws two uncaught TypeErrors from the core tab-field JS (assets/src/js/_acf-field-tab.js). The first one aborts acf.getFields()'s init loop, so every field rendered after the tab group never initializes, in our case the WYSIWYG sub-fields inside the Flexible Content layouts. The layout can no longer be expanded and its editors are gone until the page is reloaded.

This only affects layouts added client-side in the current session. Collapsing layouts that were rendered server-side (after Save + reload) works perfectly. A field group needs at least one top-level Tab field plus a Flexible Content field to reproduce.

To Reproduce

  1. Create a field group with a Tab field ("Tab A"), a second Tab field ("Tab B"), and a Flexible Content field with one layout containing a single wysiwyg sub-field (minimal JSON below).
  2. Add a new post for the assigned post type.
  3. In the Flexible Content field, click Add Row and pick the layout.
  4. Click the new layout's handle to collapse it.
  5. Open the browser console.

Expected behavior
The layout collapses/expands and the fields inside it (including the WYSIWYG) keep working, exactly as they do for saved / server-rendered layouts.

Screenshots or Video
Both errors fire on the single collapse click:

Uncaught TypeError: Cannot read properties of undefined (reading 'addTab')
    at TabField.initialize           (acf-input.js)
    at acf.newField -> acf.getField -> acf.getFields -> doAction()

Uncaught TypeError: Cannot read properties of undefined (reading 'get')
    at render -> getTabs().map()      (the tab-fields manager)
    at doAction('append' | 'show' | 'refresh')

Code

Minimal repro field group:

{
  "key": "group_repro_tab_fc",
  "title": "Repro - tabs + flexible content",
  "fields": [
    { "key": "field_repro_tab_a", "label": "Tab A", "name": "", "type": "tab", "placement": "top" },
    { "key": "field_repro_text_a", "label": "Text A", "name": "text_a", "type": "text" },
    { "key": "field_repro_tab_b", "label": "Tab B", "name": "", "type": "tab", "placement": "top" },
    { "key": "field_repro_fc", "label": "Blocks", "name": "blocks", "type": "flexible_content", "button_label": "Add Row",
      "layouts": {
        "layout_repro_text": {
          "key": "layout_repro_text", "name": "text", "label": "Text", "display": "block",
          "sub_fields": [
            { "key": "field_repro_body", "label": "Body", "name": "body", "type": "wysiwyg", "tabs": "all", "toolbar": "basic", "media_upload": 0 }
          ]
        }
      }
    }
  ],
  "location": [[{ "param": "post_type", "operator": "==", "value": "post" }]]
}

Version Information:

  • WordPress 7.0
  • PHP 8.4
  • ACF PRO 6.8.3
  • Chrome 148 (macOS). Reproduces with a stock theme; our theme JS does not touch ACF or TinyMCE.

Additional context - root cause (pinpointed to assets/src/js/_acf-field-tab.js)

After the collapse, a .acf-tab-wrap element stays in the DOM but loses its group instance: $wrap.data('acf') is now undefined. Two paths then dereference it.

  1. TabField.initialize() runs again (collapse -> doAction -> acf.getFields() -> acf.newField -> initialize). With a preceding wrap present it takes the "reuse" branch:
// L84-L91
if ( ! $tabs.length || settings.endpoint ) {
    this.tabs = new Tabs( settings );
} else {
    this.tabs = $tabs.data( 'acf' );          // L87 -> undefined (orphaned wrap)
}
this.tab = this.tabs.addTab( $tab, this );    // L91 -> throws: undefined.addTab

The throw is uncaught, so the enclosing getFields().map() loop aborts and no field after the tab group initializes. This is what kills the WYSIWYG editors.

  1. The tab-fields manager re-renders on append / show / refresh and maps over every wrap in the document:
// L525-L534
getTabs: function () {
    return acf.getInstances( this.findTabs() );   // L526 -> array containing undefined
},
render: function ( $el ) {
    this.getTabs().map( function ( tabs ) {
        if ( ! tabs.get( 'initialized' ) ) {      // L531 -> throws: undefined.get
            tabs.initializeTabs();
        }
    } );
},

Important: I tried defensively guarding both sites (try/catch around initialize, and filtering falsy values out of getInstances). That silences both console errors, but it does not restore the editors or the collapse/expand behaviour, the form state is already corrupted by then. So the underlying issue is that the PRO Flexible Content collapse / re-render leaves a .acf-tab-wrap without its data('acf') group (it should keep it bound, or re-bind on re-render). L87/L91 and L526/L531 are only where it surfaces; null-guarding them would mask the symptom, not fix it.

Possibly related: #986 (Flexible Content expand/collapse state).

Notes:

  • The console line that often co-occurs, Failed to load plugin url: .../wp-includes/js/tinymce/langs/<locale>.js, is unrelated and cosmetic: ACF already prints tinymce.ScriptLoader.markDone(...) for that URL. The editor breakage is the tab crash above, not the TinyMCE language file.
  • Also reproduces with the post type's editor support removed and a non-en admin locale, but neither is required for the crash.

repro.json

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions