PDA

View Full Version : php/ajax form posts fine, won't refresh with updated values


bustamelon
01-18-2008, 06:47 AM
Sorry if this has been covered before. I spent a long time sifting but didn't see this exact problem discussed.... or the common solution to a similar problem doesn't work for me.

I have a PHP/MySQL-generated calendar that is used as a sub-form on a larger php form. The calendar lets users block out dates for vacation rentals, and enter weekly rates for each week/calendar row. SQL and POST works great. DB gets updated without any problems. The problem is, when the calendar is redrawn, it uses old data and makes it look to the user like it ignored/erased his work.

It first uses PHP to create the array of dates, performs a SQL query to get rates and blackout dates, then draws the calendar. I use a javascript toggle to make the dates behave like checkboxes, where red is checked/booked, green is available. The SAVE button submits the values to another php script via AJAX post, which in turn calls the calendar rendering function again.

I have found the browser cache issue discussed ad nauseum and have tried every suggestion to no avail -- adding a long random number to the url string doesn't work, no amount of cache-clearing headers helps at all. What may be happening is that the javascript functions are reloading fine but they're using the original (outer html) sql results over and over again.... or maybe the db is still busy inserting/updating when the reloaded page is already performing the new select? idunno but I am downright stumped. Admittedly, I'm a total AJAX noob, and never was a real javascript whiz either.

I can post the full code if you want but what I have is massive and I will have to whittle it down a bit to protect the innocent.

<script language="javascript">
/**************************************
Calendar functions:
**************************************/
function createQCObject() {
var req;
if(window.XMLHttpRequest){
// Firefox, Safari, Opera...
req = new XMLHttpRequest();
} else if(window.ActiveXObject) {
// Internet Explorer 5+
req = new ActiveXObject("Microsoft.XMLHTTP");
} else {
alert('Problem creating the XMLHttpRequest object');
}
return req;
}

// Make the XMLHttpRequest object
var http = createQCObject();

function displayQCalendar(m,y,rid) {
var ran_no=(Math.round((Math.random()*9999)));
http.open('get', '<?php echo $ajaxPath; ?>?m='+m+'&y='+y+'&rid='+rid+'&ran='+ran_no, true);
http.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
http.setRequestHeader("Last-Modified", "Sat, 1 Jan 2000 01:00:00 GMT");
http.setRequestHeader("Expires", "Sat, 1 Jan 2000 01:00:00 GMT");
http.setRequestHeader("Cache-Control", "no-store, no-cache, must-revalidate"); // HTTP/1.1
http.setRequestHeader("Cache-Control", "post-check=0, pre-check=0", false);
http.setRequestHeader("Pragma", "no-cache");
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
var response = http.responseText;
if(response) {
document.getElementById('quickCalendar').innerHTML = http.responseText;
//alert(document.getElementById('quickCalendar').innerHTML);
}
}
}
http.send(null);
}



/**************************************
Form submission functions:
**************************************/
var xmlReq = null;

function sub(f) {
var file = '/webadmin/lib/save_calendar.php';
var str = getFormValues(f,"validate");
xmlReq = getXML(file,str);
}


function getXML(file,str) {
var doc = null;
var month = document.getElementById("month").value;
var year = document.getElementById("year").value;
var rid = document.getElementById("rid").value;
if (typeof window.ActiveXObject != 'undefined' ) {
doc = new ActiveXObject("Microsoft.XMLHTTP");
doc.open( "POST", file, true );
//doc.onreadystatechange = displayQCalendar(month,year,rid);
doc.onreadystatechange = new Function(displayQCalendar(month,year,rid));
}else{
doc = new XMLHttpRequest();
doc.open( "POST", file, true );
doc.onload = displayQCalendar(month,year,rid);
}
doc.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
doc.send(str);
return doc;
}


function getFormValues(fobj,valFunc) {
var str = "";
var valueArr = null;
var val = "";
var cmd = "";
for(var i = 0;i < fobj.elements.length;i++){
switch(fobj.elements[i].type){
case "text":
case "hidden":
//alert(fobj.elements[i].name + ': ' + fobj.elements[i].value);
case "password":
case "textarea":
str += fobj.elements[i].name + "=" + encodeURIComponent(fobj.elements[i].value) + "&";
break;
case "checkbox":
case "radio":
if(fobj.elements[i].checked) str += fobj.elements[i].name + "=" + encodeURIComponent(fobj.elements[i].value) + "&";
break;
case "select-one":
str += fobj.elements[i].name + "=" + fobj.elements[i].options[fobj.elements[i].selectedIndex].value + "&";
break;
}
// debug: (WARNING - this outputs every var one at a time, i dont recommend it)
//alert(fobj.elements[i].name + ': ' + fobj.elements[i].value);
}
str = str.substr(0,(str.length - 1));
return str;
}


