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
Related
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".
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]()
))
}
}
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.
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>
As more and more people are interested in Scala (like myself), rather than a question, I'd like to discuss one implementation of a login/logout snippet for a webapp based on Lift.
I just started to learn Scala and Lift so it's probably not the best way to implement such a feature but I'd like to share it for other beginners and discuss it with more experienced developers.
Please note that I'm also not an expert in web development. Any help for improvements would be greatly appreciated (especially performance and security related ones) ;-)
1) First of all, the snippet needs to be easily plugable, like with 1 line of code in your default template. I've done it using the embedded Lift feature (notice the underscore so it can't be rendered as a page itself but only invoked from a rendered page, in short, some kind of "private" snippet):
<lift:embed what="_logInForm" />
2) Then, in _logInForm.html, I use the below markup and a conditional display to handle everything:
<div>
<!-- User is not logged in, show a form to log in using the method loggedOut -->
<lift:LogInForm.loggedOut>
<form class="lift:LogInForm.logIn?form=post">
<label for="textName">Username: </label><input type="text" id="textName" name="name" /> <span class="lift:Msg?id=name;errorClass=error"/><br/>
<label for="textPassword">Password: </label><input type="password" id="textPassword" name="password" /> <span class="lift:Msg?id=password;errorClass=error"/><br/>
<input type="submit" value="Log in" />
</form>
</lift:LogInForm.loggedOut>
<!-- User is logged in, show who she is and a way to log out using the method loggedIn -->
<lift:LogInForm.loggedIn>
<form class="lift:LogInForm.logOut?form=post">
Connected as <span class="lift:LogInForm.getName" />.<br />
<input type="submit" id="btnLogOut" value="Log out" />
</form>
</lift:LogInForm.loggedIn>
</div>
3) ... and now the Scala/Lift logic behind this markup:
object LogInForm {
private object name extends SessionVar("")
private object password extends RequestVar("")
private object referer extends RequestVar(S.referer openOr "/")
var isLoggedIn = false
def loggedIn(html: NodeSeq) =
if (isLoggedIn) html else NodeSeq.Empty
def loggedOut(html: NodeSeq) =
if (!isLoggedIn) html else NodeSeq.Empty
def logIn = {
def processLogIn() {
Validator.isValidName(name) match {
case true => {
Validator.isValidLogin(name, password) match {
case true => { isLoggedIn = true } // Success: logged in
case _ => S.error("password", "Invalid username/password!")
}
}
case _ => S.error("name", "Invalid username format!")
}
}
val r = referer.is
"name=name" #> SHtml.textElem(name) &
"name=password" #> (
SHtml.textElem(password) ++
SHtml.hidden(() => referer.set(r))) &
"type=submit" #> SHtml.onSubmitUnit(processLogIn)
}
def logOut = {
def processLogOut() { isLoggedIn = false }
val r = referer.is
"type=submit" #> SHtml.onSubmitUnit(processLogOut)
}
def getName = "*" #> name.is
}
Comments:
The selection between the two forms is made by the logic, rendering either the provided markup or NodeSeq.Empty, based on the fact the user is either logged in or logged out.
I used Lift:Msg to have error messages next to the appropriate fields (name/password). The message is sent using S.error in the logic behind and the appropriate id.
I actually perform the checks in a Validator helper using regexps and formats checks, etc. This return a boolean each time to pattern matching is trivial.
I use the referer so the user can log in / log out while staying on the same page.
The username is kept as a session variable and shown when logged in.
4) You can control access to other pages doing the following in Boot.scala:
def sitemap() = SiteMap(
Menu("Home") / "index",
Menu("Protected page") / "protectedPageName" >> If(() => LogInForm.isLoggedIn, ""),
// etc.
Questions:
No SSL protection (that could be an improvement, haven't had a look at this in Scala/Lift yet). Any experience from someone else could be useful?
Use of session variable. Maybe there is a better way to keep state in Scala/Lift?
Is there already something made especially for logging in/out in Lift that I could have missed?
It's not very long, but could maybe be more compact? (I'd like not to sacrifice too much readability though. Other developers need to quickly understand it)
Any other suggestion?
Cheers,
Marc.
This shows how to force use of SSL, you will typically use that for the login form page menu: Lift filter to force ssl
A session variable is typically best way to keep session information
ProtoUser (you can use Lifty (http://lifty.github.com/) to setup a project with login as an example)
Lift provides a scaffolding framework for these usecases. You want to have a look at the source code of net.liftweb.proto.ProtoUser. Especially login() and loginXhtml.
The basic examples of Lift do it the following way:
Create an own mapped user class that uses all the nasty lift code
package yourcompany.model
import net.liftweb.mapper._
import net.liftweb.util._
import net.liftweb.common._
class User extends MegaProtoUser[User] {
// your code, mostly overrides
}
object User extends User with MetaMegaProtoUser[User] {
override def dbTableName = "users"
// other database related code
}
In your Boot after you defined your sitemap, do the following:
// do not forget to import your user
def sitemap() = Sitemap(Menu("Home") / "index" >> User.AddUserMenusAfter)
// now comes the magic
LiftRules.setSiteMap(User.sitemapMutator(sitemap()))
This will include many links on your page, all gathered under /user_mgt e.g. sign_up, login, lost_password. For me this is a great base to work from. You can override almost all the things that happen in ProtoUser, or just copy the nice bits and implement everything yourself.
Also if someone has additional docs on that, it will be very helpful. At the moment I try to figure out the most part using the sources.