How to make a snippet in Lift - Scala - scala

What I need to do is to put one tag into html page:
<lift:surround with="default" at="content">
<span class="lift:WorkingStatus.print"></span>
</lift:surround>
and to have a snippet class in appropriate snippet.* package:
class WorkingStatus {
def print():String={return "<table></table>";}
def speak = <span>Hello World</span>
def render = "* *" #> "Hello there in span"
}
function will generate some html table in string which will show up in html, like in def print() .
So, this is something rather trivial I cannot get to work in any way. I need a Scala class which could be incorporated wherever needed in html pages, like - lets say - with <jsp:include> and this class should be able to connect to outer web service, get list of some data and generate html which will then be inserted on desired places... complicated enough? :)

You have a wrong signature for the print method. Snippets' methods are NodeSeq => NodeSeq.
So the more verbose variant is
def print(xhtml:NodeSeq):NodeSeq={return <table></table>;} or you can use
def print = "*" #> <table></table>
if you will need a some kind of transformation, or just:
def print = <table></table>

Related

Lift 2.6: How to append a NodeSeq rather than using SetHtml()

I use SetHtml() all the time to swap chunks of HTML as part of the return trip of an ajax call, ie. when my "Edit Item" ajax call returns, I swap the contents of with form elements representing the "Item" to be edited.
Now I'm trying to add a new item to a list (so that "Item 2" appears as the next li under "Item 1"). My html looks like (this is a greatly simplified version) this:
<div data-lift="form.ajax">
<div data-lift="JsCmdTest">
<test:submitbutton></test:submitbutton>
<ul id="targetdiv">
<li>Item 1</li>
</ul>
</div>
</div>
and my Lift code looks like this
class JsCmdTest extends StatefulSnippet
{
def dispatch = { case "render" => render }
def bindStuff(ns: NodeSeq): NodeSeq =
{
bind("test", ns,
"submitbutton" -> SHtml.ajaxButton("Go", ()=> {
val newLi = Jx(<div>Item 2</div>)
(ElemById("targetdiv") ~> JsFunc("appendChild", newLi.toJs)).cmd
})
)
}
def render: (NodeSeq)=>NodeSeq = (ns: NodeSeq) => {
bindStuff(ns)
}
}
When I run this, the ajax call is fired, and the response looks fine, but I get the following error in the browser console:
The server call succeeded, but the returned Javascript contains an error: NotFoundError: Failed to execute 'appendChild' on 'Node': The new child element is null.
WANTED: a way to append replacement HTML to an existing < ul >. I feel like the way I'm going about appending seems way more esoteric than probably needed, but i haven't found any other way to do it.
Thanks in advance!
you're using a very old syntax for binding. If I am not mistaken, a new way for bindings was introduced somewhere in lift-2, and it is the recommended one since somewhere in lift-2.2. (I'll write the syntax below.)
JsFunc is an anonymous function, like def local(a: A): B. You don't need to send anonymous function, you can send the code directly. (See below.)
So, I recommend something like this:
import net.liftweb.http.js.{JsExp, JsCmd, JsCmds}
import net.liftweb.http.js.jquery.JqJE.{JqAppend, JqId}
def render = {
val appendJs: JsExp = JqId("targetdiv") ~> JqAppend(newLi)
"#mySubmitButton" #> SHtml.ajaxButton("Go", () => appendJs.cmd)
}
You'll also have to adapt the HTML a little, like using a normal <button> with id="mySubmitButton".

Play framework dynamic template include

