I'm trying to create a favorite/star/heart button (like in Gmail) for each item on a list of elements in CakePHP 2.2.
I've got a working controller and a JS toggle function but my problem is that the AJAX parts of my scripts aren't actually happening; instead the page redirects to itself (as part of the fallback for browsers with JavaScript off) and the reloads. This is what I'm trying to avoid so the users don't loose their position on the page.
Here's my CakePHP controller that Un-Favorites an item:
class MarksController extends AppController {
public $components = array('RequestHandler');
public function unfav($id = null) {
// set default class & message for setFlash
$class = 'flash_bad';
$msg = 'Invalid List Id';
if ( validation($id) ) {
//load this mark's data
$this->request->data = $this->Mark->read(null, $id);
$this->request->data['Mark']['favorite'] = '0';
if ($this->Mark->save($this->request->data)) {
$class = 'message';
$msg = 'The WatchMark has removed from favorites.';
} else {
$msg = 'This WatchMark could not be saved. Please, try again.';
}
$this->autoRender= false;
$this->autoRender = $this->layout = false;
// output JSON on AJAX request
if($this->request->is('ajax')) {
echo json_encode(array('success'=>($class=='flash_bad') ? FALSE : TRUE,'msg'=>"{$msg}"));
exit;
}
// set flash message & redirect
$this->Session->setFlash($msg,'default',array('class'=>$class));
$this->redirect($this->referer());
} else {
//trying to edit a mark that doesn't belong to this user:
throw new NotFoundException(__('Invalid mark'));
} // end ownedby check
} //end markfav method
I have another class that's the inverse function called markfav (combining them into a single toggle function is a task for another day).
Then, on the JQuery & JavaScript side:
$(document).ready(function() {
// class exists
if($('.fav_toggler').length) {
// add click handler
$('.fav_toggler').click(function(e){
$('#flashMessage').fadeOut();
if($(this).hasClass("heart")) {
$.post('/marks/markfav/' + $(this).attr('target_id'), "ajax=1", function(response) {
if(response.success == true) {
$(this).addClass("heart").removeClass("hearttoggle");
}
},'json');
}
else {
$.post('/marks/unfav/' + $(this).attr('target_id'), "ajax=1", function(response) {
if(response.success == true) {
$(this).addClass("heart").removeClass("hearttoggle");
}
},'json');
}
e.preventDefault();
});
return false;
}
});
I've tested by throwing alerts into my JavaScript and I think that it's executing correctly (it gets to the right part of my code and changes the class between heart & hearttoggle.
I also know that it's skipping the if($this->request->is('ajax'))
block in my PHP for some reason (though I have an AJAX delete function that hits a similar conditional just fine). I've tested removing the if($this->request->is('ajax'))
condition from the PHP block to force it to execute the JSON_encode bit and I just end up with a page containing the JSON encoded array and nothing else.
How do I make this work correctly using AJAX without a page reload?
EDIT: Headers from call to /marks/unfav/624
Request URL:http://towatchlist.com/marks/unfav/624
Request Method:GET
Status Code:200 OK
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Cookie:CAKEPHP=blah
Host:towatchlist.com
Referer:http://towatchlist.com/marks
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/537.8 (KHTML, like Gecko) Chrome/23.0.1255.0 Safari/537.8
Response Headers
Connection:Keep-Alive
Content-Encoding:gzip
Content-Type:text/html
Date:Sun, 09 Sep 2012 20:55:18 GMT
Keep-Alive:timeout=5, max=94
Server:Apache/2.2.22 (Ubuntu)
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-Powered-By:PHP/5.3.10-1ubuntu3.2