Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make AJAX Pagination work with (AJAX) URL filter parameters on refresh

I have a well defined AJAX filter, in two parts, one for a load more button, and one for a selection of drop down filters. Both reload a list of properties on the front-end in AJAX, and work together in unison (e.g. If I select Min price, max price and number of beds in dropdowns, the list refreshes, and the load more button works as it should).

The same filters also work with URL parameters. So for example, if the URL was:

mydomain.com/?min_price=100000&max_price=5000000&beds=3&page=3

It would filter by those parameters. It even shows 3 pages worth of properties as it should. This works great, including on initial load. However, it does not work with the pagination, IF the URL is loaded directly. If I enter the above URL directly into the browser, and load, the initial results are correct, but on clicking the Load More button, it changes page=3 to page=4 (correct) but it append's the second page of properties completed unfiltered, and continues to do so - rather than carrying on the filtered pagination.

What am I doing wrong?

Here's my code (sorry it's a lot!)

JS:

jQuery(function($){


 // AJAX Stuff for filters + load more button

/*
 * Load More
 */
$('#prop_loadmore').click(function(){

    $.ajax({
        url : prop_loadmore_params.ajaxurl, 
        data : {
            'action': 'loadmorebutton',
            'query': prop_loadmore_params.posts, // loop parameters passed by wp_localize_script()
            'page' : prop_loadmore_params.current_page, // Get the current page

        },
        type : 'POST',
        beforeSend : function ( xhr ) {
            $('#prop_loadmore').text( 'Loading...' ); 
            $('#prop_loadmore').addClass( 'loading' );

        },
        success : function( posts ){
            if( posts ) {
                $('#prop_loadmore').removeClass( 'loading' );
                $('#prop_loadmore').text( 'More Listings' );
                $('#main_posts').append( posts ); 
            //    $(".price-txt").digits(); // Add the commas!
                localStorage.setItem("posts", posts);
                prop_loadmore_params.current_page++; // Increase current page by 1

                var params = new URLSearchParams(location.search);
                params.set('page', prop_loadmore_params.current_page);
                window.history.replaceState({}, "", decodeURIComponent(`${location.pathname}?${params}`));


                if ( prop_loadmore_params.current_page == prop_loadmore_params.max_page ) 
                    $('#prop_loadmore').hide(); // if last page, hide loadmore

            } else {
                $('#prop_loadmore').hide(); // if no properties, hide loadmore
            }
        }
    });
    return false;
});

/*
 * Filter
 */
$('#filter').change(function(){

    $.ajaxSetup({cache: false});

    $.ajax({
        url : prop_loadmore_params.ajaxurl,
        data : $('#filter').serialize(), 
        dataType : 'json',

        success : function( data ){

            // reset current page to 1 when filters on
            prop_loadmore_params.current_page = 1;

            prop_loadmore_params.posts = data.posts;

            // set max page
            prop_loadmore_params.max_page = data.max_page;

            found_posts = data.found_posts 

            //First pull out the empty strings

            var formData = $('#filter').serializeArray().filter(function (i) {
                if(i.value != 'prop_filters') {
                    return i.value;
                }
            });

            //Now push formData to URL
            window.history.pushState('', 'title', '?' + $.param(formData) + '&page=' + prop_loadmore_params.current_page);

            $('#main_posts').html(data.content);
            $('.listings-count').text( found_posts + ' Real Estate Listings for Sale' );


            if (found_posts > 9) {
                $('#prop_loadmore').show();
            }
             if ( prop_loadmore_params.current_page == prop_loadmore_params.max_page ) 
                    $('#prop_loadmore').hide(); // if last page, hide loadmore




            // If not enough posts for 2nd page, hide loadmore
            if ( data.max_page < 2 ) {
                $('#prop_loadmore').hide();
            } else {
                $('#prop_loadmore').show();
            }
        }
    });

    return false;

});

});

Functions.php :

add_action( 'wp_enqueue_scripts', 'properties_script_and_styles');

function properties_script_and_styles() {
global $wp_query;

wp_register_script( 'property_scripts', get_stylesheet_directory_uri() . '/assets/js/properties.js', array('jquery') );


$the_page = $wp_query->query_vars['paged'] ? $wp_query->query_vars['paged'] : 1;

if (!empty($_GET['page'])) {
    $the_page = $_GET['page'];
}

wp_localize_script( 'property_scripts', 'prop_loadmore_params', array(
    'ajaxurl' => site_url() . '/wp-admin/admin-ajax.php', 
    'posts' => $wp_query->query_vars,
    'current_page' => $the_page,
    'max_page' => $wp_query->max_num_pages,
    'found_posts' => $wp_query->found_posts,

) );

wp_enqueue_script( 'property_scripts' );
}

add_action('wp_ajax_loadmorebutton', 'prop_loadmore_ajax_handler');
add_action('wp_ajax_nopriv_loadmorebutton', 'prop_loadmore_ajax_handler');

