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".
Related
I'm looking for a solution to accomplish the same thing as in this similar question:
HTML forms with java Play Framework 2
But in Scala. Is there a way to do this? I just have one text field and a submit button. I want to get the value from the text field when pressing my button and pass this value to backend code.
object MyController extends Controller {
val submissionForm = Form(
single("myvalue" -> text)
)
def myaction = TODO //as like #mbarlocker told before
}
On such a simple case of a single input, you don't have to use mapping in form definition and you don't have to use any template helper at the input. Template helpers are useful but in this case you get freedom over the layout, without writing a custom field constructor. For example, if you need to place the the submit button next to the input field, just write something like this:
#form(routes.Application.myaction, 'class -> "form-inline") {
<input type="text" id="myvalue" name="myvalue" value="#submissionForm.data.get("myvalue")">
<input type="submit" class="btn" value="Submit :)">
}
Basically, set up the view & form the same way as in HTML forms with java Play Framework 2, and then put this in your controller.
object MyController extends Controller {
case class Submission(value: String)
val submissionForm = Form(
mapping(
"value" -> text
)(Submission.apply)(Submission.unapply)
)
def myaction = Action { implicit request =>
submissionForm.bindFromRequest().fold(
formWithErrors => {
// do something with the bad form, like reshow the view
Ok("got a bad form")
},
submission => {
// do something with the submitted form
Ok("got " + submission.value)
}
)
}
}
Play 2.1 Forms Documentation
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.
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.
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
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>