6

Thanks to this seven-year-old tutorial from Pippin Williamson, I have just learned how to live-load content in to an admin-side WordPress plugin page, using ajax.

My plugin sets up a management page (Tools) in the admin back-end, containing an empty div #cxt-results and a form with submit button cxt-submit.

Thanks to the following PHP function and jQuery, when the button is clicked, the latest five WordPress posts of a given post type, 'viewpoint', are fetched, and the response, their titles, is returned in to the cxt-results div, all in one list.

enter image description here

/* ------------------------------------------------------------------------ *
 * Ajax Function
 * ------------------------------------------------------------------------ */

function cxt_process_ajax() {

    echo '<p>This is my response</p>';

    // If neither of these verifies
    if (!isset($_POST['cxt_nonce']) || !wp_verify_nonce($_POST['cxt_nonce'], 'cxt-nonce') ) {
        die('Permissions check failed');
    }


    $myposts = get_posts(
        array(
            'post_type'         => 'viewpoint',
            'posts_per_page'    => 5
        )
    );

    if ($myposts) {
        echo '<ul>';
        foreach ($myposts as $mypost) {
            echo '<li>' . get_the_title($mypost->ID) . '</li>';
        }
        echo '</ul>';
    } else {
        echo '<p>' . __('No results found', 'cxt') . '</p>';
    }

    die();
}
add_action('wp_ajax_cxt_get_results', 'cxt_process_ajax');

.

jQuery(document).ready(function($) {

  // When the form is submitted
  $('#cxt-form').submit(function() {

    $('#cxt-loading').show();                   // Loading animation
    $('#cxt-submit').attr('disabled', true);    // Submit button
    $('#cxt-results').empty();                  // Content box

    data = {
      action: 'cxt_get_results',
      cxt_nonce: cxt_vars.cxt_nonce
    };

    // Finish up
    $.post(ajaxurl, data, function(response) {   // Post cxt_get_results to wp-admin ajax, get response
      $('#cxt-loading').hide();                  // Loading animation
      $('#cxt-submit').attr('disabled', false);  // Submit button
      $('#cxt-results').html(response);          // Content box
    });

    return false;
  });
});

What I'd like, however, is to move to a more "progressive" update for the cxt-results div - that is, to add a new post title to it sequentially until exhausted, rather than all in one response at the end of the process.

This doesn't mean much for a process as quick as fetching a post list, as in the above example. But progressive feedback will be useful for a future use case I have in mind, wherein each step being output may take a longer time to process.

I would want to see each post title be echoed to the div one-by-one.

How can I go about doing this? I might imagine it involves a fresh approach on the jQuery/Javascript side, and not just PHP, since it would involve a stepped update rather than a single response (?).

Or is there a way to do it more with just PHP, teasing out a standard foreach?

Edit: More detailed code...

    /* ------------------------------------------------------------------------ *
     * Menu Item
     * ------------------------------------------------------------------------ */

    add_action( 'admin_menu', 'cxt_add_plugin_admin_menu' );

    function cxt_add_plugin_admin_menu(  ) {
        /*
        add_management_page(                    // Administration Pages addable: https://codex.wordpress.org/Administration_Menus
             'Magic Terms',                     // Page title: The text to be displayed in the title tags of the page when the menu is selected.
             'Magic Terms',                     // Menu text: The text to be used for the menu.
             'manage_options',                  // Capability: The capability required for this menu to be displayed to the user.
             'magic-terms',                     // Menu slug: The slug name to refer to this menu by (should be unique for this menu).
             'cxt_plugin_page'                  // Callback function: The function to be called to output the content for this page
         );
         */

         // Per https://www.youtube.com/watch?v=7pO-FYVZv94
         global $cxt_settings;
         $cxt_settings = add_management_page(
             __('Magic Terms Demo', 'cxt'),
              __('Magic Terms', 'cxt'),
              'manage_options',
              'magic-terms',
              'cxt_plugin_page'
          );

    } // end cxt_add_plugin_admin_menu




    /* ------------------------------------------------------------------------ *
     * Page Callback
     * ------------------------------------------------------------------------ */

    /**
     * Renders the basic display of the menu page for the theme.
     */
    function cxt_plugin_page(  ) {

            ?>

            <div class="wrap">

                    <h1>Magic Terms Plugin</h1>

                    <p>This is the plugin page, cxt_plugin_page. Stuff goes here.</p>

                    <?php
                    // settings_fields( 'pluginPage' );
                    // do_settings_sections( 'pluginPage' );
                    // submit_button();
                    ?>

                    <!-- https://stackoverflow.com/a/32340299/1375163 -->

                    <!--
                    <form method="POST" action="<?php echo admin_url( 'admin.php' ); ?>">
                        <input type="hidden" name="action" value="magic-terms" />
                        <input type="submit" value="Do it!" class="button button-primary" />
                    </form>
                    -->

                    <!-- https://www.youtube.com/watch?v=7pO-FYVZv94 -->
                    <form id="cxt-form" action="" method="POST">
                        <div>
                            <input type="submit" name="cxt-submit" id="cxt-submit" value="Get Results" class="button button-primary" />
                            <img src="/wp-admin/images/wpspin_light.gif" class="waiting" id="cxt-loading" style="display:none">
                        </div>
                    </form>


                    <div id="cxt-results">
                    </div>


            </div>

            <?php

    }



    /* ------------------------------------------------------------------------ *
     * Ajax Enqueue
     * ------------------------------------------------------------------------ */
    // Per https://www.youtube.com/watch?v=7pO-FYVZv94
    function cxt_load_scripts($hook) {

        // Use settings above to know when we are on this settings page
        global $cxt_settings;

        if ( $hook != $cxt_settings )
            return;

        wp_enqueue_script( 'cxt-ajax', plugin_dir_url(__FILE__).'js/cxt-ajax.js', array('jquery') );
        wp_localize_Script('cxt-ajax', 'cxt_vars', array(
            'cxt_nonce'     => wp_create_nonce('cxt-nonce')
        ));

    }
    add_action('admin_enqueue_scripts', 'cxt_load_scripts');




    /* ------------------------------------------------------------------------ *
     * Ajax Function
     * ------------------------------------------------------------------------ */

    function cxt_process_ajax() {

        echo '<p>This is my response</p>';

        // If neither of these verifies
        if (!isset($_POST['cxt_nonce']) || !wp_verify_nonce($_POST['cxt_nonce'], 'cxt-nonce') ) {
            die('Permissions check failed');
        }


        $myposts = get_posts(
            array(
                'post_type'         => 'viewpoint',
                'posts_per_page'    => 5
            )
        );

        if ($myposts) {
            echo '<ul>';
            foreach ($myposts as $mypost) {
                echo '<li>' . get_the_title($mypost->ID) . '</li>';
                ob_flush();
                flush();
                sleep(2);
            }
            echo '</ul>';
        } else {
            echo '<p>' . __('No results found', 'cxt') . '</p>';
        }


        die();
    }
    add_action('wp_ajax_cxt_get_results', 'cxt_process_ajax');
