nize
nize

Reputation: 1032

How do I invoke a template from another template in Play Framework?

I have a template accountlist.scala.html looking like this:

@(accounts: models.domain.AccountList)
@titlebar = {<p>some html</p>}
@content = {
    @for(account <- accounts) {
        <p>@account.name</p>
    }
}

@main(titlebar)(content)

... and another template account.scala.html like this:

@(account: models.domain.Account)

@titlebar = {<p>@account.name</p>}
@content = {
    @for(transaction <- account.getTransactions()) {
        <p>@transaction.detail</p>
    }
}
@main(titlebar)(content)

From both of them I am invoking the template main.scala.html.

I have access to the entire Account POJO in the first view accountlist.scala.html, so really there is no need for me to invoke the server to get the details of the account when I go to the view in which I display the details. I would just like to change view on the client side. How could I call the second view account.scala.html from the view accountlist.scala.html a user clicks on an account in the list? I am ready to change the templates as needed.

Upvotes: 2

Views: 1667

Answers (2)

nize
nize

Reputation: 1032

The question reveals a misunderstanding of play framework templates. When compiling the play project the template code is transformed to html, css and javascript.

You can not "invoke"/link another template showing the account transactions from a href attribute of your Account row. However, you can do any of the following:

  1. In case you have loaded all transactions from all accounts to the client in one go: extend the template to generate separate <div> sections for each account showing the transactions. Also generate javascript to 1) hide the overview div and 2) show the specific transaction div when clicking on one of the accounts in the overview. Please see the knockout library proposed by Edmondo1984 or the accordion or tabs in twitter bootstrap.
  2. In case you only load the account overview from the server. Generate a link such as this one href="@routes.Controllers.account(account.id)" (see Edmondo1984 answer) and make another template to view this data.

Since the question concerned a case in which you got all data from the server, go by option 1.

Upvotes: 0

Edmondo
Edmondo

Reputation: 20080

I have provided a previous answer, which is still available at the end of this post. From your comments, however I understand that you are asking for something else without understanding how dangerous it is.

There are three ways of handling your use case, let's start with the worst one.

A stateful web application

If you have loaded data into a Pojo from some data source and you want to re-use the Pojo between multiple requests, what you are trying to do is to implement some kind of client-state on the server, such as a cache. Web applications have been developed in this way for long time and this was the source of major bugs and errors. What happens if the underlying account data in the database is updated between one http request and the following? Your client won't see it, because it use cached data. Additionally, what happens when the user performs a back in his browser? How do you get notified on the server side so you keep track of where the user is in his navigation flow? For these and others reasons, Play! is designed to be stateless. If you are really into web applications, you probably need to read about what is the REST architectural style.

A stateless web application

In a stateless web applications, you are not allowed to keep data between two http requests, so you have two ways to handle it:

Generate the user interface in a single shot

This is the approach which you can use when your account data is reduced. You embed all the necessary data from each account into your page and you generate the view, which you keep hidden and you show only when the user clicks. Please note that you can generate the HTML on the server side and with Javascript makes only certain part of your DOM visible, or just transfer a JSON representation of your accounts and use some kind of templating library to build the necessary UI directly on the client

Generate the user interface when required

This approach becomes necessary when the account data structure contains too many informations, and you don't want to transfer all this information for all the accounts on the client at first. For example, if you know the user is going to be interested in seeing the details only of very few accounts, you want to require the details only when the user asks for it.

For example, in your list of accounts you will have a button associated with each account, called details and you will use the account id to send a new request to the server.

@(accounts: models.domain.AccountList)
@titlebar = {<p>some html</p>}
@content = {
    @for(account <- accounts) {
        <p>@account.name <button class="details" href="@routes.Controllers.account(account.id)">details</button></p>
    }
}

Please note that you can also generate the user interface on the client side, but you will still need to retrieve it from the server the data structures when the user clicks on the button. This will ensure that the user retrieves the last available state of the account.


Old answer

If your goal is to reuse your views, Play views are nothing else then Scala classes, so you can import them:

@import packagename._

and then you use it in another template:

@for(account <- accounts) {
        @account(account)
    }

Upvotes: 4

Related Questions