function prop_loadmore_ajax_handler(){

$args = json_decode(  $_POST['query'] ); 
$args['paged'] = $_POST['page'] + 1; 
 if (!is_user_logged_in()) {
    $args['post_status'] = 'publish';
}
else {
    $args['post_status'] = array('publish', 'private');
}
query_posts( $args );

if( have_posts() ) :


    while( have_posts() ): the_post();

        get_template_part( 'template-parts/post/content', get_post_format() );

    endwhile;
endif;
die;
}


function prepare_property_filters(array $args): array {

/** Price Args**/

if (!empty($_REQUEST['price_min']) || !empty($_REQUEST['price_max'])) 
{
    $args['meta_query'] = ['relation'=>'AND'];
}

// If Both
if( !empty( $_REQUEST['price_min'] ) && !empty( $_REQUEST['price_max'] )) {
    $args['meta_query'][] = array(
        'key' => 'price',
        'value' => array( $_REQUEST['price_min'], 
$_REQUEST['price_max'] ),
        'type' => 'numeric',
        'compare' => 'between'
    );
} else {
    // if only min price
    if( !empty( $_REQUEST['price_min'] ) ) {
        $args['meta_query'][] = array(
            'key' => 'price',
            'value' => $_REQUEST['price_min'],
            'type' => 'numeric',
            'compare' => '>'
        );
    }
}

    // if only max price
    if( !empty( $_REQUEST['price_max'] ) ) {
        $args['meta_query'][] = array(
            'key' => 'price',
            'value' => $_REQUEST['price_max'],
            'type' => 'numeric',
            'compare' => '<'
        );
    }

//* 
// Bedrooms Arg
//*
    if( !empty( $_REQUEST['beds'] ) ) {
        $args['meta_query'][] = array(
            'key' => 'bedrooms',
            'value' => $_REQUEST['beds'],
            'type' => 'numeric',
            'compare' => '>='
        );
    }

//* 
// Property Type Arg
//*
    if( !empty( $_REQUEST['type'] ) ) {
        $args['meta_query'][] = array(
            'key' => 'property_type',
            'value' => $_REQUEST['type'],
            'compare' => 'IN'
        );
    }

return $args;
}


add_action('wp_ajax_prop_filters', 'property_filters'); 
add_action('wp_ajax_nopriv_prop_filters', 'property_filters');

function property_filters() {
//*
// Sort by Args
//*

    if( $_REQUEST['sort_by'] === 'price-desc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'DESC';
        $meta_key = 'price';
    }

    elseif( $_REQUEST['sort_by'] === 'price-asc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'ASC';
        $meta_key = 'price';
    }
    elseif( $_REQUEST['sort_by'] === 'bedrooms-desc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'DESC';
        $meta_key = 'bedrooms';
    }
    elseif( $_REQUEST["sort_by"] === 'bedrooms-asc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'ASC';
        $meta_key = 'bedrooms';
    }
    else {
        $orderby = 'date'; 
        $order = 'DESC';
        $meta_key = '';
    }
$args = prepare_property_filters([
    'posts_per_page' => 9, 
    'post_status' => is_user_logged_in() ? ['publish', 'private'] : ['publish'],
    'paged' => $_POST['page'],
    'meta_key' => $meta_key,
    'orderby' => $orderby,
    'order' => $order
]);
query_posts( $args );

    global $wp_query;

    if( have_posts() ) :

        ob_start();

        while( have_posts() ): the_post();

            get_template_part( 'template-parts/post/content', get_post_format() );

        endwhile;

        $posts_html = ob_get_contents(); 
        ob_end_clean(); 
    else:
        $posts_html = '<p>Nothing found for your criteria.</p>';
    endif;

    echo json_encode( array(
        'posts' => json_encode( $wp_query->query_vars ),
        'max_page' => $wp_query->max_num_pages,
        'found_posts' => $wp_query->found_posts,
        'content' => $posts_html,
    ) );

    die();
}

and the HTML:

<input id="filter_toggle"  type="checkbox">
<?php //We need to save the varaibles in arrays, so we can then check them against the URL and populate the dropdowns

$price_min = [
'' => 'Any Price',
'100000' => '$100,000',
'150000' => '$150,000',
'200000' => '$200,000',
'250000' => '$250,000',
//etc
];

$price_max = [
'' => 'Any Price',
'100000' => '$100,000',
'150000' => '$150,000',
'200000' => '$200,000',
'250000' => '$250,000',
//etc
];

$beds = [
'' => 'All Beds',
'1' => '1+',
'2' => '2+',
'3' => '3+',
'4' => '4+',
'5' => '5+'
];

$property_type = [
'' => 'All Property Types',
'single-family-home' => 'Single Family Home',
'condo' => 'Condo',
'land' => 'Land',
'townhouse' => 'Townhouse'
];

$sort_by = [
'newest' => 'Sort by Newest',
'price-desc' => 'Sort by Price (High to Low)',
'price-asc' => 'Sort by Price (Low to High)',
'bedrooms-desc' => 'Sort by Beds (Most to Least)',
'bedrooms-asc' => 'Sort by Beds (Least to Most)'
];
?>

