Gaui
Gaui

Reputation: 8959

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.

Upvotes: 5

Views: 8000

Answers (4)

Siarhei Skavarodkin
Siarhei Skavarodkin

Reputation: 118

Try the following:

<script type="text/javascript" language="javascript">
    var url = "@{Application.hasliked}" + "/" + yourId;
    window.location.replace(url);
</script>

Upvotes: -1

tmbrggmn
tmbrggmn

Reputation: 8830

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.

Upvotes: 1

Pere Villega
Pere Villega

Reputation: 16439

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.

Upvotes: 8

Jan Thom&#228;
Jan Thom&#228;

Reputation: 13630

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.

Upvotes: 4

Related Questions