Using requestAction & custom layouts to add XHR functionality
3 : Views and layouts
The view for singlecalendar looks like this:
View Template:
/*Format the date params a little */
$calMonthInt = (strlen($calMonthInt) ==1)?"0".$calMonthInt :$calMonthInt ;
$YYYYMM = $calYearInt.'-'.$calMonthInt.'';
$firstDayOfMonthUnixTimestamp = strtotime($YYYYMM);
$viewingMonth = date("F",$firstDayOfMonthUnixTimestamp);
$viewingYear = date("Y",$firstDayOfMonthUnixTimestamp);
echo "this is the Calendar for <h2>".$viewingMonth. "</h2><h3>". $viewingYear."</h3><br />";
Our calendarnavigation view however must use links to other calendars, and looks like this:
View Template:
$calMonthInt = (strlen($calMonthInt) ==1)?"0".$calMonthInt :$calMonthInt ;
$YYYYMM = $calYearInt.'-'.$calMonthInt.'';
$firstDayOfMonthUnixTimestamp = strtotime($YYYYMM);
$viewingMonth = date("F",$firstDayOfMonthUnixTimestamp);
$viewingYear = date("Y",$firstDayOfMonthUnixTimestamp);
//next month
$nextMonthText = date('F Y',strtotime("+1 months",$firstDayOfMonthUnixTimestamp)).">>";
// previous month
$previousMonthText = "<<".date('F Y',strtotime("-1 months",$firstDayOfMonthUnixTimestamp));
//next month link
$nextMonthLink = "/calendar/generatecalendar/".date('Y\/m',strtotime("+1 months",$firstDayOfMonthUnixTimestamp));
// previous month link
$previousMonthLink = "/calendar/generatecalendar/".date('Y\/m',strtotime("-1 months",$firstDayOfMonthUnixTimestamp));
?>
<div id="calendar_navigation" style="text-align:center;">
<?php echo $html->link($previousMonthText,$previousMonthLink); ?>
<?php echo $viewingMonth ?> <?php echo $viewingYear ?>
<?php echo $html->link($nextMonthText,$nextMonthLink); ?>
</div>
Notice that the views contain links to other methods. This is fine for the non-XHR but means that we do not have any links to further refresh parts of the page, using these links would refresh the whole page.
This is where our alternate layouts come into their own. By adding Javascript code to the additional layouts we can rewrite the links so that they call updates, not whole pages. Using this method keeps XHR alternatives out of our core Cake MVC files and adds them as further customisations within layouts only called when necessary.
Without XHR, the custom layout is ignored and the links remain the same.
With XHR, the custom layout ensures that the page navigation continues to provide dynamic updates.
The layout views/layouts/calendardynamic.thtml simply drops in the same content, and looks like this:
<div style="background-color:#fff;padding:2em;">
<?php echo $content_for_layout ?>
</div>
The layout views/layouts/calendarnavigationdynamic.thtml has URLs to rewrite, and uses the Mootools framework (you don't have to use Mootools, So long as you update the same elements from the
main layout and index view ,it does not matter how you acheive it:
<div id="calendar_navigation" style="background-color:#e0e0e0;padding:2em;">
<?php echo $content_for_layout ?>
</div>
<script type="text/javascript">
$$('#calendar_navigation a').each(function(el){
var url = el.getProperty('href');
url = url.replace(/generatecalendar/,'singlecalendar');
el.addEvent('click', function(e) {
e = new Event(e).stop();
e.preventDefault();
new Ajax(url, {
method: 'get',
onRequest: function(){
$('calendarDiv').setStyle('border','12px solid red');
},
onSuccess: function(){
$('calendarDiv').setStyle('border','1px dotted blue');
UpdateNav(url);
},
update: $('calendarDiv')
}).request();
//
function UpdateNav(url) {
url = url.replace(/singlecalendar/,'calendarnavigation');
new Ajax(url, {
method: 'get',
evalScripts: true,
onRequest: function(){
$('calendarNavDiv').setStyle('border','12px solid red');
},
onSuccess: function(){
$('calendarNavDiv').setStyle('border','1px dotted blue');
},
update: $('calendarNavDiv')
}).request();
}
});
});
</script>
The javascript in the views\layouts/calendarnavigationdynamic.thtml layout simply searches for the links as they are output from the controller and rewrites them.
First it finds all anchor elements loaded into the DOM element calendar_navigation and adds a (Mootools) function to add an AJAX request to it:
$$('#calendar_navigation a').each(function(el){...
it then detects any urls with the non-ajax components, and replaces them:
var url = el.getProperty('href');
url = url.replace(/generatecalendar/,'singlecalendar');
The rest of the function tells the link what to do while loading and which DOM element to update when the request completes. In this example the methods sequenced, when the singlecalendar request successfully returns (updating the calendar), the calendarnavigation function is called that updates the navigation. Making one rely on the other helps to ensure that your two dynamic elements remain in sync.
The DOM elements that are requested in these functions exist in the index view, which is shown here complete, with XHR and non XHR links:
views/calendar/index.thtml
View Template:
<h1>Calendar Index</h1>
<h3>Non Ajax</h3>
<p><a href="/calendar/generatecalendar">Go to The Calendar Page</a></p>
<h3>Ajax</h3>
<p><a href="#" id="calendarGen">Load The Ajax Calendar</a></p>
<div id="calendarNavDiv" style="border:1px solid black;margin-top:.5em;">
<h3>This is the Calendar Nav Div</h3>
The Calendar Navigation will load here
</div>
<div id="calendarDiv" style="border:1px solid black;margin-top:.5em;">
<h3>This is the Calendar Div</h3>
The Calendar will load here
</div>
<script type="text/javascript">
$('calendarGen').addEvent('click', function(e) {
e = new Event(e).stop();
var url = "/calendar/singlecalendar";
new Ajax(url, {
method: 'get',
onRequest: function(){
$('calendarDiv').setStyle('border','12px solid red');
},
onSuccess: function(){
$('calendarDiv').setStyle('border','1px dotted blue');
},
update: $('calendarDiv')
}).request();
});
$('calendarGen').addEvent('click', function(e) {
e = new Event(e).stop();
var url = "/calendar/calendarnavigation";
/**
* The simple way for an Ajax request, use onRequest/onComplete/onFailure
* to do add your own Ajax depended code.
*/
new Ajax(url, {
method: 'get',
evalScripts: true,
onRequest: function(){
$('calendarNavDiv').setStyle('border','12px solid red');
},
onSuccess: function(){
$('calendarNavDiv').setStyle('border','1px dotted blue');
},
update: $('calendarNavDiv')
}).request();
});
</script>
Latest Comments