// replace traditional checkboxes with color toggle and hidden fields
function toggleColor(obj,chkID,val) {
if(chkID) {
var chk = document.getElementById(chkID + '-checkbox');
if ( (obj.style.backgroundColor == "#FF6666") || (obj.className == "booked")) {
// clear an existing block to make date available -- red to green
obj.style.backgroundColor = "#99FF99";
obj.className = "available";
chk.value = "0";
} else {
// block an available date - green to red
obj.style.backgroundColor = "#FF6666";
obj.className = "booked";
chk.value = val;
}
}
}
</script>

<?php

class CreateQCalendarArray {

var $daysInMonth;
var $weeksInMonth;
var $firstDay;
var $week;
var $month;
var $year;
var $rid;

function CreateQCalendarArray($month, $year, $rid) {
$this->rid = $rid;
$this->month = $month;
$this->year = $year;
$this->week = array();
$this->daysInMonth = date("t",mktime(0,0,0,$month,1,$year));
// get first day of the month
$this->firstDay = date("w", mktime(0,0,0,$month,1,$year));
$tempDays = $this->firstDay + $this->daysInMonth;
$this->weeksInMonth = ceil($tempDays/7);
$this->fillArray();
}

function fillArray() {
// create a 2-d array
$counter = 0;
for($j=0;$j<$this->weeksInMonth;$j++) {
for( $i=0;$i<7;$i++ ) {
$counter++;
$this->week[$j][$i] = $counter;
// offset the days
$this->week[$j][$i] -= $this->firstDay;
if (($this->week[$j][$i] < 1) || ($this->week[$j][$i] > $this->daysInMonth)) {
$this->week[$j][$i] = "";
}
}
}
}
}

class QCalendar {

var $rid;
var $html;
var $weeksInMonth;
var $week;
var $month;
var $year;
var $today;
var $links;
var $css;

function QCalendar( $rid, $cArray, $today, &$links, $css='' ) {
$this->rid = $rid;
$this->month = $cArray->month;
$this->year = $cArray->year;
$this->weeksInMonth = $cArray->weeksInMonth;
$this->week = $cArray->week;
$this->today = $today;
$this->links = $links;
$this->css = $css;
$this->createHeader();
$this->createBody();
$this->createMemo();
$this->createFooter();
}

function createHeader() {
$header = date('M', mktime(0,0,0,$this->month,1,$this->year)).' '.$this->year;
$nextMonth = $this->month+1;
$prevMonth = $this->month-1;
// thanks adam taylor for modifying this part
switch($this->month) {
case 1:
$lYear = $this->year;
$pYear = $this->year-1;
$nextMonth=2;
$prevMonth=12;
break;
case 12:
$lYear = $this->year+1;
$pYear = $this->year;
$nextMonth=1;
$prevMonth=11;
break;
default:
$lYear = $this->year;
$pYear = $this->year;
break;
}
// --
$this->html = "
<table cellspacing='0' cellpadding='0' class='$this->css'>
<tr><th colspan='9' style='font-size: 80%'>Click 'Save' for each month where changes are made</th></tr>
<tr>
<th class='header'>&nbsp;<a href=\"javascript:;\" onclick=\"displayQCalendar('$this->month','".($this->year-1)."','$this->rid')\" class='headerNav' title='Prev Year'><<</a></th>
<th class='header'>&nbsp;<a href=\"javascript:;\" onclick=\"displayQCalendar('$prevMonth','$pYear','$this->rid')\" class='headerNav' title='Prev Month'><</a></th>
<th colspan='3' class='header'>$header</th>
<th class='header'><a href=\"javascript:;\" onclick=\"displayQCalendar('$nextMonth','$lYear','$this->rid')\" class='headerNav' title='Next Month'>></a>&nbsp;</th>
<th class='header'>&nbsp;<a href=\"javascript:;\" onclick=\"displayQCalendar('$this->month','".($this->year+1)."','$this->rid')\" class='headerNav' title='Next Year'>>></a></th>
<th class='header rate'></th>
<th class='header rate'></th>
</tr>";
}

function createBody(){
// start rendering table
$this->html.= "<tr><th>S</th><th>M</th><th>T</th><th>W</th><th>Th</th><th>F</th><th>S</th><th>Rate</th></tr>";
for($j=0;$j<$this->weeksInMonth;$j++) {
$this->html.= "<tr>";
for ($i=0;$i<8;$i++) {
$cellValue = $this->week[$j][$i];
$chkid = $cellValue;
if ( $chkid == "" ) {
$chkid = 0;
}

// if today
if (($this->today['day'] == $cellValue) && ($this->today['month'] == $this->month) && ($this->today['year'] == $this->year)) {
$cell = "<div class='today' id='$cellValue' onclick=\"toggleColor(this,$chkid,1)\">$cellValue</div><input type='hidden' name='book[$cellValue]' id='$cellValue-checkbox' value='' />";
}
// else normal day
else {
$cell = "<div class=\"available\" style=\"background-color: #99FF99;\" id='$cellValue' onclick=\"toggleColor(this,$chkid,1)\">$cellValue</div><input type='hidden' id='$cellValue-checkbox' name='book[$cellValue]' value='' />";
}

// if days with link/booking
$rates_filled = 0;
foreach ($this->links as $val) {

if (($val['day'] == $cellValue) && (($val['month'] == $this->month) || ($val['month'] == '*')) && (($val['year'] == $this->year) || ($val['year'] == '*'))) {
//$cell = "<div class='booked'><a href=\"{$val['link']}\" title='{$val['desc']}'>$cellValue</a></div>";
$cell = "<div class=\"booked\" style=\"background-color: #FF6666;\" id='$cellValue' onclick=\"toggleColor(this,'$chkid',1)\">$cellValue</div><input type='hidden' id='$cellValue-checkbox' name='book[$cellValue]' value='$val[status]' />";
break;
}
// fill in rate value
if ( $i > 6 && $i < 8 ) {
$cell = "<div class='rate'><input type='text' size='6' name='rate[$j]' id='rate[$j]' value='" . getRate( $j, $val['month'], $val['year'] ) . "' />";
$rates_filled = 1;
break;
}
}
if( !$rates_filled ){
if ( $i == 7 ) {
$cell = "<div class='rate'><input type='text' size='6' name='rate[$j]' id='rate[$j]' value='' />";
}
}

$this->html .= "<td>$cell</td>";
}
$this->html .= "</tr>";
}
$this->html .= "<input type='hidden' name='month' id='month' value='$this->month' />";
$this->html .= "<input type='hidden' name='year' id='year' value='$this->year' />";
$this->html .= "<input type='hidden' name='rid' id='rid' value='$this->rid' />";
}

function createMemo() {
// get and report moth memo:
global $m, $y, $rid;
$memo = "";
$sql = "SELECT memo FROM calendar_memos WHERE rental_id='$rid' AND (month='$m' AND year='$y') || (month='*' AND year='$y') || (month='$m' AND year='*') || (month='*' AND year='*')";
//echo "<br>SQL: $sql<br>"; // debug
$rs = mysql_query( $sql );
while ( $rw = mysql_fetch_array( $rs )) {
//print_r( $rw ); // debug
$memo = $rw['memo'];
}
$this->html .= "<tr><td colspan='9' class='memo'><textarea name='memo' style='height: 60px;'>$memo</textarea></td></tr>";
}


function createFooter() {
$this->html .= "<tr><td colspan='9' class='footer'><a href=\"javascript:;\" onclick=\"displayQCalendar('{$this->today['month']}','{$this->today['year']}','$this->rid')\" class='footerNav'>Today is {$this->today['day']} ".date('M', mktime(0,0,0,$this->today['month'],1,$this->today['year']))." {$this->today['year']}</a></td></tr>";
$this->html .= "<tr><td colspan='9' class='footer' align='right'><input type='button' name='save' value=' Save ' class='formbutton' onClick='sub(this.form)' /></td></tr></table>";
}


function render() {
echo $this->html;
}
}





