What is the "best" way to pass Data with a Link?
In the moment i save the Data in a SessionVAr and get it out on the other side. But that feels very clumsy. Is there a other way to do that?
object activeHw extends SessionVar[Box[Hw]](Empty)
object AddHwScreen extends LiftScreen {
object hw extends ScreenVar(activeHw.is.getOrElse(Hw.createRecord))
addFields(() => hw.is)
def finish() = {
redirectTo("/hw")
}
}
class HwSnippet extends StatefulSnippet with PaginatorSnippet[Hw] {
var dispatch: DispatchIt = {
case "showAll" => showAll _
}
def showAll(xhtml: NodeSeq): NodeSeq = {
page.flatMap(hw => {
(".hwDetail *" #> link ("hw/detail", () => activeHw.set(Full(hw)), Text("Detail")) &
".hwSn *" #> hw.sn).apply(xhtml)
})
}
def redirectToHome = {
redirectTo("/hw")
}
}
If you have:
object MyInfo extends RequestVar("nothing")
Then:
val str = (new java.util.Date).toString
S.redirectTo("/", () => MyInfo.set(str))
You capture the value by calculating it before doing the redirectTo, then execute a function that sets a RequestVar to the value. The RequestVar can be accessed by the target page.
If it's in a SessionVar then there's no need to pass it in the link. Just access it in the next Snippet.
Update: Sorry, I misread your question. You are already doing that but would rather pass it on the link (as a query parameter?). Generally that isn't the Lift way of doing things: someone could change the value, etc. If you really want to you can add a query param to the link and then access it in the next Snippet with S.param("the-name").
Related
From what I understand controllers in play are singletons, so I'm not sure how to go about this.
In Rails I could simply load a user in the base_controller, and then in any controller that inherits from the base_controller the user object would be available for me to use.
In play, I'm not sure how this will work. Basically in each Action in a controller I want the user object to be available for me.
def someAction = {
val name = user.name
val age = user.age
Ok("hello")
}
Is this possible in play? I'm' sure it is but I can't seem to figure out way to do this.
I'm not sure you'll be able to make it quite as terse as Rails, but using Play's Action Composition style you can build up something quite flexible.
We start by creating a trait which knows how to build such a user, and wraps a normal Action, allowing the User to be obtained, and passed into the body:
trait UserAware {
def buildUserFromCookie(request:RequestHeader):Option[User] = {
request.cookies.get("myUserCookie").map { c =>
// Build up a User object from cookie contents
// ...
}
}
def cookieNotFound = Results.InternalServerError("Lost your session cookie :-(")
def UserAction(f: User => Request[AnyContent] => SimpleResult):Action[AnyContent] =
UserAction[AnyContent](BodyParsers.parse.anyContent)(f)
def UserAction[T](bodyParser:BodyParser[T])(f: (User) => Request[T] => SimpleResult):Action[T] = {
Action(bodyParser) { request =>
buildUserFromCookie(request).fold(cookieNotFound) ({ user =>
f(user)(request)
})
}
}
}
I'm going to assume that you have a session cookie that holds sufficient information to be able to recreate a User object. Whatever you need to do within buildUserFromCookie() is out of scope here.
I've defined two UserAction wrappers, one delegating to the other. The difference is whether you need a special body parser or not. Below you'll see it in use.
The second UserAction method is the interesting one - if you supply it with a method that can take a User and a Request, it will call it for you after creating the user, or bombing out if it can't find the cookie.
Here it is in use:
object MyController extends Controller with UserAware {
def doSomething = UserAction { implicit user => implicit request =>
Ok(s"The user's name is $user.")
}
def doSomethingJsonish = UserAction(parse.json) { implicit user => implicit request =>
Ok(s"The user's name is $user and their request body is JSON: ${request.body}")
}
}
With the overloaded UserActions, you can still use Play's Body Parsers to get access to a nicely-typed request body, and you've got the freshly-created User available as user.
The scenario:
I have two different Api calls to make (across the web). ApiCall1, ApiCall2. Eventually ApiCall1 will return an Option[Catalogue] and ApiCall2 will return a Seq[Catalogue]
I then need to take these two and build a FrontPage object. During the instantiation of the FrontPage object it creates a Seq[NewProducts]. Everytime it makes a NewProduct the NewProduct must also call a MongoDB across the web inside a Future. Every Future must have completed prior to handing the FrontPage object off to the view.
Here is the code for the FrontPage class:
case class FrontPage(maybeCat1: Option[Catalogue], maybeCat2: Seq[Catalogue]) {
val newProducts:Seq[NewProduct] = {
maybeCat2.map( { cat =>
NewProduct(cat)
})
}
}
Here is the code so far for the NewProduct class:
case class NewProduct(cat:Catalogue) {
val indivProduct:Option[IndivProduct] = {
// ???
// This next line goes out to Mongo and returns a Future[List[JsObject]]
val indiv:Future[List[JsObject]] = MongoFetch.getIndivProduct(cat)
//need to strip out the 'Future', wait for it to return?
val listJS = indiv .. ???? // <-- need just the List[JsObject]]
return IndivProduct(listJs) // <-- constructs a new Option[IndivProduct]
}
}
Here is the code so far for the controller:
def landing() = Action.async {
for {
catalogue1 <- models.Granite.getCatalogue("front-page") // <- ApiCall1
catalogue2 <- models.Granite.getCatalogue("tags") // <- ApiCall2
} yield {
//??? How to now build the FrontPage object
// surely it also depends on the future?
val fp = FrontPage(catalogue1, catalogue2)
Ok(views.html.frontpage.landing(fp)) // <- at this point all futures must have returned.
}
}
I really want to be able to pass a nice neat FrontPage object to the View (and hence the designers) with a really simple set of function defined on it for them to template with. All the futures must return. Catalogue1 and Catalogue2 are not dependent on anything or even each other. Creating a Seq[NewProducts] inside the FrontPage object is depending on both of them having returned. And I can not then pass the FrontPage object to the view until it to has returned the NewProducts from Mongo.
This level of complexity is above what I am used to. I am getting confused with where and when to use for/yield comprehensions. I am afraid that this will somehow just block because the Futures are too far embedded in a case class, within a case class. The very top level of the controller is wrapped in an Async, so will this mean that any and all Futures within that Async call will be non-blocking?
See the futures as steps to get to a fully complete front page, not parts of it and think what each little part of those steps would be.
For example, to construct an instance of NewProduct, create a method that talks to the db and returns the future completed NewProduct instances.
case class NewProduct(cat:Catalogue, indiv: Option[IndivProduct])
def newProductFor(cat: Catalogue): Future[NewProduct] =
for {
listJs <- MongoFetch.getIndivProduct(cat)
} yield NewProduct(cat, IndivProduct(listJs))
You can then create your front page, again, in a function/method that handles the loading/future:
case class FrontPage(
maybeCat1: Option[Catalogue],
maybeCat2: Seq[Catalogue],
newProducts: Seq[NewProduct])
def loadFrontPage: Future[FrontPage] =
for {
catalogue1 <- models.Granite.getCatalogue("front-page")
tags <- models.Granite.getCatalogue("tags")
newProducts <- loadNewProducts(tags)
} yield FrontPage(catalogue1, tags, newProducts)
def loadNewProducts(catalogues: Seq[Catalogue]): Future[Seq[NewProduct]] = {
Future.traverse(catalogues) { catalogue =>
newProductFor(catalogue)
}
}
Note Future.traverse which takes a collection with A:s in and a function that goes from A => Future[B] and returns a Future[collection[B]].
You can then call that in an async controller to feed to a template:
def page() = Action.async {
for {
frontPage <- loadFrontPage
} yield Ok(views.some.template(frontPage))
}
I have a global filter that I would like to implement in my scalatra based API. For the sake of simplicity, I want ANY API call that has a variable foo with a value bar to throw a 403. I started this problem with an inheritance chain.
class NoFooBarRouter extends ScalatraServlet{
before() {
if(params.getOrElse("foo", "") == "bar") //params is empty here.
halt(403, "You are not allowed here")
else
pass()
}
}
class APIRouter extends NoFooBarRouter{
get("/someurl/:foo") {
"Hello world!"
}
}
This does not work. During the debug process, I noticed that the params variable is always empty regardless of whether there is a param or not. Is there a better approach or is there another method to extract the params from the before filter?
The params are not filled out during the before method. You can override the invoke method.
class NoFooBarRouter extends ScalatraServlet{
override def invoke(matchedRoute: MatchedRoute): Option[Any] = {
withRouteMultiParams(Some(matchedRoute)){
val foo = params.getOrElse("foo", "")
if(foo =="bar")
halt(403, "You are not authorized for the requested client.")
else
NoFooBarRouter.super.invoke(matchedRoute)
}
}
}
I'd like to add a (per method / global) filter to requests, that simply rejects (404/403 page) any request that doesn't have a specific URL parameter.
I know Play has one-two mechanism to do this (e.g. register on Global.onRouteRequest()), so don't just send me a link to the documentation unless it contains a code sample that covers this question. I tried playing with the API but got a bit stuck.
Is this what you mean?
object Global extends WithFilters(AccessCheck)
object AccessCheck extends Filter with Results {
override def apply(next:RequestHeader => Result)(request:RequestHeader):Result =
request
.getQueryString("myCheck")
.map( myCheck => next(request))
.getOrElse(Forbidden)
}
http://www.playframework.com/documentation/2.1.0/ScalaInterceptors
If you are just trying to make some reusable code to filter requests on specific actions you my want to try creating an EssentialAction like this. This is known as action composition. This is what it would look like in the case you described. There is more information in the docs: http://www.playframework.com/documentation/2.1.1/ScalaActionsComposition
Note that you can do this in Play 2.0.X as well but EssentialAction doesn't exist, instead you use an Action, and there is just a little more syntax involved.
def RequireMyCheck(action: => EssentialAction): EssentialAction = {
EssentialAction { request =>
request
.getQueryString("myCheck")
.map( myCheck => action()(request))
.getOrElse(Forbidden)
}
}
You can use it like this:
def index = RequireMyCheck {
Action { request =>
Ok("Hello")
}
}
In response to some asynchronous event on the server, I want to update the class of an HTML node in order to reflect its updated status. I know the id of the node, and the class I want to change it to. What JsCmd do I need to use to update the class? In general, where can I find a good reference on the JsCmd's and what they do?
A simple example:
case class UpdateClass(id: String, htmlClass: String)
class ClassUpdater extends CometActor {
override def lowPriority: scala.PartialFunction[scala.Any, scala.Unit] = {
case UpdateClass(id, htmlClass) =>
partialUpdate(Noop /* now what? */)
}
def render = NodeSeq.Empty
}
So if I had the HTML:
<html><body>
<lift:comet type="ClassUpdater"/>
<div id="foo" class="bar">insert text here</div>
</body></html>
If I sent the message UpdateClass("foo", "baz") to my ClassUpdater, I want the class of my div to change to baz.
Edit: I’ve found a better way to do it. The old code is now more of a blueprint for more complicated stuff.
Looks like there is a more straightforward way of doing it without jQuery:
SetElemById("foo", JE.Str("baz"), "className")
which translates to a JavaScript call
document.getElementById("foo").className = "baz";
Note that JE.Str("baz") can be any JsExp and if you could also do something like
SetElemById("foo", JE.Str("baz"), "firstChild", "className")
which would change the class of the first child. (See: SetElemById)
You can have a look at the code for the JsCMD trait for what else is possible with build-in commands.
In case you want to something more complicated, however, something like this might help you. It sends a jQuery command which will change the class in #oldId to newClass.
case class ChangeClassAtId(oldId: String, newClass: String) extends JsCmd {
def toJsCmd = """try {
$(""" + ("#" + oldId).encJs + """).attr("class", """ + newClass.encJs + """);
} catch (e) {}"""
}
Changing all occurrences of a class everywhere is a bit more complicated:
case class ChangeClass(oldClass: String, newClass: String) extends JsCmd {
def toJsCmd = """try {
$(""" + ("." + oldClass).encJs + """).each(function(){
$(this).addClass(""" + newClass.encJs + """).removeClass(""" + oldClass.encJs + """);
});
} catch (e) {}"""
}
You should use it instead of Noop of course.
EDIT - I misread the question. My answer merely updates the contents of the div.
Check out: http://github.com/lift/lift/blob/master/examples/example/src/main/scala/net/liftweb/example/comet/Clock.scala
You'll want something like this:
case UpdateClass(id, htmlClass) =>
partialUpdate(SetHtml(id, Text("TEXT TO SHOVE INTO DIV")))