11

I'm trying to figure out how to use one of my view elements inside of a controller...

I know, I know: "Don't do that!" (99% of the time this is the correct answer)

But I think I actually have a good reason. The action is handling an AJAX request which returns markup. The returned markup is a list which I display everywhere else using an element. So in an effort to keep my code DRY, I think it's appropriate to do this here.

Is this possible?

tereško
  • 56,151
  • 24
  • 92
  • 147
emersonthis
  • 30,934
  • 52
  • 191
  • 328
  • You can view your ajax callback using your browser. For example (ctrl+shift+i) network section in chrome. You can see the request and callbacks and the view that you want in html view . – Fury Feb 15 '19 at 14:50

6 Answers6

22

Easy:

$view = new View($this, false);
$content = $view->element('my-element', $params);

Also:

DON'T DO THAT ANYMORE!!!

  • It breaks the flow of the data in the MVC pattern that cakephp uses. ie the controller should not have access to the view like the model should not have access to the controller and the view should not access the model. This ensures everything goes 'flows' nicely – Andrew Mcghie Dec 09 '17 at 07:28
  • I really agree that this is not the right way, but I'm building a AngularJS project which there is a helper to handling image cache an so on, I just can't write an entire tool in JS to deal with the images. For me, this is the only way. But, don't do that anymore guys! LOL – Leonardo Aug 11 '18 at 13:49
10

Sometimes, you need to render a CakePhp element from a view and inject its content into the page using AJAX the same time. In this case rendering element as a regular view from controller is better than creating a dedicated view that just contains <?php echo $this->element('some_element') ?>, and may be done this way:

<?php
public function ajax_action() {
    // set data used in the element
    $this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));

    // disable layout template
    $this->layout = 'ajax';

    // render!
    $this->render('/Elements/some_element');
}
Serge S.
  • 4,505
  • 3
  • 36
  • 45
5

I know this is an old question and other people have already given basically the same answer, but I want to point out that this approach (provided by Serge S.) ...

<?php
public function ajax_action() {
    // set data used in the element
    $this->set('data', array('a'=>123, 'b'=>456, 'd'=>678));

    // disable layout template
    $this->layout = 'ajax';

    // render!
    $this->render('/Elements/some_element');
}

...is not a hacky workaround, but is in fact the recommended approach from the CakePHP docs for this common and legitimate use case:

If $view starts with ‘/’, it is assumed to be a view or element file relative to the /app/View folder. This allows direct rendering of elements, very useful in AJAX calls.

(Again: Credit to Serge S. for the code above)

mga226
  • 124
  • 2
  • 4
2

$this->view = '/Elements/myelement';

ADmad
  • 8,032
  • 14
  • 17
1

You should use a client-side template. You should never return mark-up from a web service or API, just data. Have your JavaScript take the data, and then format it how you wish.

For example:

function getItems() {
    $.get('/some/url', function(response) {
        if (response.data.length > 0) {
            for (var i = 0; i < response.data.length; i++) {
                var item = response.data[i];
                $('.results').append('<li>' + item.title + '</li>');
            }
        }
    });
};

This is just an example written off the cuff. Obviously you’ll need to write your own implementation.

Martin Bean
  • 33,901
  • 20
  • 114
  • 183
  • I don't necessarily disagree with you but why should I *never* return mark-up from a web service/API? The problem with just sending the data in this situation is that none of the Cake magic is available if I'm building the markup in javascript (presumably in the `success()` callback). I'll be rewriting all the same code and maintaining it in two different places. That doesn't seem ideal either. – emersonthis Apr 19 '13 at 00:57
  • Separation of concerns. And if you’re using the same API call in multiple places, wrap the templating into a function and call that function in place instead. What happens if you need to change the mark-up generated in one part of your app but not the other? What happens if you just want the data to then spit out as JSON via an API? What if someone else wants to use your API? Spitting out HTML is never a good idea. – Martin Bean Apr 19 '13 at 15:43
  • I love to separate concerns, but isn't the point of using elements to NOT maintain them separately? I'm still not clear on how to do this without either A) sending back markup or B) maintaining two duplicate blocks of the same markup (one as javascript and the other as PHP used elsewhere). Am I missing something? – emersonthis Apr 19 '13 at 16:36
  • You want to return data only from an API call. Another advantage is it saves bytes and increases load times. You say you display the result of this call everywhere. Well instead of grabbing the HTML via AJAX each time, create a function that grabs the data from the call via AJAX, wraps it in whatever HTML, and call that function in place instead. You can then change the HTML at a later data without touching your API. – Martin Bean Apr 19 '13 at 17:32
  • I should clarify: I don't use the AJAX call everywhere. The same markup appears in many places on the site. It only results from an AJAX call in this one situation. The part I'm still not clear about is how to "wrap the data in whatever HTML" on the client side (after the AJAX response) without supporting a whole second block of code javascript that would have to be seperately updated every time I modify the way those data tables should look. – emersonthis Apr 19 '13 at 19:08
  • Like I say, wrap it in a function, as I have in my example, and then just call that function instead of making an AJAX request. – Martin Bean Apr 19 '13 at 20:24
  • In other words: maintain the markup in two different places? Isn't that what you're proposing? – emersonthis Apr 20 '13 at 22:33
  • It’s difficult to comment without knowing the exact scenario. All you’ve told it you want to spit out a view from a controller, which goes against MVC principles. – Martin Bean Apr 20 '13 at 22:56
0

The way I did any ajax handling in Cake was to have my own AjaxController. Any interaction of ajax-kind goes there, which in-turn uses their own views (and view partials / elements). That way you can keep your code DRY and isolate and propagate all ajax use-cases there.

Example excerpt:

<?php
class AjaxController extends AppController {
    /**
    * (non-PHPdoc)
    * Everything going to this controller should be accessed by Ajax. End of story.
    * @see Controller::beforeFilter()
    */
    public function beforeFilter() {
        parent::beforeFilter();
        $this->autoRender = false;
        $this->layout = false;
        if (!$this->request->is('ajax')) {
            $this->redirect('/');
        }
    }
    public function preview() {
        if ($this->request->is('ajax')) {
            $this->set('data', $this->data);
            $this->render('/Elements/ajaxpreview');
        }
    } 
?>

Here's the source: https://github.com/Sobient/dosspirit/blob/master/app/Controller/AjaxController.php

Coreus
  • 4,504
  • 3
  • 31
  • 43