I'm trying to create a load more button that loads more custom post types on click, however, I keep receiving a 400 (Bad Request) error. Here is the code i'm using.
// Header.php
<!doctype html>
<html <?php language_attributes(); class="no-js">
<head>
<!-- meta tags & links --->
<script type="text/javascript">
ajaxurl = <?php echo admin_url('admin-ajax.php'); ?>;
// HTML:
<div class="project">
<?php
$posts_array = array(
'post_type' => 'project',
'status' => 'publish',
'order' => 'ASC',
'posts_per_page' => -1,
'paged' => $paged
);
$posts = get_posts($posts_array);
$count = 0;
?>
<?php foreach ($posts as $post) : $count++; ?>
<div class="project">
<php echo the_title(); ?>
</div>
<?php endforeach; wp_reset_query(); ?>
</div>
<button id="more_posts">Load More</button>
// JavaScript:
$("#more_posts").on("click", function() {
// When btn is pressed.
$.ajax({
url: ajaxurl,
action: "more_post_ajax",
offset: page * ppp + 1,
ppp: ppp
}).success(function(posts) {
page++;
$(".projects").append(posts);
});
});
// Functions.php
function more_post_ajax(){
$offset = $_POST["offset"];
$ppp = $_POST["ppp"];
header("Content-Type: text/html");
$args = array(
'post_type' => 'project',
'status' => 'publish',
'posts_per_page' => $ppp,
'order' => 'ASC',
'offset' => $offset,
);
$loop = new WP_Query($args);
while ($loop->have_posts()) { $loop->the_post();
the_title();
}
exit;
}
add_action('wp_ajax_nopriv_more_post_ajax', 'more_post_ajax');
add_action('wp_ajax_more_post_ajax', 'more_post_ajax');
There is a good amount here to fix.
Your Ajax is syntax is off a bit. Also, you can specify posts per page for the return with PHP, so you don't need to send that, you can keep your page attribute on the frontend.
$("#more_posts").on("click", function(e) {
$.ajax({
// use the ajax object url
url: ajax_object.ajax_url,
data: {
action: "more_post_ajax", // add your action to the data object
offset: page * 4 // page # x your default posts per page
},
success: function(data) {
// add the posts to the container and add to your page count
page++;
$('.projects').append(data);
},
error: function(data) {
// test to see what you get back on error
console.log(data);
}
});
});
For your initial PHP function, you should only specify the amount of posts you want to show at first. If you use -1 it will just show all posts at the start, which you don't want. You also don't need to use the paged parameter. You only need offsets.
<?php
$args = array(
'post_type' => 'project',
'status' => 'publish',
'order' => 'ASC',
'posts_per_page' => 4,
);
$posts = new WP_Query($args);
?>
<div class="projects">
<?php
while ($posts->have_posts()) {
$posts->the_post(); ?>
<div class="project">
<?php the_title(); ?>
</div>
<?php
wp_reset_postdata();
}; ?>
</div>
For your more posts function, you only need to specify an offset, and can add your additional posts right in that function.
function more_post_ajax(){
$offset = $_POST["offset"];
$args = array(
'post_type' => 'project',
'status' => 'publish',
'posts_per_page' => 4,
'order' => 'ASC',
'offset' => $offset,
);
$post = new WP_Query($args);
while ($post->have_posts()) { $post->the_post();
<div class="project">the_title();</div>
}
wp_reset_postdata();
die(); // use die instead of exit
}
add_action('wp_ajax_nopriv_more_post_ajax', 'more_post_ajax');
add_action('wp_ajax_more_post_ajax', 'more_post_ajax');
I have not tested this, but this should get you closer to a working set.
Just in case anyone is looking at this in the future and aren't sure on why they can't make it work theres a couple of things that need changing in the above code for starters in the js you need to define the variable page before the on click function...
var page = 1;
$("#more_posts").on("click", function(e) {
$.ajax({
// use the ajax object url
url: ajax_object.ajax_url,
data: {
action: "more_post_ajax", // add your action to the data object
offset: page * 4 // page # x your default posts per page
},
success: function(data) {
// add the posts to the container and add to your page count
page++;
$('.projects').append(data);
},
error: function(data) {
// test to see what you get back on error
console.log(data);
}
});
});
This code appends the data to the admin-ajax url which means you have to use $_GET not $_POST to retrieve the offset variable in your php function:
$offset = $_GET["offset"];
Related
I am doing filtering posts using ajax.
I wish $ array was different for pages "Products" and "Faq"
If this is executed within an ajax request, is_page() won't be set. the page you're making the request from was a previous request. you need to set something on the originating page to pass with your ajax request to let it know it's for your special case page.
functions.php
function load_scripts() {
wp_enqueue_script( 'ajax', get_template_directory_uri() . '/vendor/jquery/main.js', array( 'jquery' ), NULL, true );
wp_localize_script( 'ajax', 'wp_ajax',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'is_page' => is_page( 420 ), // Whatever you want to pass. Add as many key pairs as needed.
)
);
}
add_action( 'wp_enqueue_scripts', 'load_scripts');
add_action( 'wp_ajax_nopriv_filter', 'filter_ajax' );
add_action( 'wp_ajax_filter', 'filter_ajax' );
function filter_ajax() {
$category = $_POST['category'];
if(isset($_POST['is_page']) && $_POST['is_page'] === 'faq'){
$cat__in = 10;
print_r( $_POST );
echo "FAQ";
} else{
$cat__in = 4;
print_r( $_POST );
echo "Products";
}
$args = array(
'post_type' => 'post',
'posts_per_page' => 50,
'category__in' => $cat__in,
);
if(isset($category)) {
$args['category__in'] = array($category);
}
$query = new WP_Query($args);
if($query->have_posts()) :
while($query->have_posts()) : $query->the_post();
the_title('<h2>', '</h2>');
the_content('<p>', '</p>');
endwhile;
endif;
wp_reset_postdata();
die();
}
faq.php
<div class="js-filter">
<?php
$args = array(
'post_type' => 'post',
'posts_per_page' => 100,
'child_of' => 10,
'exclude' => array(1),
);
$query = new WP_Query(array( 'cat' => 10 ));
if($query->have_posts()) :
while($query->have_posts()) : $query->the_post();
the_title('<h2>', '</h2>');
the_content('<p>', '</p>');
endwhile;
endif;
wp_reset_postdata(); ?>
</div>
<div class="categories">
<ul>
<?php
$cat_args = array(
'exclude' => array(1),
'child_of' => 10
);
?>
<?php
global $post;
$category = reset(get_the_category($post->ID));
$category_id = $category->cat_ID;
?>
<li class="js-filter-item">All</li>
<?php $categories = get_categories($cat_args); foreach($categories as $cat) : ?>
<li class="js-filter-item"><a data-category="<?= $cat->term_id; ?>" href="<?= get_category_link($cat->term_id); ?>"><?= $cat->name; ?></a></li>
<?php endforeach; ?>
</ul>
</div>
products.php
<div class="js-filter">
<?php
$args = array(
'post_type' => 'post',
'posts_per_page' => 100,
'child_of' => 6,
'exclude' => array(1,10),
);
$query = new WP_Query(array( 'cat' => 6 ));
if($query->have_posts()) :
while($query->have_posts()) : $query->the_post();
the_title('<h2>', '</h2>');
the_content('<p>', '</p>');
endwhile;
endif;
wp_reset_postdata(); ?>
</div>
<div class="categories">
<ul>
<?php
$cat_args = array(
'exclude' => array(1,10),
'child_of' => 6
);
?>
<?php
global $post;
$category = reset(get_the_category($post->ID));
$category_id = $category->cat_ID;
?>
<li class="js-filter-item">All</li>
<?php $categories = get_categories($cat_args); foreach($categories as $cat) : ?>
<li class="js-filter-item"><a data-category="<?= $cat->term_id; ?>" href="<?= get_category_link($cat->term_id); ?>"><?= $cat->name; ?></a></li>
<?php endforeach; ?>
</ul>
</div>
main.js
jQuery(function($){
$(document).on('click', '.js-filter-item > a', function(e){
e.preventDefault();
var category = $(this).data('category');
$.ajax({
url: wp_ajax.ajax_url,
data: {
action: 'filter',
is_page: wp_ajax.is_page, // This param will be passed to your ajax function.
category: category,
pageslug: location.pathname.replace(/\//g,'')
},
type: 'post',
success: function(result) {
$('.js-filter').html(result);
},
error: function(result) {
console.warn(result);
}
});
});
});
do you have any idea how to solve it? After clicking on the "All" button, it displays posts from the "ID = 6" category
You can pass your parameter by using the localize script feature. Since the page isn't passed to your ajax function, you need to set your params in localize script, then pass to your ajax as part of the localized array.
function load_scripts() {
wp_enqueue_script( 'ajax', get_template_directory_uri() . '/vendor/jquery/main.js', array( 'jquery' ), NULL, true );
wp_localize_script( 'ajax', 'wp_ajax',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'is_page' => is_page( 420 ), // Whatever you want to pass. Add as many key pairs as needed.
)
);
}
add_action( 'wp_enqueue_scripts', 'load_scripts');
In the case above, is_page is either true or false, and will pass to your javascript via the localized script. So in the JS file, wp_ajax.is_page will be either true or false (you can view this by inspecting your js object on the page. It should be a 0 or a 1.
If you wanted to pass other objects, you can create as many items to your array and then pass to your js template.
Your AJAX:
jQuery(function($){
$(document).on('click', '.js-filter-item > a', function(e){
e.preventDefault();
var category = $(this).data('category');
$.ajax({
url: wp_ajax.ajax_url,
data: {
action: 'filter',
is_page: wp_ajax.is_page, // This param will be passed to your ajax function.
category: category,
pageslug: location.pathname.replace(/\//g,'')
},
type: 'post',
success: function(result) {
$('.js-filter').html(result);
},
error: function(result) {
console.warn(result);
}
});
});
});
Then in your updated php ajax action.
function filter_ajax() {
$category = $_POST['category'];
// If it's page 420... then $_POST['is_page'] will equal a string 1.
if ( isset( $_POST['is_page'] ) && $_POST['is_page'] === '1' ) {
$cat__in = 10;
print_r( $_POST );
echo "FAQ";
} else {
$cat__in = 4;
print_r( $_POST );
echo "Products";
}
// .... rest of function
}
is_page() is not supposed to work in wp_ajax_ functions.
Check this answer: Why doesn't is_page(id) work in functions.php?
is_page() relies on a complete global $wp_query object. If you call it before the action template_redirect has been fired, it might be impossible to get that data.
Instead of using is_page(), you can pass the slug as ajax data array value.
To pass as slug, edit main.js to replace
data: { action: 'filter', category: category },
with
data: {
action: 'filter',
category: category,
pageslug: location.pathname.replace(/\//g,'')
},
Then, in functions.php in filter_ajax() function, replace
if (is_page('faq'))
with
if(isset($_POST['pageslug']) && $_POST['pageslug'] === 'faq')
I'm trying to build out a Load More button in my Wordpress app, but the AJAX return value is always 'success: false', even though I know there are more posts in the DB. I followed a tutorial I found on YouTube, but not sure what I'm missing.
JS
jQuery(document).ready(function () {
const button = document.getElementById('loadmore');
button.addEventListener('click', () => {
let current_page = document.querySelector('#main-content').dataset.page;
// let max_pages = document.querySelector('#main-content').dataset.max;
const params = {
'action': 'load_more_posts',
'current_page': current_page
}
$.post('/wp-admin/admin-ajax.php', params, (data) => {
console.log(data);
});
});
})
PHP (functions.php)
wp_enqueue_script('loadmore', get_template_directory_uri() . '/js/loadmore.js', array('jquery'), filemtime(get_template_directory() . '/js/loadmore.js'));
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');
add_action('wp_ajax_load_more_posts', 'load_more_posts');
function load_more_posts() {
$next_page = $_POST['current_page'] + 1;
$query = new WP_Query([
'posts_per_page' => 12,
'paged' => $next_page
]);
if ($query->has_posts()):
ob_start();
while($query->have_posts()) : $query->the_post();
get_template_parts('partials/blog','posts');
endwhile;
wp_send_json_success(ob_get_clean());
else:
wp_send_json_error('no more posts');
endif;
}
The returned result continues to be:
data: "no more posts"
success: false
One thing that usually gets me too, your ajax function should always end in a wp_die(); function call, otherwise it will always return 0, even if the rest of your code is correct.
In this instance:
function load_more_posts() {
$next_page = $_POST['current_page'] + 1;
$query = new WP_Query([
'posts_per_page' => 12,
'paged' => $next_page
]);
if ($query->have_posts()):
ob_start();
while($query->have_posts()) : $query->the_post();
get_template_parts('partials/blog','posts');
endwhile;
wp_send_json_success(ob_get_clean());
else:
wp_send_json_error('no more posts');
endif;
wp_die();
}
Looks like u are passing wrong current page number, what there is in the let current_page = document.querySelector('#main-content').dataset.page; ??
Edited for Clarity:
I'm using the misha_filter_function from: https://rudrastyh.com/wordpress/ajax-post-filters.html to filter posts.
The initial array is displaying the correct posts by the location_and_season (made with ACF) tag. But something weird is going on with the second array because when I click my dropdown select to sort the posts by category it's filtering all of the posts within the post type, and effectively ignoring 'tag' => $value,
I'm relatively new to Wordpress PHP and JS, so my Google-Fu hasn't been too helpful since I don't really know what to search. Any help would be appreciated. Thanks!
Here's the filter Code:
<?php
add_action('wp_ajax_myfilter', 'misha_filter_function'); //
wp_ajax_{ACTION HERE}
add_action('wp_ajax_nopriv_myfilter', 'misha_filter_function');
function misha_filter_function()
{
$value = get_field('location_and_season');
$args = array(
'post_type' => 'shows',
'tag' => $value,
'posts_per_page' => - 1, // show all posts.
'orderby' => 'name', // we will sort posts by name
'order' => 'ASC'
//$_POST['name'] // ASC or DESC
);
// for taxonomies / categories
// IMPORTANT! Adding && !empty( $_POST['categoryfilter'] ) fixes the no posts found for All Categories
if (isset($_POST['categoryfilter']) &&
!empty($_POST['categoryfilter'])) $args['tax_query'] = array(
array(
'taxonomy' => 'category',
'field' => 'id',
'terms' => $_POST['categoryfilter']
)
);
$query = new WP_Query($args);
// The Query
query_posts($args);
// The Loop
while (have_posts()):
the_post(); ?>
Here's the front end dropdown code:
<form action="<?php echo site_url() ?>/wp-admin/admin-ajax.php"
method="POST" id="filter" style="float:right; margin-right: 15px;">
<?php
if ($terms = get_terms(array(
'taxonomy' => 'category',
'orderby' => 'name'
))):
echo '<select name="categoryfilter"><option id="refresh" value="all"
class="dropdown-select">All Topics...</option>';
foreach ($terms as $term):
echo '<option value="' . $term->term_id . '">' . $term->name .
'</option>';
endforeach;
echo '</select>';
endif;
?>
<div class="processing" style="height:30px;"></div>
<input type="hidden" name="action" value="myfilter">
</form>
And the JS
jQuery(function ($) {
$('#filter').change(function () {
var filter = $('#filter');
$.ajax({
url: filter.attr('action'),
data: filter.serialize(), // form data
type: filter.attr('method'), // POST
beforeSend: function (xhr) {
filter.find('.processing').html('<img src="spinner.gif" class="spinner">'); // changing the button label
},
success: function (data) {
filter.find('.processing').text(''); // changing the button label back
$('#response').html(data); // insert data
}
});
$(document).ready(function () {
$('#filter').on("click", function () {
$('.post-section').toggleClass("active");
});
});
return false;
});
I don't see anything that actually calls your php function. Wordpress ajax calls require an action in the data.
$.ajax({
url: filter.attr('action'),
type: filter.attr('method'), // POST
data: {
action: 'misha_filter_function',
categoryfilter: $(select['name ="categoryfilter"]').val()
},
beforeSend: function (xhr) {
filter.find('.processing').html('<img src="spinner.gif" class="spinner">'); // changing the button label
},
success: function (data) {
filter.find('.processing').text(''); // changing the button label back
$('#response').html(data); // insert data
},
failure: function(response){
console.log(response); //Debug only
}
});
Also, do you enqueue the js script with wp_localize_script?
My AJAX load more function doesn't check end of posts properly - it shows preloader one more time if there are no posts. This is caused by removing pleroader's hidden class every time before send ajax request.
How should I check end of posts correct?
AJAX
If response is 0 - just hide the preloader. So preloader is shown one more time when it shouldn't be.
var page = 3;
var emptyQuery = false;
function load_posts() {
page++;
var category = $('.cat-list__item.active').data('category');
$.ajax({
url: flow.ajax_url,
type: 'POST',
beforeSend: function (jqXHR) {
$('body').addClass('loading');
if ($('.preloader').length) {
$('.preloader').removeClass('preloader--hidden');
}
},
data: {
action: 'scroll_post_load',
page: page,
category: category
},
success: function (response) {
if ( response==0 ) {
emptyQuery = true;
if ($('.preloader').length) {
$('.preloader').addClass('preloader--hidden');
}
} else {
if ($('.preloader').length) {
$('.preloader').addClass('preloader--hidden');
}
var html = response;
$(html).hide().appendTo('.posts-list').fadeIn(1000);
}
$('body').removeClass('loading');
}
});
}
// Scripts which runs on scrolling
$( window ).on( 'scroll', function() {
if( $(window).scrollTop() > $('.posts-list').height() && !$('body').hasClass('loading')) {
if(emptyQuery == false) {
load_posts();
}
}
} );
PHP
If it is the last page - returns 0 in response
function scroll_post_load(){
$paged = $_POST['page'];
$category = $_POST['category'];
if( $category=='all' || !$category ) {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => '3',
'paged' => $paged
);
}
if( $category ) {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => '3',
'paged' => $paged,
'cat' => $category
);
}
$wp_query = new WP_Query( $args );
if ( $wp_query->max_num_pages == get_query_var('paged')) {
return '0';
}
if ($wp_query->have_posts()) :
while ($wp_query->have_posts()) : $wp_query->the_post();
show_template('loop-post');
endwhile;
endif;
wp_die();
}
add_action('wp_ajax_scroll_post_load', 'scroll_post_load');
add_action('wp_ajax_nopriv_scroll_post_load', 'scroll_post_load');
You can resolve this if you just return in your response what is the total number of pages and then in the script check if next page is last or something.
That's how I do it usually. I always return the max number of pages along with the content so I can check if I need to execute the AJAX call one last time.
I have some issue to load posts from widgets using ajax ,I have this code in my own widget to fetch some posts:
$query_args = array(
'post_type' => 'post',
'posts_per_page' => 15,
'post_status' => 'publish',
'cat' => $catID
);
$q = new WP_Query($query_args);
while($q->have_posts()){
$q->the_post();
the_title();
}
wp_reset_query();
And this code in mysidebar.php
<div>
<?php dynamic_sidebar('mysidebar'); ?>
</div>
In wordpress admin I drag and drop 4 widget in mysidebar with different category ID.
At last register a ajax function like blow:
add_action("wp_ajax_itr", "itr");
add_action("wp_ajax_nopriv_itr", "itr");
function itr() {
require 'path/to/mysidebar.php';
wp_die();
}
This is my jquery code to load mysidebar.php data:
$.post(ajaxurl, {
action : "itr"
}, function (data, status) {
// Success Response ...
if (status === "success") {
$("#the_id").html(data);
}
}
});
What is the problem? the problem is when user not logged in, wp_query return wrong posts! Its load some posts for all categories!
Note: There is no private or protected post