Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a controller method within Play! template

How can I call a controller method from within a Play! template?

I have a default controller, Application, and the hasliked() method inside that controller. The method returns whether the logged in user has liked the post ID. It returns "none" if the user has liked the post, otherwise it returns "block" (for the CSS)

I have added the following route: GET /hasliked/{id} Application.hasliked

I tried the following:

#{list items:postList, as:'post'} %{ display = Application.hasliked(post.id); %}

<div style="display: ${display}">...</div> #{/list}

But I get this error:

Template execution error

Execution error occured in template /app/views/Application/dashboard.html. Exception raised was NullPointerException : Cannot invoke method hasliked() on null object.

like image 817
Gaui Avatar asked Nov 18 '11 11:11

Gaui


3 Answers

Try using a fully qualified name like:

controllers.Application.hasliked()

EDIT on comment:

The issue with your exception is that you are accessing the controller to get a value. That's wrong.

Controllers in Play are used to navigate. They are static, they return "void", and they do a call to another controller method or to a render method. What you try to do may have unexpected results.

What you want to do is to get the value inside the controller and pass it as a parameter:

//On controller

public static void yourRequest() {
   //...
   Object display = getDisplay(); //get your value
   render(display);
}

//On template
<div style="display: ${display}">...</div>

That's the recommended way.

The exception you get is (most likely) caused because your Application.hasLiked() ends up with a redirect call (either render() or call to another controller's method) and that's happening while you render the page corresponding to the initial call. So it breaks.

like image 88
Pere Villega Avatar answered Oct 04 '22 08:10

Pere Villega


It would probably be a better way to do fill the information that is required into the list of items instead of calling back the controller:

  • Your template doesn't need to know about your controllers. It should just convert data to HTML and not acquire data from somewhere else. That's the task of the controller.
  • It would also be more efficient in terms of database access to fetch the like status for all items at once instead of doing several calls.
  • When doing refactoring (e.g. renaming methods etc of your controller) the IDE cannot help you if you call controllers from the template (unless it's aware of how Play! templates work).

If you really must do this (and again, you shouldn't) you need to fully qualify the name of the controller:

controllers.Application.hasLiked() 

just like Pere Villega pointed out.

like image 23
Jan Thomä Avatar answered Oct 04 '22 09:10

Jan Thomä


An alternative to this may be to issue an AJAX call to set the style, rather than using the controller.

Set the style of your "liked" element to display: none by default, and when your view has rendered, issue an GET request to /hasliked/ with the ID as a parameter and update your CSS styles accordingly: when the user has not already liked this, output false (or whatever you want), so that you can use JavaScript to re-define the style.

The easiest way to do this would be to use jQuery to issue a request to your controller when the view has loaded. Have a look at the Play! documentation on AJAX for some inspiration. Note that you don't have to use #{jsAction /} at all - personally I find it easier to define the jQuery calls myself.

like image 30
tmbrggmn Avatar answered Oct 04 '22 07:10

tmbrggmn