Custom WP_List_Table displays blank rows

You’re getting the blank rows because your column headers are registered late. And you should register the headers (i.e. initialize the list table class instance) before admin notices are rendered on the page, i.e. before WordPress fires hooks like admin_notices.

But your customer_list_page() function, which I believe, is a callback for either add_menu_page() or add_submenu_page(), is only being called after the admin notices (and other stuff) are displayed, hence get_column_headers() (that’s used by WP_List_Table::get_column_info()) didn’t recognize the column headers for your list table. And that is because get_column_headers() stores the column headers in a static variable/array which once set, will not be modified anymore, so the next time the function is called for your screen/page, the function returns the “old” column headers.

More specifically, your class instance ($customers_table = new CustomerList()) should be initialized before WP_Screen::render_screen_meta() is called in wp-admin/admin-header.php. And if you look at WP_List_Table::single_row_columns() which is used (although not directly) by WP_List_Table::display(), the body/content rows will only be displayed if there are valid column headers registered for the list table. Hence that explains why the column headers are mandatory for the content rows to be displayed. Well, just like a human can’t live without a head… ( 😀 )

If you can’t initialize the class instance before admin notices are rendered… you can manually set the $_column_headers value.

So in the prepare_items() method, just replace this:

$this->_column_headers = $this->get_column_info();

with this:

$this->_column_headers = [
    $this->get_columns(),
    [], // hidden columns
    $this->get_sortable_columns(),
    $this->get_primary_column_name(),
];

But try to always initialize it “on-time”. 🙂

And here’s an example using the load-<page hook> hook:

class My_Plugin {
    private $list_table;

    public function __construct() {
        add_action( 'admin_menu', [ $this, 'add_admin_menus' ] );
    }

    public function add_admin_menus() {
        $hook_name = add_menu_page( 'Customers', 'Customers',
            'edit_posts', 'my-page', [ $this, 'render_admin_page' ] );

        // Initialize the list table instance when the page is loaded.
        add_action( "load-$hook_name", [ $this, 'init_list_table' ] );
    }

    public function init_list_table() {
        $this->list_table = new CustomerList;
    }

    public function render_admin_page() {
        ?>
            <div class="wrap my-plugin">
                <h1>Customers</h1>
                <?php $this->list_table->prepare_items(); ?>
                <?php $this->list_table->display(); ?>
            </div>
        <?php
    }
}

if ( is_admin() ) {
    new My_Plugin;
}

And just a gentle reminder to double-check for typos like the emial in your get_sortable_columns()… =) Happy coding!

Leave a Comment