<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php" method="POST" id="filter">
<div class="filters_options">

  <select name="price_min" class="min_max_select">
    <option disabled="disabled" selected="" value="">Minimum Price</option>
    <?php foreach ($price_min as $key => $value) {
         $selected = '';
         if ($_GET['price_min'] == $key) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     } ?>
  </select>

  <select name="price_max" class="min_max_select">
    <option disabled="disabled" selected="selected" value="">Maximum Price</option>
    <?php foreach ($price_max as $key => $value) {
         $selected = '';
         if ($_GET['price_max'] == $key) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     } ?>
  </select>

  <select name="beds" class="select_beds">
    <option disabled="disabled" selected="selected" value="">Bedrooms</option>
    <?php foreach ($beds as $key => $value) {
         $selected = '';
         if ($_GET['beds'] == $key) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     } ?>
  </select>

  <!-- <select>
    <option disabled="disabled" selected="selected" value="">Bathrooms</option>
    <option value="">All Baths</option>
    <option value="1+">1+</option>
    <option value="1+">2+</option>
    <option value="1+">3+</option>
    <option value="1+">4+</option>
    <option value="1+">5+</option>
  </select> -->

  <select name="type" class="sort_by_property_type">
    <option disabled="disabled" selected="selected" value="">Property Type</option>
    <?php foreach ($property_type as $key => $value) {
         $selected = '';
         if ($_GET['type'] == $key) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     } ?>
  </select>

  <!-- <select>
    <option disabled="disabled" selected="selected" value="">Property View</option>
    <option value="">All Property Views</option>
    <option value="Golf View">Golf View</option>
    <option value="Ocean View">Ocean View</option>
    <option value="Ocean Front">Ocean Front</option>
  </select> -->

  <select name="sort_by" class="sort_by_dropdown">
    <?php
     foreach ($sort_by as $key => $value) {
         $selected = '';
         if ($_GET['sort_by'] == $key) {
             $selected = ' selected="selected"';
         }
         printf(
             '<option value="%s"%s>%s</option>',
             $key,
             $selected,
             $value
         );
     }
     ?>
  </select>

<input type="hidden" name="action" value="prop_filters" />
</div>

 <span class="reset_btn reset">reset</span>
<label class="done_btn" for="filter_toggle">Done</label>
</form> 
</div>

<ul id="main_posts" class="item-listings">
<?php 

//*
// Sort by Args
//*

    if( $_GET['sort_by'] === 'price-desc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'DESC';
        $meta_key = 'price';
    }

    elseif( $_GET['sort_by'] === 'price-asc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'ASC';
        $meta_key = 'price';
    }
    elseif( $_GET['sort_by'] === 'bedrooms-desc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'DESC';
        $meta_key = 'bedrooms';
    }
    elseif( $_GET["sort_by"] === 'bedrooms-asc' ) {
        $orderby = 'meta_value_num'; 
        $order = 'ASC';
        $meta_key = 'bedrooms';
    }
    else {
        $orderby = 'date'; 
        $order = 'DESC';
        $meta_key = '';
    }

    $per_page = 9;

    if(!empty( $_GET['page'])) {
        $per_page = $_GET['page'] * 9;
    }

// Build the inital Loop
$args = prepare_property_filters([
    'posts_per_page' => $per_page,
    'paged' => $_POST['page'],
    'meta_key' => $meta_key,
    'orderby' => $orderby,
    'order' => $order
]);

query_posts($args);

if( have_posts() ) :

    while( have_posts() ): the_post();

        get_template_part( 'template-parts/post/content', get_post_format() );

        $count_posts = $wp_query->found_posts; 

    endwhile;
endif;

?>
</ul>


<?php if (  $wp_query->max_num_pages > 1 ) :
    echo '<div id="prop_loadmore">More Listings</div>';
endif;?>

<span class="listings-count"><?php echo $count_posts;?> Real Estate Listings for Sale</span>
<!-- <span class="reset">reset</span> -->
like image 934
user2115227 Avatar asked Jan 28 '20 09:01

user2115227


1 Answers

This block of code is executed when #prop_loadmore is clicked:

var params = new URLSearchParams(location.search);
params.set('page', prop_loadmore_params.current_page);
window.history.replaceState({}, "", decodeURIComponent(`${location.pathname}?${params}`));

You are using prop_loadmore_params which increments its current page when the button is clicked, but doesn't appear to have any reference to price_min or price_max

You should adjust $('#prop_loadmore').click(function(){...}) to include something like:

var params = new URLSearchParams(location.search);

params.set('page', prop_loadmore_params.current_page);

//Add All Additional Parameters in this way
if (prop_loadmore_params.price_min){
     params.set('price_min', prop_loadmore_params.price_min);
}
if (prop_loadmore_params.price_max){
     params.set('price_max', prop_loadmore_params.price_max);
}
// ...

window.history.replaceState({}, "", decodeURIComponent(`${location.pathname}?${params}`));

And then on $('#filter').change(function(){}) make sure that you add the filtered values to the prop_loadmore_params object.

Let me know how it goes!

like image 127
JobiWon9178 Avatar answered Oct 23 '22 11:10

JobiWon9178