scalaplayframeworkplayframework-2.1scala-template

How Scala functions work in Play framework 2.1


I am developing an application with play framework v 2.1 for the views I have to use Scala
I was trying to read through one of the sample code of play framework which can be found here http://www.playframework.com/documentation/2.0.x/Samples the Forms one
In the views folder of the app there is file views.contact package called form.scala.html

@(contactForm: Form[Contact])
@import helper._
@import helper.twitterBootstrap._

 @title = {
 Add a new contact <small><a href="@routes.Contacts.edit">Or edit an existing     contact</a></small>
 }

@phoneField(field: Field, className: String = "phone") = {
    @input(field, '_label -> "Phone numbers", '_class -> className) { (id, name, value, _) =>
    <input type="text" name="@name" value="@value"> 
    <a class="removePhone btn danger">Remove</a>
    }
}

@informationGroup(field: Field, className: String = "profile") = {
<div class="twipsies well @className">

    <a class="removeProfile btn danger pull-right">Remove this profile</a>

    @inputText(
        field("label"), 
        '_label -> "Label"
    )

    @inputText(
        field("email"), 
        '_label -> "Email"
    )

    <div class="phones">

        @repeat(field("phones"), min = 0) { phone =>

            @phoneField(phone("number"))

        }

        @**
         * Keep an hidden block that will be used as template for Javascript copy code
         **@
        @phoneField(
            field("phones[x].number"),
            className = "phone_template"
        )

        <div class="clearfix">
            <div class="input">
                <a class="addPhone btn success">Add a phone number</a>
            </div>
        </div>

    </div>

</div>
}

@main(title, nav = "contact") {

@if(contactForm.hasErrors) {
    <div class="alert-message error">
        <p><strong>Oops</strong> Please fix all errors</p>
    </div>
}

@helper.form(action = routes.Contacts.submit, 'id -> "form") {

    <fieldset>
        <legend>General informations</legend>

        @inputText(
            contactForm("firstname"), 
            '_label -> "First name"
        )

        @inputText(
            contactForm("lastname"), 
            '_label -> "Last name"
        )

        @inputText(
            contactForm("company"), 
            '_label -> "Company"
        )

    </fieldset>

    <fieldset>
        <legend>Profiles</legend>

        <div id="profiles">

            @repeat(contactForm("informations")) { information =>

                @informationGroup(information)

            }

            @**
             * Keep an hidden block that will be used as template for Javascript copy code
             **@
            @informationGroup(
                contactForm("informations[x]"),
                className = "profile_template"
            )

            <div class="manage">
                <a class="addProfile btn success">Add another profile</a>
            </div>

        </div>

    </fieldset>

    <div class="actions">
        <input type="submit" class="btn primary" value="Insert">
        <a href="@routes.Application.index" class="btn">Cancel</a>
    </div>

}

The code is supposed to render a view like this
enter image description here

By pressing the add phone number some filed are added to the form and it'll become like this :

enter image description here

what make me really confused about this code is these parts and how they work :

@phoneField(field: Field, className: String = "phone") = {
    @input(field, '_label -> "Phone numbers", '_class -> className) { (id, name, value,   _) =>
  <input type="text" name="@name" value="@value"> 
  <a class="removePhone btn danger">Remove</a>
  }

}

and

@repeat(field("phones"), min = 0) { phone =>

        @phoneField(phone("number"))

    }

    @**
     * Keep an hidden block that will be used as template for Javascript copy code
     **@
    @phoneField(
        field("phones[x].number"),
        className = "phone_template"
    )

Can someone please provide me a brief explanation about how these lines of codes work, and please don't put links to short tutorials on blogs or websites to Scala I can find them myself with a Google search

I am just looking for a short but descriptive explanation about these lines of codes, thanks in advance!!

btw I dropped out the javascript codes from original code


Solution

  • Let's start with the @phoneField function:

    @phoneField(field: Field, className: String = "phone") = {
        @input(field, '_label -> "Phone numbers", '_class -> className) { (id, name, value,   _) =>
           <input type="text" name="@name" value="@value"> 
           <a class="removePhone btn danger">Remove</a>
        }
    }
    

    @input is a helper (i.e. function) that allows you create html for the field yourself. This is needed in this context because we want to add .removePhone button. So @phoneField simply takes instance of Field and constructs html input and remove-link.

    Now, what about @repeat?

    @repeat(field("phones"), min = 0) { phone =>            
         @phoneField(phone)
    }
    

    In the app/controllers/Contacts.scala defined contactForm and there you can see that "phones" field defined as list(text). It is a sort of collection with elements that are text fields. So @repeat will iterate over field("phones") and pass each text field to the @phoneField. Important thing that all fields that will go to @phoneField will have names like "phones[0]", "phones1", ....

    Now things got interesting.

     @phoneField(
           field("phones[x]"),
           className = "phone_template"
     )
    

    constructs a template for the javascript function that will copy it's content to the page on response to "add phone field" button. Looks like field("phones[x]") constructs empty field with name "phones[x]" similar to what @repeat is generating. Then whole construct will create a phone field (and remove-link) with name "phones[x]" and empty value.

    When you'll look at the javascript code you will see that when user clicks "Add a phone number" link javascript callback will be executed that will copy the html from the template to the dom under <div class="phones">, and will renumber all .phone input which name matches /phones\[.+\]/

    I hope that you have read Using the form template helpers.