ProcessWire - Ajax
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()
});