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
Related
I am getting an error when I try to extract a user from a form with validation errors.
I have the following route configured in my routes file:
GET /users/:user controllers.UsersController.viewUser(user: models.User)
GET /users/:user/edit controllers.UsersController.editUser(user: models.User)
This is fine at this point, and I can render a link to the user view from my scala templates:
routes.UsersController.viewUser(myUserObject)
My problem is that in my user edition form I need to get myUserObject from a Form[User] object. What I am currently doing is:
routes.UsersController.viewUser(userForm.get)
However, when the userForm has any errors, the get method raises an exception, as shown in the documentation.
The approach I have taken is passing an additional User parameter to the scala view, together with the Form[User] parameter I was passing up to now, I mean,
userEdit.render(user, userForm)
instead of just
userEdit.render(userForm)
However, I would like to know if there is a more suitable solution that does not involve including an additional parameter.
The documentation states that you can prefill a form with existing data:
val filledForm = userForm.fill(User("Bob", 18))
Given the preexisting User, it should be trivial to adapt to your example.
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")
}
I have a parent model that has many child models, for example:
App.Blog = Ember.Object.extend({})
App.Comment = Ember.Object.extend({})
Then I have a view for the Blog:
window.App.BlogView = Ember.View.extend
templateName: 'app_templates_blog'
Now, when I initially retrieve a Blog via my REST API, it contains the first 10 Comments or so. Those are instantiated like this:
window.App.Blog.reopenClass
create: (obj) ->
Object.tap #_super(obj), (result) =>
if result.comments
result.comments = result.comments.map (p) => App.Comment.create(p)
I display by blog by calling BlogView.set('blog', blogInstance) and everything is displaying perfectly.
However!
I am implementing an infinite scroll, so when the end user gets to the bottom of the comments, I want to load more. I do this via REST, but I can't for the life of me get them to display by appending them.
This is what my morePosts() method looks like in BlogView:
moreComments: () ->
blog = #get('blog')
jQuery.getJSON "/blogs/#{blog.get('id')}/comments", (result) =>
comments = blog.get('comments')
result.each (c) => comments.push(App.Comment.create(c))
blog.set('comments', comments)
this.set('blog', blog)
However, this never seems to add the new comments. What am I doing wrong here?
To properly support Ember's bindings and observers, you need to use KVO aware getters and setters. Just as you use get and set for standard properties, you also have to use special methods for Arrays. In this case, you'd use pushObject. You can see a full list of the functions implemented here: https://github.com/emberjs/ember.js/blob/master/packages/ember-runtime/lib/mixins/mutable_array.js.
I have a site of about 60 tabular report pages. Want to convert this to Zend. The report has two states: empty report and filled in with data report. Each report has its own set of input boxes and select drop downs to narrow down searches. You click on submit and it retrieves the data. Thats all each page does.
Do I create 60 controllers with each one with default index action and getData action? All I have read online do not really describe how to architect a real site.
If the method of fetching and retrieving data is pretty similar as you mention between all 60 reports. It would seem silly to create 60 controllers (+PHP files).
It seems that you are trying to solve this problem with the default rewrite router. You can add a route to the router that will automatically store your report name, and you can abstract and delegate the logic off to some report-runner-business-object-thingy.
$router = $ctrl->getRouter(); // returns a rewrite router by default
$router->addRoute(
'reports',
new Zend_Controller_Router_Route('reports/:report_name/:action',
array('controller' => 'reports',
'action' => 'view'))
);
And then something like this in your controller...
public function viewAction() {
$report = $this->getRequest()->getParam("report_name");
// ... check to see if report name is valid
// ... stuff to set up for viewing report...
}
public function runAction() {
$report = $this->getRequest()->getParam("report_name");
// ... check to see if report name is valid
// Go ahead and pass the array of request params, as your report might need them
$reportRunner = new CustomReportRunner( $report, $this->getRequest()->getParams() );
$reportRunner->run();
}
You get the point; hope this helps!
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!