ProcessWire - Ajax

Aus Wikizone
Wechseln zu: Navigation, Suche

Man kann mit PW relativ einfach dynamische Webseiten erzeugen. Dabei werden nur die benötigten Teile der Webseite ausgetauscht, anstatt die Seite nach jedem Klick neu zu laden. So entsteht sehr schnell ein "App-Feeling". ProcessWire unterstützt einen dabei.

https://processwire.com/talk/topic/225-how-to-work-with-ajax-driven-content-in-processwire/

Mit der Variable $config->ajax checkt man ob ein AJAX Request vorliegt. Im Prinzip ist das eine Abkürzung für eine solche Funktion, die die Header checkt:

function isAjax() {
   return (isset($_SERVER['HTTP_X_REQUESTED_WITH'])
              &&
            ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'));
}

Snippets[Bearbeiten]

Inhalte über JSON nachladen[Bearbeiten]

Beispiel 1[Bearbeiten]

Aus dem Quiz Template von dekratools. Es werden nach beantworten der Frage Notizen nachgeladen.

Javascript

$( document ).ready(function() {
  console.log( "ready!" );
  var url = window.location.href; // Aktuelle url
  $('input.check').change(
    function(){// if check Class check answer via c attr (if set)
      if ($(this).is(':checked')) {
        if(this.getAttribute('c')==1){
          qid=this.getAttribute('name')
          $(this).parent().addClass('right')
          // get notes
            $.getJSON(url, {action:"getNote",qid:qid}, function(data) {
              $("#note-text").html('');
              $.each(data, function(key, value) {
                $("#note-text").append("<div>" + value + "</div>");
              });
            }); 
          }else{
        $(this).parent().addClass('wrong')
      }
    }
  });
});

PHP (Template)

<?php namespace ProcessWire;

if($config->ajax){ // ajax request...
	$postData = array();
	$json = array();
	// clean post data
	foreach($input->get as $key => $value) {
	  $postData[$sanitizer->text($key)] = $sanitizer->text($value);
	}
	switch ($postData['action']) {
		case 'getNote':
			$json['note'] = getNote($postData['qid']);
			break;
	}
	echo json_encode($json);
}

function getNote($qid){
	$note = '';
	$qid = intval(substr($qid,4));
	$myPage = pages()->get($qid);// qid is id of question page
	$note = $myPage->note;
	return $note;
}else{
// normal page load
}
?>

Beispiel 2[Bearbeiten]

Lets say that you want pages in your site to return a JSON string with the page's id, title, and number of children when it is requested from ajax. When not requested from ajax, they will return their content as normal.

To handle the ajax requests, you'd want to add something like this at the top of your template file before any other output.

<?php
if($config->ajax) {
    // this is an ajax request, return basic page information in a JSON string
    $json = array(
        'id' => $page->id,
        'title' => $page->title, 
        'numChildren' => $page->numChildren
        ); 
    echo json_encode($json);
    return;
}

And here is some markup and inline javascript you might use to test the ajax call on some other page (or the same one if you prefer). You would paste this snippet right in your site's markup where you want that info to appear.

<ul id='info'></ul>
<script type='text/javascript'>
    var url = '/'; // this is homepage, so replace '/' with page URL you want to load JSON from or use window.location.href for actual page
    $(document).ready(function() {
        $.getJSON(url, function(data) {
            $.each(data, function(key, value) {
            $("#info").append("<li>" + key + ": " + value + "</li>");
        }); 
    }); 
}); 

</script>

The above snippet would output something like this:

• id: 1
• title: Home
• numChildren: 5

Inhalt einer Seite laden (vereinfacht)[Bearbeiten]

Beachte, man sollte noch einiges mehr beachten (Browser History...) Es gibt ein ausführliches Tutorial dazu.

<?php
if(!$config->ajax) include("./head.inc");
echo $page->body;
if(!$config->ajax) include("./foot.inc");
$("#topnav a").click(function() {

            $("#topnav a.on").removeClass('on'); // unhighlight selected nav item...
            $(this).addClass('on'); // ...and highlight new nav item
            $("#bodycopy").html("<p>Loading...</p>"); 
            
            $.get($(this).attr('href'), function(data) { 
                $("#bodycopy").html(data); 
            }); 

            return false; 
        });

Einführung[Bearbeiten]

Links:

https://webdesign.tutsplus.com/tutorials/how-to-create-an-ajax-driven-theme-for-processwire--cms-26579 (Kurzanleitung)
https://github.com/tutsplus/how-to-create-an-ajax-driven-theme-for-processwire (ProcessWire Profile mit AJAX)

Allgemeines Vorgehen

  • Links mit der Klasse ajax-link werden über JavaScript (meist jQuery) abgefangen und über AJAX an den Server geschickt.
  • ProcessWire erkennt anhand der internen System-Variable $ajax dass ein AJAX Request vorliegt
  • Mit einfachen Ifs kann man ProcessWire dazu bringen nur den gewünschten Content zu generieren
  • Den zurückgegebenen HTML Inhalt mit jQuery austauschen.

Natürlich sind auch JSON Antworten denkbar. Das kann man dann alles über JavaScript verarbeiten

Vereinfachtes Beispiel (bessere Struktur wäre wohl die Teile der Seiten in Variablen zu laden und dann die if Statements kompakt zu halten.

<?php
// include page structure if not an ajax request
if(!$ajax):
    include("./head.inc");
?>
 
<!-- ... -->
<?php echo $pages->get('/')->title; ?>
<nav><!-- ... --></nav>
<div class="content-container cf">
  <div class="content current-content">

<?php endif; // end if ajax ?>
         
<?php
  // if ajax then only return $content
  echo $content;
?>
         
<?php if(!$ajax): ?>
  </div>
</div>

<?php
include("./foot.inc");
endif; // end if ajax
?>

JavaScript

$(function() {
 
    var href;
    var title;
 
    $('body').on('click','a.ajax-link',function(e) { // nav link clicked
 
        href = $(this).attr("href");
        title = $(this).attr("name");
 
        // load content via AJAX
        loadContent(href);
 
        // prevent click and reload
        e.preventDefault();
    });
 
    function loadContent(url){ // Load content
 
        // variable for page data
        $pageData = '';
 
        // send Ajax request
        $.ajax({
            type: "POST",
            url: url,
            data: { ajax: true },
            success: function(data,status){
                $pageData = data;
            }
        }).done(function(){ // when finished and successful
 
            // construct new content
            $pageData = '<div class="content no-opacity ajax">' + $pageData + '</div>';
 
            // add content to page
            $('.content-container').append($pageData);
 
            // remove old content
            $('.content.current-content').remove();
 
            // show new content and clean up classes
            $(this).removeClass('no-opacity').removeClass('ajax').addClass('current-content');
             
        }); // end of ajax().done()
    } // end of loadContent()
});

Komplexeres Beispiel mit Einbeziehen der Browser History, verhindern von doppeltem Laden bereits vorhandener Inhalte...

PHP bleibt gleich.

JavaScript

$(function() {

	// fitvids for responsivce videos
	$(".content .video").fitVids();

	// light box
	$('.gallery').Chocolat();

	// flexslider
	$('.flexslider').flexslider({
		pauseOnHover: true,
		controlNav: false,
		prevText: "",
		nextText: "",
	});


	/*
	*   HTML5 pushstate and ajax content
	*/

	var href;
	var title;

  	$('body').on('click','a.ajax-link',function(e) { // nav link clicked

    	// check to see it contents already accessed
    	if($(this).hasClass('current')){

        	console.log('current page');

			// prevent click through
			e.preventDefault();
			// exist click function (dont load new content)
        	return true;
    	}

      	$('.current').removeClass('current'); // remove .current from active link
		$('.parent').removeClass('parent'); // remove .current from active link
      	$(this).addClass('current'); // add .current

		var parent = $(this).attr('parent');
		if(parent){ // if link page has parent nav
			$('nav a[name="'+parent+'"]').addClass('parent'); // add .current
		}

		// add progress spinner on cursor
      	$('body').css('cursor','progress');

		// remove cusor spinner
		var pointerTimeout = setTimeout(function () {
			$('body').css('cursor','auto'); // progress spinner on cursor
		}, 1000);

      	href = $(this).attr("href");
      	title = $(this).attr("name");

		// load content via AJAX
      	loadContent(href);

		// add new url to html5 history
      	history.pushState('', +href, href); // push url to history

		// change title to new page
      	$('title').html(title);

		// send GA pageview
      	ga('send', 'pageview', href);

		// prevent click and reload
    	e.preventDefault();
	});

    // for back / forwards browser navigation
    var toggleHistoryVisitNum = 0;
    function toggleHistory(){
        window.onpopstate = function(event) {
            if(window.location.hash == ""){
                loadContent(location.pathname);
            }
        };
    }

	function loadContent(url){ // Load content

		// variable for page data
    	$pageData = '';

		// send Ajax request
    	$.ajax({
	        type: "POST",
	        url: url,
	        data: { ajax: true },
	        success: function(data,status){
				$pageData = data;
      		}
    	}).done(function(){ // when finished and successful

			// construct new content
      		$pageData = '<div class="content no-opacity ajax">' + $pageData + '</div>';

			// add content to page
			$('.content-container').append($pageData);

			// animate the requested content
	      	$('.content.current-content').animate({
				// animate out old content
	        	opacity: 0
	      	}, 400, function(){

				$('.content.current-content').slideUp(400);

	          	// content animation finished
	          	$('.content.ajax').animate({
					// animate in new content
	            	opacity: 1
	          	}, 400, function(){

					// remove old content
		            $('.content.current-content').remove();

					// show new content and clean up classes
		            $(this).removeClass('no-opacity').removeClass('ajax').addClass('current-content');

					/*
					* run js functions for new content
					*/

					// fitvids for responsivce videos
					$(".content .video").fitVids();

					// light box
					$('.gallery').Chocolat();

	          	});
			});

	      	// toggle use of back and forward buttons on browser
	      	if(!toggleHistoryVisitNum){
	        	toggleHistory();
	      	}

			// keep track of number of Ajax requests
			toggleHistoryVisitNum++;

    	}); // end of ajax().done()
  	} // end of loadContent()
});