// render calendar now
$cArray = &new CreateQCalendarArray( $m, $y, $rid );
$cal = &new QCalendar( $rid, $cArray, $today, $links, $css );
if (!isset($_GET['ran'])) {
echo "<div id='quickCalendar'>
<form action='$ajaxPath' method='post'>";
}
$cal->render();
if (!isset($_GET['ran'])) {
echo " </form>
</div>";
}
?>

bustamelon
01-18-2008, 04:09 PM
update:

by commenting out these lines where the form data gets posted, I have basically prevented the inner HTML from redrawing... which is not what I want but at least the user's changes stay on the screen!


//doc.onreadystatechange = new Function(displayQCalendar(month,year,rid));

//doc.onload = displayQCalendar(month,year,rid);


still interested in other thoughts/ideas... mods: should I cross-post this under PHP?
Anyone know of any good tutorials on using callbacks with "disabled" form buttons? In other words, I click "SAVE", the button goes grey and says "saving..." until it hears a response from the child script... then redraws the inner html?

A1ien51
01-21-2008, 10:56 PM
This line here

doc.onload = displayQCalendar(month,year,rid);

is saying execute this function and store the values into doc.onload.

You are NOT storing the function call.

Both of the troubled areas should be closures.

...... = function(){ displayQCalendar(month,year,rid); }

Eric

bustamelon
01-22-2008, 05:34 AM
This line here

doc.onload = displayQCalendar(month,year,rid);

is saying execute this function and store the values into doc.onload.

You are NOT storing the function call.

Both of the troubled areas should be closures.

...... = function(){ displayQCalendar(month,year,rid); }

Eric

Beautiful! Thanks, Eric. That did it. Plus I learned a thing or two in exploring your suggestion further.

bustamelon
01-22-2008, 06:12 AM
whoops, had a separate IE issue too but i figured it out...

A1ien51
01-22-2008, 02:06 PM
Read the Appendix B from my book. It will help you start to learn about OO JavaScript

http://java.sun.com/javascript/ajaxinaction/Ajax_in_Action_ApB.html

Eric