Jump to content
fruid

pagination when content is loaded via AJAX

Recommended Posts

I'm building a search/index page.

The php-code for the filters is located on the template-file and the pagination (MarkupPagerNav) as well.

The php-code for the results however is on a php-file on the server, loaded when $_GET-variables (though not visible in the URL) are sent to the server via AJAX. I have an input field for a search term ($q), radio buttons to sort (A->Z and Z->A) and letters for alphabetical search.

This all worked fine before I used pagination and URL-segments. The pagination is useless, each time I click on the next page button it renders the exact same results starting from top. I guess that's because it's not the template file anymore but the php file on the server and this one doesn't know anything about the URL-segments and pagination.

What to do?

Also, I probably will want to use Lazy-Loading instead of pagination in the near future. Any advice? 

Share this post


Link to post
Share on other sites

Could you share some code?

1 hour ago, fruid said:

I guess that's because it's not the template file anymore but the php file on the server and this one doesn't know anything about the URL-segments and pagination.

This is a good guess. You need to keep track of your pagination somewhere and pass it to the selector like "text%=$q, start=$currentPage, limit=10" 

  • Like 2

Share this post


Link to post
Share on other sites

the php is pretty straight forward, just a couple of foreach loops and if statements to render different markup for different search results.

most of the php code is what's in the default search.php template anyway and the pagination is what is suggested in the documentation for uikit-markup.

The interesting part is I think rather the javascript I wrote for the AJAX-call.     

window.onload = function() {

    var fetch = new XMLHttpRequest();
    var letter;
    var radio = "AZ";
    var temp;
    var what = document.getElementById('search-query');
    buildRequest();
    var AZ = document.getElementById('AZ');
    var ZA = document.getElementById('ZA');
    
    AZ.addEventListener('change', getRadioButton);
    ZA.addEventListener('change', getRadioButton);
    
    var alphabet = document.getElementsByClassName('alphabet');
    for (i = 0; i < alphabet.length; i++) {
        alphabet[i].addEventListener('click', getLetter);
    }
    
    function getLetter() {
        letter = this.id;
        if (temp) {temp.parentElement.classList.remove('current');}
        this.parentElement.classList.add('current');
        temp = this;
        buildRequest();
    }    

    for (var i = 0; i < how.length; i++) {
        how[i].addEventListener('change', getRadioButton);
    }

    function getRadioButton() {
        if (ZA.checked == true) {
            radio = ZA.value;
        }
        else {
            radio = AZ.value;
        }
        buildRequest();
    }
    
    var submit = document.getElementById('submit');
    submit.addEventListener("click", function(event){
        event.preventDefault();
        getRadioButton();
        buildRequest();
    });
        
    var reset = document.getElementById('reset'); 
    reset.addEventListener("click", function(event){
        event.preventDefault();
        if (temp) {temp.parentElement.classList.remove('current');}
        what.value = "";
        letter = undefined;
        radio = "AZ";
        buildRequest();
    });

    function buildRequest() {
        x = '?x=x'; // only cause it's easier to send each other variable with a leading '&' instead of building another if-statement 
        if (what.value !== "") {q = '&q='+what.value;} else {q = '';}
        if (typeof letter !== "undefined") {l = '&l='+letter;} else {l = '';}
        if (typeof radio !== "undefined") {sort = '&sort='+radio;} else {sort = '';}
        if (fetch) {
            fetch.onreadystatechange = ReloadRequest;
            fetch.open("GET", "../_advancedsearch.php"+x+q+l+sort, true);
            fetch.send(null);
        }
    }
    
    function ReloadRequest() {
        fetch.readyState;
        if (fetch.readyState == 4 && fetch.status == 200) {
            var str = fetch.responseText;
            document.getElementById("results").innerHTML = (str);
        }
    }
}

 

Share this post


Link to post
Share on other sites

any more advice here? Maybe another article where this issue is discussed? 

