Desktop version

Home arrow Computer Science arrow Building Applications with Scala

Source

Working with views(UI)

The Play framework works with a Scala-based templating engine called Twirl. Twirl was inspired by ASP.NET Razor. Twirl is compact and expressive; you will see we can do more with less. Twirl template files are simple text files, however, the Play framework compiles the templates and turns them into Scala classes. You can mix HTML with Scala smoothly in Twirl.

The UI will be compiled into a Scala class, that can and will be referenced at our controllers, because we can route to a view. The nice thing about it is that this makes our coding way safer, since we have the compiler checking for us. The bad news is that you need to compile your UI, otherwise, your controllers won't find it.

Previously in this chapter, we defined controllers for products, images, and reviews, and we wrote the following code:

Ok(views.html.product_details(None, productForm))

With the preceding code, we redirect the user to a blank page for products so that the user can create a new product. We also can pass parameters to the UI. Since it is all Scala code, you are actually just calling a function as follows:

val product = service.findById(id).get

Ok(views.html.product_details(Some(id), productForm.fill(product)))

In the preceding code, we call the service to retrieve a product by ID, and then pass the object to the UI with the form being filled.

Let's continue building our application and create the UI for the products, reviews, and images. Since we are doing a CRUD, we will need more than one template file per CRUD. We will need the following structure:

  • • Index Template
  • • List all items
  • • Link to edit one item
  • • Link to remove one item
  • • Link to create a new Item
  • • Detail Template
  • • HTML Form to create a new Item
  • • HTML form to edit an existing item(for update)

Having said that, we will have the following files:

  • • For Products:
  • • product_index.scala.html
  • • product_details.scala.html
  • • For Image:
  • • image_index.scala.html
  • • image_details.scala.html
  • • For Reviews:
  • • review _index. scala.ht ml
  • • review _details .scala.h tml

For the sake of code reuse, we will create another file containing the basic structure of our UI, like CSS imports (CSS needs to be located at

ReactiveWebStorepublicstylesheets), JavaScript imports, and page title so that we don't need to repeat that in all the templates for each CRUD. This page will be called: main.scala.html.

All the UI code should be located at ReactiveWebStore/app/views.

The main Scala with the UI index for all CRUD operations is in main.scala.html, as follows:

@(title: String)(content: Html)(implicit flash: Flash)

@title

href="@routes.Assets.at("stylesheets/bootstrap.min.css")">

src='@routes.Assets.at("images/rws.png")'>

@title

@alert(alertType: String) = {

@flash.get(alertType).map { message =>

@message

}

}

@alert("error")

@alert("success")

@content


In the preceding code, first of all, there is the following line at the very top:

@(title: String)(content: Html)(implicit flash: Flash)

This means that we define the parameters this UI can receive. Here we expect a string title, which will be the page title, and there are some currying variables as well. You can get more details about currying in Chapter 1, Introduction to FP, Reactive, and Scala. So in currying, there are two things: First is HTML, which means you can pass HTML code to this function, and second, we have Flash which the Play framework will pass for us. Flash is used to get parameters between requests.

As you can see, later in the code we have @title, which means we retrieve the title of the parameters, and add the value to the HTML. We also print any error message or any validations issues, if present, with @alert. We import JQuery and Twitter Bootstrap, but we do not put hard-coded paths. Instead, we use the routers like @routes.Assets.at. The Javascripts still need to be located at ReactiveWebStorepublicjavascripts.

Now other templates can work with @main(..), and they don't need any declaration of Javascript or CSS. They can add an extra HTML code, which will be rendered on the previous code by @content. So, for the products, the content will be HTML product content, and so on for reviews, and images. Now we can move for the products UI.

Product index: UI index for products product_index.scala.html

@(products:Seq[Product])(implicit flash: Flash)

@main("Products") {

@if(!products.isEmpty) {

@for(product <- products) {

}

Name Details Price

details(product.id.get)">@product.name

@product.details @product.price

"@routes.ProductController.remove(product.id.get)">

}

"btn btn-success">

Add Product

}

As you can see, there is HTML mixed with Scala code. Every time you need to run HTML, you just run it, and when you need run Scala code, you need use a special character, @. At the very top of the template, you can see the following code:

@(products:Seq[Product])(implicit flash: Flash)

Since this is Scala code in the end, and will be compiled, we need to define what parameters this UI template can receive. Here we expect a sequence of products. There is also a currying implicit variable called Flash, which will be provided by the Play framework, and we will use it for the message display. We also have the code-@main("Products") { ..

}. This means that we call the main Scala template and add extra HTML-the product HTML. For this product UI, we list all the products based on the sequence of products. As you can see, we define an HTML table. We also validate if the sequence is not empty before listing all the products.

Now we can go for the details page for products in product_details.scala.html as follows:

@(id: Option[Long],product:Form[Product])(implicit flash:Flash) @import play.api.i18n.Messages.Implicits._

@import play.api.Play.current