I have a template that has to include another template based on the file name that comes from the database. For example, here is a template that takes a String that contains the name of the template file that will be included in another template.
#(sourceCodeFileName: Option[String])
#{sourceCodeFileName match {
case Some(sourceCode) => {
#sourcecode.sourceCodeFileName + "scala.html"
}
}}
Where sourcecode is the package where the actual template resides. For example., if the String parameter to the above template is given as myview, then I want to include myview.scala.html. Is there a way to do this in Play framework?
To inject HTML from a static file to a scala template, you can define a function in your template:
#import scala.io.Source
#injectHtmlFromFile(sourceCodeFilename: String) = { #{
Html(Source.fromFile("static/html/" + sourceCodeFilename + ".html").mkString)
}}
…and call it later in the template this way:
#injectHtmlFromFile(sourceCode.sourceCodeFileName)
Side note
I'm not sure I quite understand the question – I've answered the OP's comment and the same question posted by him on Google Groups.
A Play scala template is a function returning Html. You should obtain the template object in the controller and pass it to the template. But in simple cases it's just easier to pass the rendered Html:
Your template would then look like this:
#(content: Option[Html])
#content
and the controller:
object Application extends Controller {
def index = Action {
val sourceCodeFileName= ...
Ok(Some(Class.forName("views.html."+sourceCodeFileName)
.getConstructor().newInstance().asInstanceOf[() => play.api.templates.Html]()
))
}
}

Cannot access the parameter of a Menu.param from a Lift Snippet

I'm trying to extract the parameter from a Lift Menu.param within a snippet so that I can use it to create a named Comet. However, I get a NullPointerException when I try to pass the parameter to the snippet using SnippetDisptach in my Boot.scala, as suggested here:
http://comments.gmane.org/gmane.comp.web.lift/44299
I've created the Menu item as follows:
object AnItemPage {
// create a parameterized page
def menu = Menu.param[Item]("Item", "Item",
s => fetchItem(s), item => item._id.toString) / "item"
private def fetchItem(s:String) : Box[Item] = synchronized {
ItemDAO.findById(ObjectId.massageToObjectId(s))
}
}
I've added the menu to SiteMap. I've also created a Snippet which I would like to pick up the Item parameter. (I'm using fmpwizard's InsertNamedComet library here):
class AddCometItemPage(boxedItem: Box[Item]) extends InsertNamedComet with DispatchSnippet{
val item : Item = boxedItem.openOr(null)
override lazy val name= "comet_item_" + item._id.toString
override lazy val cometClass= "UserItemCometActor"
def dispatch = null
}
My next step is to crate an instance of this class as demonstrated by David Pollak here:
http://comments.gmane.org/gmane.comp.web.lift/44299
This is what I have added to my Boot.scala:
LiftRules.snippetDispatch.append {
case "item_page" => new AddCometItemPage(AnItemPage.menu.currentValue)
}
My item.html references this snippet:
<div class="lift:item_page">
I get the following null pointer exception when I compile and run this:
Exception occurred while processing /item/5114eb4044ae953cf863b786
Message: java.lang.NullPointerException
net.liftweb.sitemap.Loc$class.siteMap(Loc.scala:147)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.siteMap(Menu.scala:170)
net.liftweb.sitemap.Loc$class.allParams(Loc.scala:123)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.allParams(Menu.scala:170)
net.liftweb.sitemap.Loc$class.net$liftweb$sitemap$Loc$$staticValue(Loc.scala:87)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.net$liftweb$sitemap$Loc$$staticValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.sitemap.Loc$$anonfun$paramValue$2.apply(Loc.scala:85)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.paramValue(Loc.scala:85)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.paramValue(Menu.scala:170)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.sitemap.Loc$$anonfun$currentValue$3.apply(Loc.scala:114)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.sitemap.Loc$class.currentValue(Loc.scala:114)
net.liftweb.sitemap.Menu$ParamMenuable$$anon$9.currentValue(Menu.scala:170)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:107)
bootstrap.liftweb.Boot$$anonfun$lift$8.apply(Boot.scala:106)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.util.NamedPF$$anonfun$applyBox$1.apply(NamedPartialFunction.scala:97)
net.liftweb.common.Full.map(Box.scala:553)
net.liftweb.util.NamedPF$.applyBox(NamedPartialFunction.scala:97)
net.liftweb.http.LiftRules.snippet(LiftRules.scala:711)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.http.LiftSession$$anonfun$net$liftweb$http$LiftSession$$findSnippetInstance$1.apply(LiftSession.scala:1506)
net.liftweb.common.EmptyBox.or(Box.scala:646)
net.liftweb.http.LiftSession.net$liftweb$http$LiftSession$$findSnippetInstance(LiftSession.scala:1505)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1670)
net.liftweb.http.LiftSession$$anonfun$locateAndCacheSnippet$1$1$$anonfun$apply$88.apply(LiftSession.scala:1669)
Has anybody any idea where I'm going wrong? I've not been able to find a lot of information on Menu.param.
Thank you very much for your help.
f
I have never tried what you are doing, so I am not sure the best way to accomplish it. The way you are using the Loc Param, you are extracting a variable from a URL pattern. In your case, http://server/item/ITEMID where ITEMID is the string representation of an Item, and which is the value that gets passed to the fetchItem function. The function call will not have a value if you just arbitrarily call it, and from what I can see you are requesting a value that is not initialized.
I would think there are two possible solutions. The first would be to use S.location instead of AnItemPage.menu.currentValue. It will return a Box[Loc[Any]] representing the Loc that is currently being accessed (with the parameters set). You can use that Loc to retrive currentValue and set your parameter.
The other option would be to instantiate the actor in your snippet. Something like this:
item.html
<div data-lift="AnItemPage">
<div id="mycomet"></div>
</div>
And then in your AnItemPage snippet, something like this:
class AnItemPage(item: Item) {
def render = "#mycomet" #> new AddCometItemPage(item).render
}
I haven't tested either of those, so they'll probably need some tweaking. Hopefully it will give you a general idea.