I am now trying to merge the AJAX loaded php file and the template itself and load the same search page (that uses the template) instead via AJAX. Pagination kinda works this way but now it also loads the header and footer inside the <div> that I declared to return the responseText, which is not what I want.

Another issue I'm having is with the search field in the footer. The search-page with the search.php template handles everything with AJAX so far with no issue with URL $_GET variables. But the search field in the footer also loads this search-page and still adds a &q=searchterm to the URL which I can't get rid off via AJAX.

To be honest, I don't mind not using AJAX, it would solve many problems if I didn't but I use another filter that is not built with input fields but instead with simple <a> tags and so it seems like there's no way around it (?).

If I have to use AJAX, I wonder if there's not any native Processwire solution for AJAX-loaded search results instead of me having to find all these work-arounds.

Share this post


Link to post
Share on other sites
4 hours ago, fruid said:

any more advice here? Maybe another article where this issue is discussed? 

Did you try adding a pagination variable to the ajax call to alter the selector that gets the content? 

 

Share this post


Link to post
Share on other sites
6 hours ago, fruid said:

I am now trying to merge the AJAX loaded php file and the template itself and load the same search page (that uses the template) instead via AJAX. Pagination kinda works this way but now it also loads the header and footer inside the <div> that I declared to return the responseText, which is not what I want.

I have read this several times and it's not sinking in. It seems to me though that you are probably over complicating the logic? Maybe best to break this down and get the functionality sorted first then adding to it. Suggested:

  1. Get the selectors right. I'd even do this on a template without any CSS, a blank page and Tracy keeping me for company. Am I getting the correct results? Is the ajax working OK?
  2. Still on a blank page, selectors to return paginated results. Do these methods work? PaginagedArray https://processwire.com/api/ref/paginated-array/
  3. Quote
    int Get the limit that was used in pagination.  
    int Get the starting offset number that was used for pagination.  
    int Get the total number of items in the WireArray, including all paginations.

     

  4. Bring in MarkupNav (or your own if custom). Does it work? https://processwire.com/api/ref/markup-pager-nav/
  5. Do the footer search. Check Tracy to see if inputs are getting mixed up, etc..
  6. If everything works, bring in the CSS and real template file 😄

 

 

  • Like 1

Share this post


Link to post
Share on other sites
18 hours ago, fruid said:

but now it also loads the header and footer inside the <div> that I declared to return the responseText, which is not what I want.

I’m guessing header and footer come from prepend and append files. You’ll have to figure out a way to disable those in Ajax requests. For example, if you use $config->prependTemplateFile, just condition that with $config->ajax.

//in site/config.php
if (!$config->ajax) {
	$config->prependTemplateFile = 'head.inc';
	$config->appendTemplateFile = 'foot.inc';
}

You may need to set request.setRequestHeader('X-Requested-With', 'XMLHttpRequest') on your Ajax request for ProcessWire to automatically detect it as Ajax.

  • Like 1

Share this post


Link to post
Share on other sites

thanks for all your inputs, I figured out a way to not use AJAX at all. I'm using HTML hidden input fields and only change its value to be the clicked letter via javascript. Now I have no issue with the search field in the footer and the pagination works as well. 

<input type="hidden" id="hiddenletter" name="l" value="<?php echo $input->get('l'); ?>">
var letter;
var hiddenletter = document.getElementById('hiddenletter');
var alphabet = document.getElementsByClassName('alphabet');
        
for (i = 0; i < alphabet.length; i++) {
    alphabet[i].addEventListener('click', setLetter);
}
    
function setLetter() {
    letter = this.id;
    for (i = 0; i < alphabet.length; i++) {
        alphabet[i].parentElement.classList.remove('active');
        alphabet[i].parentElement.classList.remove('current');
    }        
    this.parentElement.classList.add('active');
    hiddenletter.value = letter;
}

The only thing I'm still struggling with is the URL-segments. But that problem is discussed in this topic 

I need the URL-segments to reset when new get variables are being submited on /page2 and above. Not so easy…

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...