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:

Navigation bar
File views  Structure.scala 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:

PageTitleRow component
Figure 1. PageHeaderRow in action, shown here with title, sub-title, and three buttons as the optional extra HTML.

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).

Messages
Figure 2. Messages can have a title, a sub-title, a color, and a timeout.

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

blockUI
Figure 3. Screen blocking
yes no
Figure 4. Yes/No question
bkg
Figure 5. Background progress
To view and experiment (live!) with the Informationals library, start PSPS and navigate to the Components  Informationals 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)}
  1. will result in:

pager

views.Helpers

This class contains functions that handle many common use-cases, such as date formatting, and conditional rendering. This is also a good place to put additional useful methods (we’re happy to take pull requests!).