1

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
Nick
  • 3,172
  • 3
  • 37
  • 49
  • In your action do `pr($this)` (having debug on) and then check if it shows that the request is ajax. Also, try `$this->ResquestHandler->isAjax()` instead. It is deprecated on 2.x, but see what gives. – rlcabral Sep 09 '12 at 12:17
  • When I do `pr($this)` I get a long dump of all the items on the list (not sure if that's exactly what you want). All the items have `[isAjax] => ` (meaning not set). Is that the info you were looking for? I tried `$this->ResquestHandler->isAjax()` since it's working in my AJAX delete button but there was no change in my results here (as expected). – Nick Sep 09 '12 at 18:33
  • Yes, `[isAjax]` was what I wanted. It shows that your request is not being treated as AJAX request. Do you have firebug or any debug tool to check the request sent by the browser? Namely, the data and request headers sent. – rlcabral Sep 09 '12 at 18:56
  • I added headers dumped from Chrome's Network debugger to the question above (no room for them here). Let me know if you need more. Thanks. – Nick Sep 09 '12 at 21:02

1 Answers1

2

For some reason X-Requested-With is not presented. It should show this in your request header:

X-Requested-With: XMLHttpRequest

Here are some questions that may help:

Is the AJAX request that is working being called the same way that this one?

Community
  • 1
  • 1
rlcabral
  • 1,496
  • 15
  • 39
  • Thanks! Your info and questions helped me figure it out. I didn't have the right headers as your answer stated. The source of that was that I still had an `onclick` element on the item I was trying to attach the JQuery calls to. So the `onclick` would fire instead of the JQuery ajax bit. Removing `onclick` fixed my headers. – Nick Sep 09 '12 at 22:19