Render HTML definition lists in Lift

Another lift beginner question:
I want to render a HTML Definition List in a Lift Snippet like this:
<dl>
<dt>Name</dt>
<dd>Seppl</dd>
<dt>Street</dt>
<dd>abc</dd>
</dl>
The snippet-template looks now like this:
<dl>
<div class="definition">
<dt>Term</dt>
<dd>Description</dd>
</div>
</dl>
The Scala Snippet code:
def render = {
val values=List(("Name", "Seppl"), ("Street", "abc"))
".definition" #> values.map(value =>
("dt" #> value._1) &
("dd" #> value._2))
}
This works, but I want a definition list without div-Tags (I think, this it isn't valid HTML.)
Is this possible with CSS Transformers?
As you don't seem to use CSS I would recommend you to use the following:
Snippet Template code:
<div class="lift:render"/>
Scala Snippet code:
def render = {
val values=List(("Name", "Seppl"), ("Street", "abc"))
values.map(value =>
<dt>{value._1}</dt>
<dd>{value._2}</dd>
)
}
Of course I avoid using CSS selectors so I don't know if it is exactly what you are looking for.
See http://www.assembla.com/spaces/liftweb/wiki/Binding_via_CSS_Selectors
At the end of the page (Iteration) there's an example with unordered list.

Scala Lift - Dynamic page chrome

I have a page which is passed an id from which the content is determined. What I'm looking to do is dynamically replace the pages "chrome" depending on the content.
So if I have a db record "Book", I'd like to display the Book chrome in templates-hidden.
Thanks for any help, much appreciated :)
You can define a snippet that selects the template you want and pass the xhtml NodeSeq containing the template selection to it:
<lift:TemplateSnippet.dynamicTemplatePicking>
<lift:surround dynamic:template="" at="content">
<h2>some heading</h2>
<p>some text</p>
</lift:surround>
</lift:TemplateSnippet.dynamicTemplatePicking>
And the snippet code:
class TemplateSnippet {
def dynamicTemplatePicking(xhtml :NodeSeq) :NodeSeq = {
bind("dynamic", xhtml, AttrBindParam("template", Text("default"), "with"))
}
}
By changing the "default" in Text("default") inside the snippet you can choose another template depending on your needs. Maybee you want to do something like def choose (record: Record) = record match { case b: Book => "booktemplate"} and so on...
Source: Mads Hartmann's posting