Reputation: 2951
I have one question about design
and basic implementation
of server side data grid control(something like asp.net GridView).
I have created dummy DataGrid.scala.html
but I can be written in more sophisticated and functional (with functional programming) way.
@(ID: String, List: Seq[model.admin.Partner.Partner], Columns: Seq[String])(content: Html= Html(""))
@import model.admin.Partner.Partner
<table>
<thead>
<tr>
@for(j <- Columns) {
<td>@j</td>
}
</tr>
</thead>
@for(i <- List) {
<tr> @for(j <- Columns) {
<td> @code.classes.EntityFactory.getValue[Partner](i, j)</td>
}
</tr>
}
</table>
Basically I have one loop for table headers and second double loop for getting value by Reflection.
object EntityFactory {
def getValue[T](entity: T,fieldName: String)(implicit ev: scala.reflect.ClassTag[T]) = {
entity.getClass.getMethods.find(_.getName == fieldName).get.invoke(entity)
}
}
In my view I use this Datagrid like this:
@DataGrid(ID = "partners", List = Partners, Columns = Seq("ID","Name", "ShortName")){
}
What I wish to have
I want have something like this:
@DataGrid(ID = "partners", List = Partners){
@LinkColumn(ID = "url", Propety = "ID",Url = controllers.admin.routes.PartnerController.read(""))
@LabelColumn(ID = "Name", Property="Name", CssClass="Some")
@LabelColumn(ID = "Email",Property="Email", CssClass="Some")
@DateDateColumn(ID = "ActiveFrom")
}
Here I created structure for this
In some columns I would like to do some trick to inject into Url
route value from Property
. Something like that:
@(ID: String,entity: Partner, Property: String, Url: String)
@import model.admin.Partner.Partner
<div>
here I want to inject code.classes.EntityFactory.getValue[Partner](entity,"ID")
into @Url
</div>
<a href="@Url(code.classes.EntityFactory.getValue[Partner](entity,Property))">klik</a>
Thanks for reading and help.
After some time I finally found answer by myselve. This code is divided by two areas: code
and view
.
Code
Here are traits or classes which describe types of column.
trait Column {
val ID: String
val Label: Option[String]
val Header: String
val Property: String
val AlternateProperty: Option[String]
val Visible: Boolean
val Enabled: Boolean
}
object Column {}
case class LinkColumn(ID: String,
Label: Option[String] = None,
Header: String,
Property: String,
AlternateProperty: Option[String] = None,
Visible: Boolean = true,
Enabled: Boolean = true,
Url: Any => Call,
Type: String = LinkColumnType.Action
) extends Column {}
case class LabelColumn(ID: String,
Label: Option[String] = None,
Header: String,
Property: String,
AlternateProperty: Option[String] = None,
Visible: Boolean = true,
Enabled: Boolean = true) extends Column {}
case class MergedActionColumn(ID: String,
Label: Option[String] = None,
Header: String,
Property: String,
AlternateProperty: Option[String] = None,
Visible: Boolean = true,
Enabled: Boolean = true,
Actions: Seq[LinkColumn]) extends Column {}
object LinkColumnType extends Enumeration {
type Type = Value
val Link = ""
val Action = "btn btn-success"
}
And some helpers code.
object EntityFactory {
def getField(entity: Any,fieldName: String) = {
entity.getClass.getMethods.find(_.getName == fieldName).get.invoke(entity)
}
}
Views
DataGrid.scala.html
@(ID: String, List: Seq[Any], CssClass: String = "",
ShowCreateButton: Boolean = true)(Columns: Seq[Any])(implicit request: play.api.mvc.RequestHeader, lang: play.api.i18n.Lang)
@import code.classes.{EntityFactory => ef}
@import code.views.tags.DataGrid.{LabelColumn, LinkColumn, Column, MergedActionColumn}
@import views.html.tags.controls.DataGrid.{LabelColumn => vLabelColumn, LinkColumn => vLinkColumn}
@import code.views.tags.DataGrid.LinkColumnType
@import code.Ext
@if(ShowCreateButton){
<a class="btn btn-success" href="@controllers.admin.routes.PartnerController.create()">
<i class="icon-zoom-in icon-white"></i>
@Ext.te("common.add", lang.code)
</a>
}
<table class="@CssClass">
<thead>
<tr>
@for(j <- Columns) {
@j match {
case s: MergedActionColumn =>{
<td>@s.Header</td>
}
case s: Column => {
<td>@s.Header</td>
}
case _ => {}
}
}
</tr>
</thead>
@for(l <- List) {
<tr>
@for(c <- Columns) {
@c match {
case s: LabelColumn => {
@vLabelColumn(ID = s.ID, Header = s.Header, Property = s.Property,
Visible = s.Visible)(entity = l)
}
case s: LinkColumn => {
@vLinkColumn(ID = s.ID, Label = s.Label, Header = s.Header, Property = s.Property,
AlternateProperty = s.AlternateProperty, Visible = s.Visible, Enabled = s.Enabled,
Url = s.Url, Type = LinkColumnType.Link
)(entity = l)
}
case s: MergedActionColumn => {
<td> <table> <tr>
@for(s <- s.Actions) {
@vLinkColumn(ID = s.ID, Label = s.Label, Header = s.Header, Property = s.Property,
AlternateProperty = s.AlternateProperty, Visible = s.Visible, Enabled = s.Enabled,
Url = s.Url, Type = LinkColumnType.Action, CssClass = "no-border no-bg"
)(entity = l)
}
</tr>
</table>
</td>
}
}
}
</tr>
}
</table>
LinkColumn.scala.html
@( ID: String,
Label: Option[String] = None,
Header: String,
Property: String,
AlternateProperty: Option[String] = None,
Visible: Boolean = true,
Enabled: Boolean = true,
Url: String => Call,
Type: String = code.views.tags.DataGrid.LinkColumnType.Action,
CssClass: String = "")(implicit entity: Any)
@import code.classes.{EntityFactory => ef}
<td class="@CssClass">
<a href="@Url(ef.getField(entity, Property).toString)" class="@Type">
@Label.getOrElse(ef.getField(entity, AlternateProperty getOrElse Property).toString)
</a>
</td>
LabelColumn.scala.html
@(ID: String,
Label: Option[String] = None,
Header: String,
Property: String,
AlternateProperty: Option[String] = None,
Visible: Boolean = true,
Enabled: Boolean = true)(implicit entity: Any)
@import code.classes.{EntityFactory => ef}
<td>
@ef.getField(entity, Property)
</td>
Usage of datagrid:
@DataGrid(ID = "facilities", List = Facilities, CssClass = "table table-striped table-bordered bootstrap-datatable datatable",
ShowCreateButton = true)(
Seq(
LabelColumn(ID = "ID", Header = "ID", Property = "ID"),
LabelColumn(ID = "Name", Header = "Nazwa", Property = "Name"),
MergedActionColumn(ID = "Actions", Header = "Actions", Property = "ID", Actions = Seq(
LinkColumn(ID = "Update", Header = "ID", Property = "ID", Url = pfc.update, Label = Some("Update")),
LinkColumn(ID = "Delete", Header = "ID", Property = "ID", Url = pc.delete, Label = Some("Delete")),
LinkColumn(ID = "Delete", Header = "ID", Property = "ID", Url = pc.read, Label = Some("View")),
LinkColumn(ID = "Delete", Header = "ID", Property = "ID", Url = pfc.index, Label = Some("Facilities"))
))
)
)
Of course this is only base scaldfolding but it shows how to use playframework views like controls in asp.net or jsf.
Upvotes: 2
Views: 1042
Reputation: 1722
You may want to check existing form helpers and also (outdated) bootstrap support. Check corresponding html.scala
files in play sources
It is good example of implementation non trivial components in play.
Upvotes: 0