@main("Product: " + product("name").value.getOrElse("")){

@if(product.hasErrors) {

Sorry! Some information does not look right. Could you review it please and re-submit?

}

@helper.form(action = if (id.isDefined)

routes.ProductController.update(id.get) else routes.ProductController.insert)

{

@helper.inputText(product("name"), '_label -> "Product

Name")

@helper.inputText(product("details"), '_label -> "Product Details")

@helper.inputText(product("price"), '_label -> "Price")

}

}

For this preceding UI, at the very top, we have the following line:

@(id: Option[Long],product:Form[Product])(implicit flash:Flash)

This means that we expect an ID which is completely optional. This ID is used to know if we are dealing with an insert scenario or an update scenario, because we use the same UI for both insert and update. We also get the product form, which will be passed through ProductController, and we receive Flash, which will be provided by the Play framework.

We need to do some imports on the UI so that we can get access to the Play framework i18n support. This is done by the following:

@import play.api.i18n.Messages.Implicits._

@import play.api.Play.current

Like the previous UI, we render this UI within the main Scala UI. So we don't need to specify JavaScript and CSS again. This is done by the following code:

@main("Product: " + product("name").value.getOrElse("")){ .. }

Next we check if there are any validation errors. If there are, the users will need to fix the errors before moving on. This is done by the following code:

@if(product.hasErrors) {

Sorry! Some information does not look right. Could you review it please and re-submit?

}

Now is the time to create the product form, which, in the end, will be mapped to HTML input boxes. We do this with the following code:

@helper.form(action = if (id.isDefined) routes.ProductController.update(id.get) else routes.ProductController.insert) {

@helper.inputText(product("name"), '_label -> "Product Name")

@helper.inputText(product("details"), '_label -> "Product Details")

@helper.inputText(product("price"), '_label -> "Price")

}

@helper.form is a special helper provided by the Play framework to create HTML forms easily. So, the action is the target where the form will be submitted. We need to do an if here, since we need to know if it is an update or an insert. Then we map all the fields we have for our product model with this code:

@helper.inputText(product("name"), '_label -> "Product Name")

@helper.inputText(product("details"), '_label -> "Product Details")

@helper.inputText(product("price"), '_label -> "Price")

Remember, the product form comes from the product controller. For the helper, we just need to tell it which product field is for which HTML label, and that's it. This will produce the following UIs.

The following image shows the blank product index UI:

The insert UI form for product details looks as follows:

With products added, the product index UI appears as follows:

Now we can move to reviews. Let's go for the UIs.

The review index UI in review_index.scala.html is as follows:

@(reviews:Seq[Review])(implicit flash: Flash)

@main("Reviews") {

@if(!reviews.isEmpty) {

@for(review <- reviews) {

}

ProductId Author Comment

(review.id.get)">@review.productId

@review.author @review.comment

}

success">Add Review

}

So here we have the same things as we had for the products. Let's take a look at the details page for review now. You can find it in review_details.scala.html.

@(id: Option[Long],review:Form[Review],products: Seq[(String,String)])(implicit flash:Flash)

@import play.api.i18n.Messages.Implicits._

@import play.api.Play.current

@main("review: " + review("name").value.getOrElse("")){

@if(review.hasErrors) {

Sorry! Some information does not look right. Could you review it please and re-submit?

}

@helper.form(action = if (id.isDefined) routes.ReviewController.update(id.get) else routes.ReviewController.insert) {

@helper.select(

field = review("productId"), options = products,

'_label -> "Product Name",

'_default -> review("productId").value.getOrElse("Choose One"))

@helper.inputText(review("author"), '_label -> "Author")

@helper.inputText(review("comment"), '_label ->

"Comment")

}

}

Here, in this last code, we have almost everything similar to what we had for products, however, there is one big difference. Review needs to be associated with a product ID. That's why, we need to have a select for the products, which is fulfilled by products:Seq[(String,String)]. This comes from the ReviewController code. This code produces the following UIs. The blank review index UI is shown as follows:

The insert review details UI looks as follows:

The review index UI with reviews will look like the following image:

Now we can move to the last one: the image UI. The image UI is very similar to the review UI, because it depends on the product ID too. Let's go for it. The image index UI has the following code in image_index.scala.html:

@(images:Seq[Image])(implicit flash:Flash)

@main("Images") {

@if(!images.isEmpty) {

@for(image <- images) {

}

ProductID URL
(image.id.get)">@image.id @image.productId @image.url

"@routes.ImageController.remove(image.id.get)">

}

"btn btn-success">

Add Image

}

Image Details UI [image_details.scala.html]

@(id: Option[Long],image:Form[Image],products:Seq[(String,String)]) (implicit flash:Flash)

@import play.api.i18n.Messages.Implicits._

@import play.api.Play.current

@main("Image: " + image("productId").value.getOrElse("")){ @if(image.hasErrors) {

Sorry! Some information does not look right. Could you image it please and re-submit?

}

@helper.form(action = if (id.isDefined) routes.ImageController.update(id.get) else routes.ImageController.insert) {

@helper.select(field = image("productId"), options = products,

'_label -> "Product Name",

'_default -> image("productId").value.getOrElse("Choose One")

)

@helper.inputText(

image("url"),

'_label -> "URL",

'_placeholder -> "/assets/images/default_product.png", 'onchange -> "javascript:loadImage();"

)

Visualization

}

}

This UI template will create the following HTML Pages: The blank image index UI is shown in the following image:

The insert UI for image details looks as follows:

The following is the image index UI with items:

Now we have a complete working UI application. There are controllers, models, views, and simple services as well. We also have all validations in place.

Summary

In this chapter, you learned how to create controllers, models, services, views (using Twirl templating), Guice injections, and routing. We covered the principles of Scala Web Development using the Play framework. By the end of the chapter, we got the application with the Play framework up and running.

In the next chapter, we will learn more about services. As you may realize, we did some simple services in this chapter for products, reviews, and images, but now we will continue working with services.

 
Source
Found a mistake? Please highlight the word and press Shift + Enter  
< Prev   CONTENTS   Next >

Related topics