i'm using an AjaxSelect which contains several ids. By selecting an id the additional information to this id should be displayed in a given table which will be generated by a snippet. Now i want to know which would be the best solution to refresh my list?
HTML:
<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr class="lift:MainScreen.cars">
<td><car:name /></td>
<td><car:type /></td>
</tr>
</tbody>
</table>
SCALA:
def doSelect(msg: NodeSeq) = {
SHtml.ajaxSelect(cars.map(i => (i.no.toString, i.no.toString + ". Car")),
Empty, {
selectedCar =>
controller.chooseCar(selectedCar.toInt)
// RELOAD TABLE
})
}
def cars(node: NodeSeq): NodeSeq = {
val cars = controller.chosenCarFamily.cars
cars match {
case null => Text("There is no items in db")
case game => game.flatMap(i =>
bind("car", node,
"name" -> car.name,
"type" -> car.type))
}
}
You should use ValueCell and WiringUI. Very good examples can be found at simple_wiring and invoice_wiring.
When using WiringUI, each time the valueCell cell is updated, the content linked with WiringUI.apply(cell) will be updated. So it should do the trick.
Here is an example for your specific case:
HTML:
Same as yours
SCALA:
class MainScreen{
def doSelect(msg: NodeSeq) // same as yours
def cars = WiringUI.apply(controller.chosenCarFamily)(displayResult)
def displayResult(carFamily:CarFamily)(node: NodeSeq) ={
carFamily.cars match {
case null => Text("There is no items in db")
case game => game.flatMap(i =>
bind("car", node,
"name" -> i.name,
"type" -> i.type))
}
}
}
object Controller{
val selectedCar = ValueCell(1)
def chooseCar = sectectedCar.set
val chosenCarFamily = selectedCar.lift(car:Int => //Stuff to output the family)
}
Related
Question is - how should I take data from this kind of inputs and pass it properly to DB (by SORM framework)?
I try to pass data to two tables in my DB, the result of my work is that while the values are properly inserted to the first table (Author), second one (Book) remains untouched.
Probably my problem is that I am not propely naming nested, repeated inputs on my page, but i cant find proper way to do this (I'm fairly new to Scala, so I probably lack of experience on this).
Ok(Json.toJson(author))
in addData in Application.scala shows me
{"name":"what","books":[]}
so I think, that the problem is in binding data from request.
I tried to follow examples here: How to model an entity with many children in Sorm? and here: https://www.playframework.com/documentation/2.1.1/ScalaForms, by operating on the template "play-scala" from Play framework, so i've got code like this:
Models are:
case class Author(name: String, books: Seq[Book]) {
}
object Author {
implicit val authorFormat = Json.format[Author]
}
case class Book(title: String ) {
}
object Book {
implicit val bookFormat = Json.format[Book]
}
case class AuthorBook(name: Author, title: Book) {
}
The scala.index.html
<table>
<tr>
<td>
On the left Author, on the right Book
<td>
</tr>
<tr>
<td>
<ul id="authors"></ul>
</td>
<td>
<ul id="books"></ul>
</td>
</tr>
</table>
<table>
<form action="#routes.Application.addData()" method ="post">
<tr>
<td>Author: <input name="author" type="text">
</td>
<td>Books: <input name="books.title[0]" type="text"><br><input name="books.title[1]" type="text">
</td>
</tr>
<tr>
<td>
<button>Submit</button>
</td>
</tr>
</form>
</table>
And Application.scala
class Application extends Controller {
def index = Action {
Ok(views.html.index("E-Library"))
}
val authorForm: Form[Author] = Form {
mapping(
"author" -> text,
"books" -> seq(
mapping(
"title" -> text)(Book.apply)(Book.unapply)
)
)(Author.apply)(Author.unapply)
}
def error = Action {
Ok("error")
}
def addData = Action { implicit request =>
authorForm.bindFromRequest.fold(
formWithErrors => {
BadRequest("Bad request!")
},
authorF => {
val author = DB.save(authorF)
Ok(Json.toJson(author))
//Redirect(routes.Application.index())
}
)
}
def getAuthor = Action {
val dataAuthor = DB.query[Author].fetch
Ok(Json.toJson(dataAuthor))
}
def getBook = Action {
val dataBook = DB.query[Book].fetch
Ok(Json.toJson(dataBook))
}
def getData = Action {
Redirect(routes.Application.index())
}
}
Found it!
As mentioned here: Nested form in Play! Scala 2.2
I needed to rename input names in my form from
book.title[0]
to
book[0].title.
In my Play application I've a configuration like this:
social {
twitter {
url="https://twitter.com"
logo="images/twitter.png"
}
facebook {
url="https://www.facebook.com"
logo="images/facebook.png"
}
}
Ho do I iterate over all the social entries to get url and logo for each entry?
<table border="0" cellspacing="0" cellpadding="2"><tr>
#configuration.getConfig("social").map { config =>
#for(item <- config.entrySet) {
<td><a href="item.getString("url")">
<img src="#routes.Assets.at("item.getString("logo")").absoluteURL()" width="24" height="24"/></a></td>
}
}
</table>
Of course, item.getString in the snippet here above does not work... it just shows what I'm trying to achieve.
The final objective would be to be able to add any further social url without having to modify the page template.
If you change the config to:
"social" : [
{
name="twitter",
url="https://twitter.com",
logo="images/twitter.png"
},
{
name="facebook",
url="https://www.facebook.com",
logo="images/facebook.png"
}
]
You could do it like this:
#(message: String)(implicit request: RequestHeader)
#import play.api.Play.current
<table border="0" cellspacing="0" cellpadding="2"><tr>
#current.configuration.getConfigList("social").get.map { config =>
<td><a href="#config.getString("url")">
<img src="#routes.Assets.at(config.getString("logo").get).absoluteURL()" width="24" height="24"/></a></td>
}
</table>
For posterity, here's another way to iterate over a nested config like you had. I prefer that format to the array one and I'd rather make my config cleaner than the code.
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
socialConfig.root.map { case (name: String, configObject: ConfigObject) =>
val config = configObject.toConfig
println(config.getString("url"))
println(config.getString("logo"))
}
I'm sure the OP could convert this into a Twirl template. That's about as clean as I can get it.
In case you're using Java, for a config like:
"social" : [
{
name="twitter",
url="https://twitter.com",
logo="images/twitter.png"
},
{
name="facebook",
url="https://www.facebook.com",
logo="images/facebook.png"
}
]
this will work:
ConfigList socials = ConfigFactory().load.getList("social")
for (ConfigValue cv : socials) {
Config c = ((ConfigObject)cv).toConfig();
System.out.println(c.getString("url"));
System.out.println(c.getString("logo"));
}
socialConfig.root.map does not work.
Here is my solution -
private val firstSegmentRE = """^(\w+)[\.*].*$""".r
// convert "aaa.bbb.ccc" to "aaa"
private def parseFirstSegment(fullPath: String) : Option[String] = {
if (fullPath.contains("."))
fullPath match {
case firstSegmentRE(segment) => Some(segment)
case _ => None
}
else
Some(fullPath)
}
// for all keys in white list get a map of key -> config
protected def subConfigMap(config: Config, whiteList: List[String],
configName: String) : ErrorOr[Map[String, Config]] = {
// This will traverse the whole config and flatten down to the leaves..
val leafKeys : List[String] =
config.entrySet()
.asScala
.map(e => e.getKey)
.toList
// Remove all after the first dot
val nextLevelKeys : List[String] =
leafKeys.map(parseFirstSegment)
.collect {
case Some(firstSegment) => firstSegment
}
.distinct
val keysToSearch = nextLevelKeys.filter(whiteList.contains)
// we have a list of valid first level children
// parse out subconfigs and convert to map
keysToSearch.traverseErrorOr( key =>
extractSubConfig(config, key, configName).map((key, _))
)
.map(_.toMap)
}
Where extractSubConfig is a method which produces an ERROR / Config (a scalaz disjunction) and traverseErrorOr is a method to traverse a list and either process all the elements or fail and return failed disjunction if failed at any point. This method can be done without scalaz, just posting an answer to help people if they want.
Since collection.JavaConversions has since been deprecated in favor of collection.JavaConverters (and this question is the top result on a search for how to iterate through a Typesafe Config in Scala), I wanted to suggest a more modern version of Cole's great answer:
import collection.JavaConverters._
val socialConfig = ConfigFactory.load.getConfig("social")
for ( (name: String, configObject: ConfigObject) <- socialConfig.root.asScala) {
println(name) // prints "twitter" or "facebook"
val config = configObject.toConfig
println(config.getString("url"))
println(config.getString("logo"))
}
To be clear, socialConfig.root.asScala yields a standard Scala Map[String, ConfigObject] and you can iterate over it however you'd like.
import collection.JavaConversions._
val socialConfig = ConfigFactory.load.getConfig("social")
val socialConfigMap = socialConfig
.root()
.entrySet()
.asScala
.map(socialEntry => {
println(socialEntry.getKey)
val socialEntryConfig = socialEntry.getValue.asInstanceOf[ConfigObject].toConfig
println(socialEntryConfig.getString("url"))
println(socialEntryConfig.getString("logo"))
})
For the config mentioned originally in question:
social {
twitter {
url="https://twitter.com"
logo="images/twitter.png"
}
facebook {
url="https://www.facebook.com"
logo="images/facebook.png"
}
}
A solution in Java is:
val socialConfig = ConfigFactory.load.getConfig("social");
ConfigList socials = socialConfig.getList("social");
Map<String, Object> map = socials.root().unwrapped();
for (Map.Entry<String, Object> cv : map.entrySet()) {
Map<String, String> c = (Map<String, String>)cv.getValue();
System.out.println(cv.getKey());
System.out.println(c.getOrDefault("url", ""));
System.out.println(c.getOrDefault("logo", ""));
}
I have a question for which I couldn't find an answer anywhere in the web. I have a lift-based web application written on Scala. There is a form in my application which is driven by a snippet. I use Mapper for my models, and I use override def validations ... to define model's validation parameters. The template code looks like this:
<form class="lift:GroupManager.addGroup?form=POST&action=add">
<div class="row collapse" style="width: 30em">
<div class="small-8 columns">
<span class="group-name prefix radius"></span>
</div>
<div class="small-4 columns">
<span class="submit button small postfix radius">Add New Group</span>
</div>
</div>
</span>
</form>
The snippet code:
def addGroup = {
object groupName extends RequestVar[String]("")
def doAddGroup() = {
val group = Group.create.createdAt(new java.util.Date).groupName(groupName.is)
group.validate match {
case Nil => {
group.save
S.notice("Group '" + group.groupName.is + "' has been added successfully")
}
case errors:List[FieldError] => {
S.error(errors)
}
}
}
".group-name" #> SHtml.text(groupName.is, groupName(_), "id" -> "group-name") &
".submit" #> SHtml.submit("Add group", doAddGroup)
}
The model's validations override:
object groupName extends MappedString(this, 700) {
override def dbColumnName = "group_name"
override def dbNotNull_? = true
override def validations = valMinLen(1, S.?("Group name should not be empty!")) _ ::
valMaxLen(700, S.?("Group name should not be more than 700 characters long!")) _ ::
valUnique(S.?("Group name should be unique!")) _ ::
super.validations
}
Now everything is perfect except the fact that when invalid data is provided, S.error is used to inform user about problems. What I want to do is to highlight the form element (by applying class="error") in which the data is invalid and add a <span> element with the error message in front of the form element. Is there a simple way to do that with Lift framework? Or I should just traverse through errors:List[FieldError] and fetch field information from that array, which does not seem too elegant to me?
Play framework if else case is not working here
If userprofile.useraccountid, useraccount.id have the same value then not views that id user on view page
my code in the view..
#(userprofiles: List[UserProfile],myFriend:models.MyFriend,userprofile:models.UserProfile,useraccount:models.UserAccount)
#helper.form(action = routes.Application.createMyFriend) {
<br/><br/><br/>
#for(userprofile <- userprofiles){
#if(userprofile.useraccountid != useraccount.id) {
<img src="#routes.Assets.at("images/img2.png")" width="200" height="200" />
<br>
<h5>#userprofile.name</h5>
<h5>#userprofile.useraccountid</h5>=<h5>#useraccount.id</h5>
<h6>#userprofile.gender</h6>
<h6>#userprofile.date_of_birth</h6>
<div class="actions">
<input type="submit" class="btn primary" value="+1 Add As Friend" title="Send Friend Request">
</div>
<br/>
}
}
}
when checking the condition the database values are views in the view page and
#if(userprofile.useraccountid != useraccount.id)
if change the condition to
#if(userprofile.useraccountid == useraccount.id)
Nothing will in the view page.
In this code when run the program the code section
<h5>#userprofile.useraccountid</h5>=<h5>#useraccount.id</h5>
The id's are same here , and that show in the view then the idea is not false.. for example 15=15.
Here the 2 id are same but the checking in the if case is not work properly... or the coding is not right.
Edit
This is in application
def listMyFriend = Action { implicit request =>
var cid=request.session.get("userId")
println("aa",cid)
if (request.session.get("userId") == None) {
Results.Redirect("/")
}
else {
val userprofiles:UserProfile=null
val userprofileId = request.session.get("userId").get.toLong//userProfileId
val userprofile = UserProfile.findUserByAccountId(userprofileId).get
println(userprofile)
/* val myfriendId = request.session.get("myFriendId").get.toLong//myFriendId
val myfriend = MyFriend.friendidByUserIsAccepted(myfriendId,true)
println(myfriend)*/
myFriendForm.bindFromRequest.fold(
errors => BadRequest(views.html.myFriend(errors, userprofile,myfriend,myfrnd)),
myFriend => {
println("errors")
val myFriendOpt = UserProfile.myFriend(userprofile.id.get)
println(myFriendOpt)
myFriendOpt match {
case None =>
}
Results.Redirect("/myFriend")
})
}
}
You have shadowing issues in your code: userprofile is both defined as a parameter of your template and as the variable you get out of the for comprehension.
#(userprofiles: List[UserProfile],myFriend:models.MyFriend,userprofile:models.UserProfile,useraccount:models.UserAccount)
here ---^
#helper.form(action = routes.Application.createMyFriend) {
<br/><br/><br/>
#for(userprofile <- userprofiles){
and here ---^
Try renaming one of the two and sort out which one you want to refer to in your if.
I have defined this function to build a list os students, each one with his name, surname and an image, that depending an attribute can be a tick or a cross. I want to assign to the tag academic(my image) the onClick event to call a function in my snippet called academic(Student s) which receives a student,and then redirect to another page. Can anyone help me??
def students(xhtml: NodeSeq) = {
def bindStudents(template: NodeSeq): NodeSeq = {
studentsList.flatMap {
s => bind("studentTag", template, "name" -> s.Name, "surname" -> s.Surname,
AttrBindParam("academic", if (s.HasIssue("Academic")) tickUrl else crossUrl, "src"),
)}
}
bind("list", xhtml, "studentsList" -> bindStudents _)
}
def academic(s:Student)={
//do some stuff with s
redirectTo("/OtherPage.html")
}
HTML CODE
<list:studentsList>
<tr>
<td> <studentTag:name /> </td>
<td> <studentTag:surname /> </td>
<td>
<img studentTag:academic=""/>
</td>
</list:studentsList>
There is a method in SHtml called 'onEvent'.
I'm not very sure how to do this in the old way, but if you are using new designer-friendly CSS binding helpers, it will be very simple:
HTML template:
<div data-lift="MySnippet">
Name:<span id="name">Student Name</span>
SurName:<div class="surname">Surname</span>
<img src="imageURL" class="clickable"/>
</div>
Here is the snippet:
class MySnippet
{
val studentName = "whatever"
val surName = "..."
def onClickCallback(s: String): JsCmd = {
// Do what ever you want, BTW, I never have idea
// what the ``s`` the lift will passed in.
Alert('Hey, you clicked me.') // This is the javascript that browser will execute when user click on the element.
}
// Note there is no argument list
def render = {
"#name" #> studentName & // Replace the HTML tag with id="name" to studentName
".surname" #> surName & // Replace the HTML tag with class=surname to surName
".clickable [onClick]" #> SHtml.onEvent(onClickCallback) // Append onClick attribute to HTML tag that has "clickable" class, and it will calle onClickCallable in your snippet.
}
}
Update
Sorry, I didn't notice that you are binding to a list. But since there is curried function, it would not be too difficult either.
HTML code:
<table data-lift="MySnippet">
<tr>
<td><span class="name">Name</span></td>
<td><span class="surname">SurName</span></td>
<td><img src="test.jpg"/> </td>
</tr>
</talbe>
case class Student(name: String, surname: String)
class MySnippet
{
def onClickCallback(student: Student)(s: String): JsCmd = {
Alert("You Click:" + student)
}
val xs = Student("Brian", "Hsu") :: Student("David", "Walter") :: Nil
def render = {
"tr" #> xs.map { student =>
".name" #> student.name &
".surname" #> student.surname &
".clickable [onClick]" #> SHtml.onEvent(onClickCallback(student)_)
}
}
}