Play 2.0 with Scala and Scaml, Part 3: The web stuff

First of all I’d like to reference to an excellent series of posts I found that cover similar stuff. It can be found here and covers a lot more topics than I did here.
In this part I’m going to show how to actually create the little blogging app I intend to write. I canceled my original intention of using LESS when I found the wonderful Bootstrap framework made by the Twitter guys. My intention was integrating technologies in Play 2 I’d like to have at hand when developing a web application, but LESS is integral part of it anyway, so I decided to play a little with Bootstrap. It’s amazingly easy to create at least considerably good looking pages, you’ll see.
If you have the need to use LESS, what makes definitly sense for bigger projects, you can do that as well as described here.

Creating some HTML

First thing I’ve done was creating some basic webpage to show the content initiated in the Global object. The nicely concise Scaml snippet could look like this:

-@ val posts: List[models.helwich.Post]
!!!5
%html
    %title My kewl blogging application
    %body
        -for(post <- posts)
            %div
                %h2 post.title
                %textarea(readonly="readonly") post.content

Together with an Action for the index of the page as shown in the Snippet below, the content is shown correctly as plain HTML.

def index = Action {
    Ok( Scalate("main-blog.scaml").render('posts -> Post.findAll()))
}

But plain HTML is not the design we expect for a modern web application. To get some sexyness regarding design and layout we must add some CSS. As I’ve mentioned in the beginning I found Bootstrap to be the perfect solution to create a good looking website not being a web-designer. To make it happen just download the Bootstrap framework here and copy the CSS files in your /public/stylesheets folder of your app.
To get a consistent look in the whole application, you should define a default.scaml in /views/layouts. This is the Scalate way and you can use this feature exactly as described in the very good documentation. I’ve put all the CSS, JavaScript loading as well as the html, title and the body tag in the default.scaml.
To apply the styles on our page you must add some classes and ids on the HTML elements. The resulting Scamls look like this.

default.scaml:

-@ var body:String
-@ var title: String = "The new Blog"

!!!5
%link(rel="stylesheet" type="text/css" href="/assets/stylesheets/bootstrap.min.css")
:css
    body {
        padding-top: 50px;
        padding-bottom: 40px;
    }
%link(rel="stylesheet" type="text/css" href="/assets/stylesheets/bootstrap-responsive.min.css")

%html
    %title #{title}
    %body
        -unescape(body)

-#load js at the end to have the page displayed first thus quicker
%script(type="text/javascript" src="assets/javascripts/jquery-1.7.1.min.js")

I haven’t found a way to get the Play convenience methods for accessing Assets working in the Scamls. If anyone knows how to do this, please let me know.

main-blog.scaml:

%div.navbar.navbar-fixed-top
    %div.navbar-inner
        %div.container
            %a.brand Here comes the Blog
%div.container#blogarea
    -for(post <- posts)
        %div.row.well
            %h2 post.title
            %textarea.input-block-level(readonly="readonly") post.content

In Scaml/Haml, CSS as well as in CoffeeScript (as you’ll see later) the . is used to define classes and the # is used to define IDs. This consistent syntax in all web related languages is really nice, I think. The used classes here like .navbar.navbar-fixed-top, input-block-level, row, well and so forth are references to the styles defined by Bootstrap.
Here I defined the standard layout with a fixed navigation bar at the top, where only a short header “Here comes the blog” is shown. Below that the blogs are shown in rows where the content is in a read-only textarea with the applied style input-block-level.

Creating some AJAX magic

My next step was to get some AJAX magic happen. The good thing is that Play 2.0 comes fully equipped with jQuery and CoffeeScript. If you’re experienced with these frameworks you can start coding immediately. I wasn’t, so I wanted to start slowly and in small steps. The first task easy enough for was “load the content in the background via an GET request using jQuery”.

main.coffee

$.get "/listposts", (data) ->
    $.each data, (index, post) ->
      $("#blogarea").append (
          "<div class=\"row well\">" +
            "<h2>" + post.title + "</h2>" +
            "<textarea readonly=\"readonly\" class=\"input-block-level\">" + post.content + "</textarea>" +
          "</div>"
      )

This CoffeeScript file sends a get request /listposts and adds for each returned line a html-snippet to the first element with the ID blogarea. If you want to use it like this, you have to delete the lines in the main-blog.scaml where the blogs are added of course.
Furthermore there are two things to do. First add a correspoding route to the routes file of the application like this:

GET     /listposts                  controllers.Application.listPosts

Second add the listPosts method to the Application object. Its important to add generate .. as(JSON) here as this JSON is what jQuery understands.

  def listPosts = Action {
    Ok( generate(Post.findAll())).as(JSON)
  }

Thats it. But just loading data in the background for this kind of page doesn’t really make sense, does it?

Adding posts via AJAX

But if posts could be added it would be really nice to avoid the complete page reload. To accomplish there are three things to do:

  • add a form for to enter title and content
  • create a post request to send data from the client to the server
  • clear the old posts and reload the actual data from the server

To create a small example of how to modularize Scamls, I planed creating a form that could be reused somewhere else. Scalate offers different ways of structuring and reusing code. The most simple and straight forward is the render mechanism. You can create a file with some Scaml code and use -render(“yourfilename.scaml”) anywhere in a Scaml to render that file there.
In the given example this looks like this.

enter-blog-form.scaml:

%div.row.well
    %form#blog-form(name="blog" action="")
        %label(for="title") Title
        %input.input-block-level(type="text" name="title")
        %label(for="content") Content
        %textarea.input-block-level(name="content")
        %input#reset.hide(type="reset")
        %button#button.btn Send

And the main.scaml changes to:

%div.navbar.navbar-fixed-top
    %div.navbar-inner
        %div.container
            %a.brand Here comes the Blog
%div.container
    -render("enter-blog-form.scaml")
%div.container#blogarea

Hiding a button like I used to know it didn’t work here. That is why the reset button has no attribute hidden but uses the hide class of Bootstrap instead.
In some examples I found there were inputs used for the buttons. When the .btn class of Bootstrap is applied to a html element more or less everything will look like a button. I use the button tag for the send button here anyway. This has the advantage that we can use the tab key to get the focus on it.
There is not action defined for this form. This is because the post will be performed in the background via AJAX like this:

main.coffee:

readAllPosts =  ->
  $("#blogarea").empty()
  $.get "/listposts", (data) ->
    $.each data, (index, post) ->
      $("#blogarea").append (
          "<div class=\"row well\">" +
            "<h2>" + post.title + "</h2>" +
            "<textarea readonly=\"readonly\" class=\"input-block-level\">" + post.content + "</textarea>" +
          "</div>"
      )

$(document).ready ->
  readAllPosts.apply()

  $('#button').click ->
    dataString = $("#blog-form").serialize()
    $.ajax(
      type: 'POST'
      url: '/submit-new-blog'
      data: dataString
      success: ->
        $("#reset").click()
        readAllPosts.apply()
      error: (jqXHR, textStatus, errorThrown) ->
        alert(errorThrown)
    )
    false

So, what happens here? First thing to mention is the clearing of the blog data in the first function readAllPosts. This is needed as we want to reload all posts when a new one is added. Otherwise not only the new post would be added, but the complete set of posts would be added to the already shown ones with ugly duplications.
Then there is the $(document).ready stuff. This is the jQuery place for functions that shall be activated on certain events on objects of the document. I also added the initialization here – loading all posts from the server. After that I used jQuery to define a function for the element of the document with the ID button. If the button is clicked the function will be executed.
This jQuery function serializes the content of the form and makes an AJAX post to /submit-new-blog delivering the dataString to the server. In case of a success, the form is reseted and the posts are reloaded by executing the readAllPosts function. In case of an error it shows it to you. Quite important is to return false here. Otherwise the form will be submitted normally, thus there will be a normal request with the content of the form in the query. Very ugly.

Conclusion
This is it. We have a small application with some AJAX magic, with amazingly few lines of code, but very clear in its intention. No hidden side effects as you tend to have ’em in most component based frameworks. I am very enthused about Play 2 in conjunction with Anorm, Scaml and CoffeeScript. It is very promising regarding concisness, maintainability, development speed and quality of the solutions. I hope beeing able to develop some real worl stuff soon.

You can get the complete source code here. For more stuff on Play 2 and related technologies I’d like to hint to the series of blogs of Matt Raible again.

Advertisements

4 thoughts on “Play 2.0 with Scala and Scaml, Part 3: The web stuff

  1. “I haven’t found a way to get the Play convenience methods for accessing Assets working in the Scamls. If anyone knows how to do this, please let me know.”

    This is pretty simple. You just need to import controllers._ and then you can use routes.Assets.at(“javascripts/whatever.js”)

    For example (with Jade syntax):

    link(rel=”stylesheet” type=”text/css” href={routes.Assets.at(“stylesheets/libs/bootstrap.min.css”)})

    However, the following works too and seems a bit shorter:

    link(rel=”stylesheet” type=”text/css” href={uri(“/assets/stylesheets/libs/bootstrap.min.css”)})

    Thanks for your tips in getting Scalate working with Play 2. It’s helped me immensely!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s