Has anyone tried to do a scala/lift application using MVC instead of view-first?
I know that you can create Controllers/views as:
package test.test.test.view
...
Lots of imports
...
class MvcRocks extends LiftView {
def dispatch = {
case "rule" => ruleDispatch _
case "bar" => barDispatch _
}
def barDispatch(): Box[NodeSeq] = {
Full(<lift:embed what="/mvc_rucks/bar" />)
}
}
And this code will be accessible if you add it to the menu(in the boot), even if its hidden as:
val entries = Menu(Loc("Home", List("index"), "Home")) ::
List(Menu(Loc("MvcRock", List("mvc_rocks", "bar"), "Mvc really Rocks", Hidden)))
LiftRules.setSiteMap(SiteMap(entries:_*))
Now, of course this will make it so, you declare every action in the menu, then have a case for each action(per controller) and that would open the "view" (that would be a file in /mvc_rucks/bar.html).
My question is, if you would implement a full mvc, you would need to put all your logic in the action barDispatch, but how would you send those variables to the HTML template? and how would you receive post/get information?
(Notice that if you html code has lift bindings, it will of course act as view-first, even after you did MVC before).
Since your question is not specific to Lift, I'd recommend you the Playframework. The version 1.1 supports Scala 2.8.
Playframework is totally MVC with a fantastic template engine and allows you to choose freely between java/scala.
And I say: To use Play, you don't need 'nuclear scientist knowledge'. Try it!
Related
Using the Play Framework (2.6) documentation; I am attempting to handle form submission. But I'm running into an issue in repopulating the form fields - which is what I want to do if it has errors (so users can edit their entry rather than having to re-enter).
newForm.bindFromRequest.fold(
errorForm => {
BadRequest(views.html.form(errorForm))
},
formData => {
val oWriteJso = Json.toJsObject(formData) match {
case x if(x.fields.nonEmpty) => getCreatedFieldValues(x)
case _ => None
}
val oRes = Redirect(routes.Application.index).flashing("success" -> "Entry saved!")
apiC.writeAndRedirect("c", collName, None, oWriteJso)(oRes)(request)
}
)
My issue is that the example in the documentation only shows how to pass errorForm directly to a form template (e.g. views.html.form) rather than being able to render the whole page again (i.e. using views.html.index or a Redirect) with the input form fields being populated from the previous request. I found this answer as the closest to this issue but it is a little old and I am using Scala so wasn't able to implement it. Just have no idea how anyone else is doing this or what the sensible, standard approach is. Thanks for any light on this_
If you use the Play helper functions for generating input tags in your view file. They should populate with your values from the last request.
For example you can create an HTML form in your view file using helper methods like this:
#helper.form(action = routes.Application.userPost()) {
#helper.inputText(userForm("name"))
#helper.inputText(userForm("age"))
}
You can take a look at Play documentation about helpers in view files at the following link:
https://www.playframework.com/documentation/2.7.x/ScalaForms#Showing-forms-in-a-view-template
I am a Scala/PlayFramework noob here, so please be easy on me :).
I am trying to create an action (serving a GET request) so that when I enter the url in the browser, the browser should download the file. So far I have this:
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content)
}
What it does is basically show the XML in the browser (whereas I actually want it to download the file). Also, I have two problems with it:
I am not sure if using Play's templating "views.html..." is the best idea to create an XML template. Is it good/simple enough or should I use a different solution for this?
I have found Ok.sendFile in the Play's documentation. But it needs a java.io.File. I don't know how to create a File from HtmlFormat.Appendable. I would prefer to create a file in-memory, i.e. no new File("/tmp/temporary.xml").
EDIT: Here SepaCreditTransfer is a case class holding some data. Nothing special.
I think it's quite normal for browsers to visualize XML instead of downloading it. Have you tried to use the application/force-download content type header, like this?
def sepaCreditXml() = Action {
val data: SepaCreditTransfer = invoiceService.sepaCredit()
val content: HtmlFormat.Appendable = views.html.sepacredittransfer(data)
Ok(content).withHeaders("Content-Type" -> "application/force-download")
}
This question may have a bit of philosophical aspect to it.
I have been using Deadbolt 2 (Scala) in my Play application and it works quite well.
In looking at the Restrict function definition (line 47) I noticed that it will invoke the onAuthFailure for one of the following reasons:
No user in session (no subject)
Action specified no roles.
User attempted an action for which they did not possess one or more required roles.
In my application UI, I would like to receive a different status code for each of these so that a user that is not logged in (condition 1) will be redirected to login page but condition 3 would be more gracefully handled by just a warning (since they can do no harm anyway and might have accidentally tried to edit when they have 'read-only' access - perhaps a UI bug, but logging in again is a bit draconian).
If I had to settle for just 2 status codes, however, I would want to differentiate between 1 and the other 2. I can see how this could be accomplished but would like to get other opinions on the merits of even doing this.
If I were to implement this change, it looks like I could just override the Restrict function in my own extension of the DeadboltActions trait.
I'm a little new to scala, so I'm open to additional ideas on how to best accomplish these goals.
I decided to just add the code to differentiate between condition 1 and either 2 or 3 as follows:
In MyDeadboltHandler:
class MyDeadboltHandler(dynamicResourceHandler: Option[DynamicResourceHandler] = None) extends DeadboltHandler {
...
def onAuthFailure[A](request: Request[A]): Result = {
Logger.error("authentication failure")
val json = new JsonStatus("Failed to authenticate", -1).toJson.toString
if(noUserInSession(request)){
Results.Forbidden(json).withHeaders("Access-Control-Allow-Origin" -> "*")
}
else{
Results.Unauthorized (json).withHeaders("Access-Control-Allow-Origin" -> "*")
}
}
def noUserInSession(request:RequestHeader) = {
username(request) match {
case Some(u:String) => false
case _ => true
}
}
This works well for me and does not impose upon the basic Deadbolt-2 functionality.
I know that the earlier versions of Play used to support routes and conditionals (if blocks) in the routes file but I cannot find any such documentation for Play 2.2.x and HTTP routing says nothing about such a feature.
I want to replace this:
GET /api/users/:id com.corporate.project.controllers.UserController.get(id)
with a shorter version using import as follows:
import com.corporate.project.controllers._
GET /api/users/:id UserController.get(id)
Also, is it possible to have conditionals in the routes file? e.g.
if Play.isDev(Play.current())
GET /displayConfig DebugController.displayServerConfigs()
Package imports used to be possible with an SBT setting: routesImport += "com.corporate.project.controllers._". Not sure if it is still the case.
Also, is it possible to have conditionals in the routes file?
It might not be an ideal solution but we use routes tags to deal with this kind of conditional routes. You need a filter that checks if the route is annotated and runs your conditional logic.
Routes:
# #devmode
GET /displayConfig DebugController.displayServerConfigs()
Filter:
object DevmodeRouteFilter extends Filter {
private val DevmodeAnnotation = "#devmode"
override def apply(next: RequestHeader => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
if(isDevRoute(request) && !Play.isDev(Play.current()) {
Play.current.global.onHandlerNotFound(request)
} else {
next(request)
}
}
private def isDevRoute(request: RequestHeader): Boolean = {
val comments = request.tags.getOrElse(Routes.ROUTE_COMMENTS, "")
comments.lines.exists { comment =>
comment.trim == DevmodeAnnotation
}
}
}
Don't forget to add the filter to the filter chain.
Thank you for your question, it is an interesting idea, so I went to the sources of Play2 and found that in my checkout of Play 2.2.3 in the play.core.Router.scala: object HandlerInvoker constructs controller as
handlerDef.ref.getClass.getClassLoader.loadClass(handlerDef.controller)
So I am afraid that support for partial class names (stored in handlerDef.controller) is not implemented in 2.2.x.
In 2.3.x there is commit https://github.com/playframework/playframework/commit/ba98dabd7ba374ed98f14f721835b898d27dd55b
that brought notion of package/namespace for the routes.
Please see changes in Routes.scala and RoutesCompiler.scala from that commit.
But I can't find description of that functionality in the documentation and I do not have Play 2.3.x to play with it now.
P.S.: Sample magic for advanced routing for 2.3.x described in http://java.dzone.com/articles/advanced-routing-play .
I am building an application using the Play! framework version 2.0.4. Some pages that are legally required such as an imprint mostly contain lots (some 10k each) of static text which I would like to provide in different languages.
In versions 1.x there was an #include directive that allowed to construct the actual resource path using the current Lang.
What is the recommended way of implementing sth similar with Play 2.x?
Thank you & best regards,
Erich
I'm not 100% certain how you implement it as of now, but here's what I've come up with.
You could just write your own include helper. Save the following in a file Helpers.scala in your views folder. Explanations are in the code's comments.
package views.html.helper
object include {
import play.api.templates.Html
import play.api.Play
import play.api.Play.current
import play.api.i18n._
// The default is to look in the public dir, but you can change it if necessary
def apply(filePath: String, rootDir: String = "public")(implicit lang: Lang): Html = {
// Split filePath at name and suffix to insert the language in between them
val (fileName, suffix) = filePath.splitAt(filePath.lastIndexOf("."))
// Retrieve the file with the current language, or as a fallback, without any suffix
val maybeFile =
Play.getExistingFile(rootDir + "/" + fileName + "_" + lang.language + suffix).
orElse(Play.getExistingFile(rootDir + "/" + filePath))
// Read the file's content and wrap it in HTML or return an error message
maybeFile map { file =>
val content = scala.io.Source.fromFile(file).mkString
Html(content)
} getOrElse Html("File Not Found")
}
}
Now in your imprint.scala.html you could call it like this:
#()(implicit lang: Lang)
#import helper._
#include("static/imprint.html")
Way showed by Schleichardt was used in play-authenticate to select mail templates in different languages and now it's changed to work with reflections on the controller, so maybe it can be interesting for you. Anyway it was intendent to keep standard templating possibilities (cause each mail need to be personalized before send)
For static info pages, you can just save the code for each language with suffix ie. impressum_en.html, impressum_de.html in the file system and use simple controller which will find file with proper suffix and return its content exactly as it is... all you need probably to return Ok(fileContent) and set the Content-Type manually to text/html.
Other option is doing the similar thing but storing it in the DB, so you can create simple back-end and edit it with browser.
If you still need to replace some elements, you can do it with some ###MARKER### in the code + simple String operations, or with JavaScript on the client side.
Templates in 2.0 work a bit differently. The Scala based templates are compiled. From one template instead of 'including' another, you can make a call to it. I'm not sure exactly what you mean by the language thing. In 2.0 though, a parameter to the template could be the language.
Example template named 'included' in package whateverpackage.
#(lang: Lang)
...
Example calling a template named 'included':
#import whateverpackage._
#included(Lang("en"))
#()(implicit lang: Lang)
#main(Messages("imprint")) {
#lang match {
case Lang("de", _) => { #germanImprint() #* uses app/views/germanImprint.scala.html file *#}
case _ => { #englishImprint() }
}
}