2
  • after echo use flush() and ob_flush() Commented Sep 9, 2019 at 6:00
  • I have updated my answer as per your ajax Commented Sep 9, 2019 at 15:26

1 Answer 1

1

Here is an example where you can do the things that you have asked for.

Explanation

In index.html file I have made an ajax call and set a listener for progress event of download. In that progress event we able to get the progressive output and we can set it into any html.

In ajax.php file I have echoed the string along with sleep() and ob_flush() and flush(). So sleep will slowdown the execution process and flush made immediate output without storing it into buffer.

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Continuos Output Example</title>
    <script
  src="https://code.jquery.com/jquery-3.4.1.min.js"
  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  crossorigin="anonymous"></script>
</head>
<body>
<div class="content">
</div>
<script type="text/javascript">
    $.ajax({
        url:"ajax.php",
        method:"GET",
        success:function(data,status,xhr)
        {
            $(".content").html(data);
        },
        xhr: function(){
            var xhr = $.ajaxSettings.xhr() ;

            xhr.onprogress = function(evt){ 
                $(".content").html(evt.currentTarget.responseText);
            };

            return xhr ;
        }
    });
</script>
</body>
</html>

ajax.php

<?php

echo "hi";
ob_flush();
flush();

for ($i=0; $i < 10; $i++) {
    echo "hi".$i;
    ob_flush();
    flush();
    sleep(2);
}

Your ajax should be like this

$.ajax({
    url:ajaxurl,
    data:data,
    success:function(response)
    {
        //anything you want to do after end of excecution
    },
    xhr: function(){
        var xhr = $.ajaxSettings.xhr() ;
        xhr.onprogress = function(evt){ 
            $('#cxt-loading').hide();                  // Loading animation
            $('#cxt-submit').attr('disabled', false);  // Submit button
            $('#cxt-results').html(evt.currentTarget.responseText);
        };
        return xhr ;
    }
});
Sign up to request clarification or add additional context in comments.

9 Comments

Editing the loop to add this does not work... foreach ($myposts as $mypost) { echo '<li>' . get_the_title($mypost->ID) . '</li>'; ob_flush(); flush(); sleep(2); } It still sends back a single response. + FYI, the button submits with POST.
Have you attached the xhr.onprogress on your ajax ?
No. Oh, I see that. But I'm new to this. I don't understand how to make that work with the jQuery I have currently.
So far, I'm struggling to map this to my situation, since your example doesn't correspond to my use case. 1) I have no front-end index.html, this is for a back-end admin plugin. 2) What you have in ajax.php I have rendered programmatically through "wp-admin" on the WordPress admin page /wp-admin/tools.php?page=magic-terms. 3) I'm trying to understand why you have two portions of JavaScript/jQuery versus my one. Okay, I guess you're saying a "listener" is needed in the #div to be updated. But, given I have no ajax.php, what should 'url' be here?
@Dhaval Purohit, thank you so much for this example. I had read a million answers, and this is the only one that gave a clear-cut example of how to do this. I'm a few years late, but just wanted to say thanks!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.