Views
PSPS users Bootstrap 4 for its page templates. The views
package contains many facilities for supporting common HTML use-cases — most of them are detailed below.
Page Templates
bsBase.scala.html
-
Base template. Includes Bootstrap, the front-end Javascript router, CSRF token, and common Javascript files.
bsNavbar.scala.html
-
Extends
bsBase
with a nav-bar. frontEndBase.scala.html
-
Extends
bsNavBar
with the nav data for the public section. backEndBase.scala.html
-
Extends
bsNavBar
with the nav data for the back-office section. Also includes the back-end Javascript router.
Bootstrap 4 uses JQuery, and thus PSPS views include it as well. This dependency will be removed when Bootstrap 5 is released. |
Type-safe Navigation Menus
Using pre-defined tree structure, developers can describe the site navigation structure. The UI templates will draw the structure, including drop-down items etc. The structure can be static or dynamic (e.g. composed based on user permissions).
For example, the following code:
object Structure {
val publicItems:Seq[TopSiteSection[PublicSections.Value]] = Seq(
PageSection("navbar.publicHome", PublicSections.Home, routes.HomeCtrl.index),
PageSection("navbar.login", PublicSections.Login, routes.UserCtrl.showLogin),
MultiPageSection("navbar.components", PublicSections.Components,
Seq(
PageSectionItem("pageTitleRow.title", routes.HomeCtrl.pageTitleRow()),
PageSectionItem("pager.title", routes.HomeCtrl.pager(1)),
PageSectionItem("informationals.title", routes.HomeCtrl.informationals),
PageSectionItem("styledInputs.title", routes.HomeCtrl.styledInputs),
JsSectionItem("jsSectionItem.title", Html("swal('This can be any JS code')"))
)
),
MultiPageSection("Other", PublicSections.Others,
Seq(
PageSectionItem("navbar.login", routes.UserCtrl.showLogin),
SeparatorSectionItem,
PageSectionItem("navbar.publicHome", routes.HomeCtrl.index)
)
)
)
...
}
Results in:

File | contains the classes and structures of the public and back-office site areas.
Page Title Row
It is very common to have a title on a page, sometimes with a subtitle, sometimes with another component (e.g. page-wide action buttons), and sometimes with both. These titles involve some boilerplate, e.g. <div class="row"><div class="col"><h1>TITLE GOES HERE</…
.
Because we’re not happy to repeat ourselves (we are NOT happy to repeat ourselves, I say!), we wrapped is in the component comps.pageTitleRow
. So this code:
@comps.pageTitleRow("My Fancy Page", "In this page, we enjoy subtitles as well as titles"){
<button class="btn btn-primary">Add</button>
<button class="btn btn-warning">Remove</button>
<button class="btn btn-info">More Info</button>
}
Results in this page header:

Informationals
This is an infrastructure for UI messages (typically success/fail/info), that can be generated both from a controller, or from Javascript code on the client side. When created from a controller, they are passed over the flash
scope, with the key FlashKeys.MESSAGE
(this covers the common POST
-then-REDIRECT
pattern).

Additional methods allow blocking the UI (e.g. for full page data load), showing background progress, and prompting the user with YES/NO questions.



To view and experiment (live!) with the Informationals library, start PSPS and navigate to the | page.
Pagers
Having to page through a long list of data is a very common task. PSPS handles this with the views.PaginationInfo
case class, and the @comps.pager
partial template. For example, when pi
is a PaginationInfo
instance with currentPage
=3 and pageCount
=23, the following code:
@comps.pager(pi){i=>@routes.HomeCtrl.pager(i)}
-
will result in:
