MapperBinder

What is the purpose of MapperBinder.scala

MapperBinder is a helper which should be used to reduce the size of your snippet code when you want to binding a mapper instance to your template manually.

The source code is placed at: https://github.com/pkufashuo400/yabe-lift/blob/master/src/main/scala/code/lib/MapperBinder.scala

Take the following “Incident” Mapper of a Risk Management System for example, you may have about 40 fields.


class Incident extends LongKeyedMapper[Incident] with IdPK {
  object incidentType extends ...
  object severityLevel extends ...
  object organization extends ...
  object secure extends ...
  object programm extends  ...
  object courseName extends ...
  object courseNumber extends ...
  object personStatus extends ...
  object age extends ..
  object gender extends ..
  object incidentEvent extends ..
  object incidentDateTime extends ..
  object startDate extends ..
  object totalDays extends ..
  object activeHours extends ..
  object dayOccurred extends ..
  object lostDay extends ..
  object leaveField extends ..
  object leaveDate extends ..
  object evacuationType extends ..
  object medicalVisit extends ..
  object medicalCare extends ..
  object returnField extends
  object returnDate extends ..
  object propertyDamage extends ..
  object activityType extends ..
  object narrative extends ..
  object assessment extends ..
  object analysis extends ..
  object comments extends ..
  object preparer extends ..
  object preparerPosition extends ..
  object submitDate extends ..
  object reviewer extends ..
  object reviewerPosition extends ..
  object reviewerComment extends ..
  object reviewDate extends ..
  object dateUpdated extends ..
}

If you want to bind the above mapper instance in your snippet, it would be a disaster. I think you do not want you snippet code looks like this:


def displayAIncident:CssSel = {
  val incident = SomeFuncToGetYouIncident()
  "#incidentType" #> .... &
  "#severityLevel" #> ... &
  "#organization" #> ... &
  "#secure" #> ... &
  "#programm" #> ...&
  "#courseName" #> ...&
  "#courseNumber" #> ... &
  "#personStatus" #> ... &
  "#age" #>  ... &
  "#gender" #> ...&
  "#incidentEvent" #>&
  "#incidentDateTime" #> ..&
  "#startDate" #> .. &
  "#totalDays" #> .. &
  "#activeHours" #> .. &
  "#dayOccurred" #> .. &
  "#lostDay" #> .. &
  "#leaveField" #> .. &
  "#leaveDate" #> .. &
  "#evacuationType" #> .. &
  "#medicalVisit" #> .. &
  "#medicalCare" #> .. &
  "#returnField" #> .. &
  ...
  ...
  ...
  ...
  ...
}

If you add a field to a Mapper class, you have to also add it in the edit and view template, and in the bind statements in each Snippet. A lot of boilerplate code, right?

Then, you may use MapperBinder to solve this problem.

To use MapperBinder, you need to added the MapperBinder.scala file to you “code.lib” package. Then use it like this:


import code.lib.MapperBinder._
def displayAIncident:CssSel = {
  val incident = SomeFuncToGetYouIncident
  ".APlaceToDisplay" #> {bindMapper(incident) _}
}

Of course, you can use CRUDify, toForm or asHtml of the mapper to solve the problem. But it is difficult to customize the display of the template. For example, it is difficult to customize which fields to display, which to hide. it is also difficult to display forms and content of the mapper at the same time.

Use MapperBinder, you can display you template as you like.

Usage:
1. User ‘@’ before the ‘fieldName’ to show the field. e.g @courseName will display the field courseName using its asHtml function.

2. User “mb:” before the ‘fieldName’ in a class attribute to show the form of the field. e.g <input class=“mb:courseName” />

Example:

<form class="lift:Incident.editTheNarrative?form=post">
    <div class=".APlaceToDisplay">
        <h1>@incidentType </h1>
        <a href="/display_incident/@id">@programm</a>
        <span>age: @age | gender: @gender </span>
        ... code to display other fields ...
        <textarea class="mb:narrative">The incident narrative content</textarea>
        <input type="submit">
        ...
    </div>
    <div id="#SomeThingOutOf_APlaceToDisplay1"></div>
    <span id="#SomeThingOutOf_APlaceToDisplay2"></span>
</form>

There is a condition that not all dynamic content is displayed from the mapper instance. So you may need to bind other things to the template. You can do it like this.


".APlaceToDisplay" #> {bindMapper(incident , {
  "a [class]" #> "oddOrEven" &
  "type=submit" #> SHtml.onSubmitUnit(processFunc)
}) _} & 
"#SomeThingOutOf_APlaceToDisplay1 *" #> ... &
"#SomeThingOutOf_APlaceToDisplay2" #> ... & ...

Now, MapperBinder is using ‘asHtml’ method of the field for display and use ‘toForm’ method for input. However, there might be a more complicated situation that we want to display the field in different format. For example, if we have a ‘Article’ mapper for a blog engine application.


class Article extends LongKeyedMapper[Article] with IdPK {
  object title ..
  object content ...
}

You want to show part of the article in your index page, and show the whole article in the detail page. In this case, only one ‘asHtml’ method is not enough. So MapperBinder provide you a solution:


class Article extends LongKeyedMapper[Article] with IdPK {
  object title ..
  object content extends MappedTextarea(this, 1000) {
      def short:Node = {
          this.get.length match {
             case l if l > 50 => Text(this.get.substring(50) + "...")
             case _ => Text(this.get)
         }
      }
  }
}

Then you can display only 50 characters of you article by using @content.short in you template.

Please note that , you can implement any display method as you like, but it must not have arguments and it must return ‘Node’ type. If you use @content, it will invoke ‘asHtml’ method by default.

For input functionality, I think only one ‘toForm’ method is enough. So MapperBinder does not allow users to provide an alternative method for ‘toForm’. But if you want, you can just change the source code of MapperBinder. it is quite easy.

If you want to see practicle usages of MapperBinder, please check the source code of yabe-lift at https://github.com/pkufashuo400/yabe-lift. MapperBinder is used in yabe-lift for displaying and managing Comments, Users and Posts.

Ok, that’s it. An alternative and maybe more robust solution is MBindHelper here: https://github.com/tromberg/Winglet/wiki/bindMapper. But I don’t like those tags in template, so I wrote MapperBinder.