How to I render validation results in a Grails Web Flow? - grails

I have a Grails WebFlow that is similar to the following example:
def myFlow = {
init {
action {
def domain = MyDomain.get(params.id)
flow.domain = domain ? domain : new MyDomain()
}
on('success').to 'first'
}
first {
on('continue') {
flow.domain.properties = params
if(!flow.domain.validate()) {
return error()
}
}.to 'second'
}
...
}
Given this example, if a validation error occurs in the transition on('continue') in first:
What's the preferred way to set the model with the invalid domain object so I can use a <g:hasErrors>... in my view (like I would in a normal controller action)?
When I call error(), does it send the flow back to init or to first?
Does error() take any arguments (i.e. a model) that can be used for what I'm trying to accomplish (I can't find much documentation on the error() method).
I'd also take suggestions on how I could improve my flow states to better-facilitate handling these validation errors.
Summary: What's the preferred way to render validation errors within a Grails Web Flow?

-1
What's the preferred way to set the
model with the invalid domain object
so I can use a ... in my
view (like I would in a normal
controller action)?
You just need to return your domain object that has the errors. you can do that in an
action state
action {
user.validate()
return [user:user]
}
You can also set your errors in flash scope. On each transition Grails will copy the content of the flash scope into the ModelView and thus available in your gsp page
action {
flash.error = "your message"
}
-2
When I call error(), does it send the flow back to init or to
first? When you call error it call the
transition you defined for
You should define a handler for such as
on("error").to("handlerError")
Does error() take any arguments (i.e. a model) that can be used for
what I'm trying to accomplish (I can't
find much documentation on the error()
method).
I don't think so but you can do the following to set any variable when transitioning from one state to another
on("error") {
// do Something
}.to("handlerError")
3-
I'd also take suggestions on how I could improve my flow states to
better-facilitate handling these
validation errors.
I use flash for global and form errors but i needed ONE way to deal it. Currently with Grails the flash scope is managed differently in a flow than it's managed in a normal action. So I decided to write a little plugin to change the way the flash scope is handled in a flow and make it consistent with the way is managed in a normal action.
I a gsp page i can use my tags in the follwing way regardless of the action type (normal or flow)
<message:global />
or
<message:inline />
As for Form fields errors, i didn't like to deal with errors in domain objects. I wanted something more unified. So I decided to make them part of the http protocol and I have a javascript component the inject them into the form if i opt to. I found this solution much cleaner than dealing with g:errors each time.
-ken

I've found that one way to do it is to specifically invoke render() in the transition state, providing the model. Here's an example where only one field is validated:
first {
render(view: 'info', model: [flow.domain])
on('continue') {
if(!flow.domain.validate(['myField'])) {
return error()
}
}.to 'second'
}
One can then use <g:hasErrors> as follows:
<g:hasErrors bean="${domain}" field="myField">
<g:renderErrors bean="${domain}" as="list" field="myField"/>
</g:hasErrors>

Related

How can I return an error message in MVC if some part of my action does not work?

I have action methods that do things like this:
productTable.AddOrUpdate(viewModel.Account);
I don't currently test for if it works. Is there an easy way I can check if this works and if it doesn't then give an error message and return to my view?
This is not strictly related to MVC, I think it's a more general issue on how to design code properly to managed unexpected situations (or expected errors generated by user input) and giving appropriate feedback.
For unexpected behaviours we talk about Exception handling. You put your code in a try/catch block, and handle the exception in the catch.
For errors that may be generated by user input we're talking about validation. It means you check your object against a set of rules, and if one or more does not pass, you return a set of errors to your view and display it to the user.
Now for some MVC specific on how to do this. Let's have a bit of sample code:
[HttpPost]
[ModelStateToTempData]
public ActionResult SaveMyObject(MyObject myObject)
{
try
{
if (ModelState.IsValid)
{
MyRepository.Save(myObject);
return RedirectToAction("Success");
}
return RedirectToAction("MyForm");
}
catch (Exception ex)
{
//handle/log your exception as you see fit
ModelState.AddModelError("Name", "Message");
return RedirectToAction("MyForm");
}
}
In this sample we're assuming a few things:
We're following the PRG pattern (you can google for it) and implementing it using the ModelStateToTempData attribute from MvcContrib;
We're using a validation method that kicks in during modelbinding (like dataannotations)
This is a sample method that will receive the post of your form. The data comes from the modelbinder already validated. If the data is valid, you store it and redirect the user to the success page.
If the data is not valid, you redirect him back to the actionmethod that diplays the form. Careful here: do not render the view directly from here: see the PRG pattern for a full explanation of this.
The whole thing is in a try/catch block so if something fails, you can trap/handle the error, and then add a custom error message to the page, then redirect the user to the actionmethod that renders the form and display the error to him.

How to prevent Grails from rendering the default view?

I'm using Grails 1.2.1. I have this method in my controller …
class SocialMediaCacheProxyController {
def index = {
def url = params.url
if (params.dumpAll != null) {
transportCacheService.processCacheDump(request.getRemoteAddr(), response)
} else if (url != null) {
doCacheTransport(request, response)
} // if
}
Problem is, both execution paths write content to the response. However, I think Grails is trying to render a page at the end of the index method, because I repeatedly get the below error after invoking this method …
1339754 [http-8080-4] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/socialmediaproxy].[default] - Servlet.service() for servlet default threw exception
java.lang.IllegalStateException: response.getWriter() called after response.getOutputStream()
at org.codehaus.groovy.grails.web.sitemesh.GrailsPageResponseWrapper$GrailsBuffer.getWriter(GrailsPageResponseWrapper.java:284)
at org.codehaus.groovy.grails.web.sitemesh.GrailsPageResponseWrapper$3.activateDestination(GrailsPageResponseWrapper.java:125)
Any ideas how I can get Grails to stop rendering anything after my method is complete? Thanks, - Dave
If you don't tell Grails what to render, it will render based on convention. In your case, it is looking for an index.gsp. Controllers must return something. That's the whole point. So you can either use the convention and create an index.gsp that gets returned, or you can manually implement the render() method.
http://grails.org/doc/latest/ref/Controllers/render.html
It looks like most of the controller code that works with the ModelAndView keeps an eye on whether or not the ServletResponse has been committed.
I would posit that you could call response.flushBuffer() at the end of your controller action. This will cause the response to be marked as committed, and the controller will not return a ModelAndView.
An alternative would be to call response.outputStream.flush(), which would also result in the response being committed. This is what I usually end up doing after manually working with the response (e.g. for file downloads).
Note: I'm not sure where this falls within the realm of "best practices" - usually you're supposed to rely on the container to handle the flushing of the servlet streams. It's possible that there will be inadvertent side-effects by doing this. You'll probably have to test it out and see what happens with your app and the container you run it in.
I think you're trying to do something counter to the design of an MVC application. Controllers exist to send you somewhere based on parameters. Maybe you could look at using a Service instead, through an AJAX call. Or, if your controller action is changing data that you want to show, you could just redirect back to the page that the call came from. Start here:
http://grails.org/doc/1.0.x/guide/6.%20The%20Web%20Layer.html
http://grails.org/doc/1.0.x/guide/8.%20The%20Service%20Layer.html
I think Grails maybe forwarding to index.gsp, because it does so by default when you don't return a value.
Therefore, i think you should return null at the end of the index-closure to signal grails that you have already written everything you wanted to the output stream.
try this
render (view: "/the name of the page", model: [text:'hi there')
NB
if u want to render a template instead of "view" you put "template"

Grails Web Flow: Redirect to the webflow from another controller action?

Here's what I think I want to do:
class MyController {
def goToWizard = {
if (params.option1)
redirect actionName:'wizard1', params:params
if (params.option2)
redirect actionName:'wizard2', params:params
}
def wizard1Flow = {
start {
action {
// put some values from the params into flow scope
[thingsThatGotPassedIn:params.thingsThatGotPassedIn]
}
on('success').to 'nextThing...'
}
// wizard 1 implementation...
//...
done {
redirect view:'somewhereElse'
}
}
def wizard2Flow = {
start {
action {
// put some values from the params into flow scope
[thingsThatGotPassedIn:params.thingsThatGotPassedIn]
}
on('success').to 'nextThing...'
}
// wizard 2 implementation...
//...
done {
redirect view:'somewhereElse'
}
}
}
I've tried something like this, but I don't seem to ever get into the webflow. Is this a valid approach?
The reason for all this is that I have a gsp that looks like so (a form with 2 submit buttons inside, each one should trigger a different webflow)
<g:form action="goToWizard">
...
<g:submitButton name="wiz1" value="Goto Wizard1"/>
<g:submitButton name="wiz2" value="Goto Wizard2"/>
</g:form>
There are some input elements inside the form that I want to pass the values of into whichever webflow gets called. I'd rather just have the form submit call the appropriate webflow directly (the way all the examples that I've seen work), but there are two webflows, and only one form. How can I accomplish this?
I'm also interested in alternative implementations, if you think this is the wrong way. I'm new to webflows in grails.
Take a look at actionSubmit tag in grails documentation. I think, you should use actionSubmit instead of submitButton
actionSubmit creates a submit button that maps to a specific action, which allows you to have multiple submit buttons in a single form. Javascript event handlers can be added using the same parameter names as in HTML.
By this approach, You no need to mention action in form tag, i.e. No need to make a check in goToWizard. You can send contents directly to your particular action.
Is this the solution to your problem?

MVC how to return instruction to run javascript method?

I am submitting an ajax form and returning a partialresult to the view. However, when validation fails I only want to tell the user why it failed with a js method without reloading the model for the view. How do I specify what to return in the controller?
I'd return a error text/html and check for that on success of the jQuery call and if it exists then just alert it out.
What might be nicer is if you return an error partial view and display that as a modal div giving the user a nice looking error message rather than just an alert.
for that matter you could return html/text and place that within a div on the page and simply show the div modal or otherwise.
i hate the simple alert messages from j/script. grey and ugly with a horrible ding sound. but that's just me.
EDIT
I'm going to make the assumption that you are going to return a partial view representing the error. This is I think the easiest and the best way.
Create an error object somewhere. Below is a very simple object. Add properties as required.
public class MyErrorObj
{
public string errorText{get;set;}
}
Then in your controller you might have the following code. It assumes that you have a partial view called "ErrorDisplay" which inherits from MyErrorObj.
public ActionResult jQueryCheckError()
{
MyErrorObj errObj = new MyErrorObj { errorText = "error happened" };
return PartialView("ErrorDisplay", errObj);
}
Then in your view, not your partial view but your page;
$.post("/MyViewFolder/jQueryCheckError", { PARAMETERS GO HERE }, function(newErrorHTML) {
$(".MyErrorDivContainer").html(newErrorHTML);
$(".MyErrorDivContainer").html(newErrorHTML).slideDown(300);
});
The above posts to your controller action, then when it returns it uses the html that was returned and puts it into a div with a class name of "MyErrorDivContainer".
Now, you can check to see if the returned html is empty and then choose not to display the error message.
You could return a jSON object, itterate through the error collection and render the html to the same div if you wanted to. It's a little more involved but do-able.
Let me know if you want that code as well or if this is enough.
http://devlicio.us/blogs/billy_mccafferty/archive/2009/02/07/beware-of-asp-net-mvc-javascriptresult.aspx
public ActionResult DoSomething() {
script s = "$('#some-div').html('Updated!');";
return JavaScript(s);
}
But I'd suggest returning something like "Json(new { Type = "ValidationError", Errors = ModelState.SelectMany(x => x.Errors)})" - and process it on client side. Or, at least, wrap your returned JavaScript into common class, like "return new JavaScriptError(ModelState)".

How to simiulate a JSON call from an HTTP GET Call?

At this moment I am calling my index view in one of two ways. Using the normal http view and serializing it JSON. In order to test it I am using the code below, and it works. I want to get it with a http get call. Like (http://localhost/article,json or something similar. Any ideas.
$.getJSON("/Article", function(json) {
$.each(json, function(i, article) {
alert(article.title);
});
});
At this moment the index called to /article is being differentiated with the following IsAjaxRequest method. but my real question is if I am able to get around the .getJSON method in JQuery to test the following code.
if (Request.IsAjaxRequest()) {
return Json(articles);
} else {
return View(articles);
}
If you are trying to reuse the same action method for both a full GET (the page load) and an AJAX call (via getJSON), you'll run into issues because each action method should have a unique name. Otherwise, the MVC engine can't tell which action method should be called when a particular Url is requested.
You'll need two separate actions: one for the full page load that returns a ViewResult and the other for the AJAX call that returns a JsonResult. If you need the Urls for these actions to look the same, you can also play around with mapped routes that direct to different action methods.
So, how about:
/Article/Index
Maps to the default Index action (full page load)
/Article/Refresh
Maps to the Refresh action (asynchronous JSON call)
I'm not sure I understand the question correctly, but can't you make an optional parameter called "format", so that you pass in ?format=json to switch what reply type you get back?
if(Request.IsAjaxRequest() || (!string.IsNullOrEmpty(format) && format.Equals("json"))
{
...
}
If you're wondering how to test your action and you're talking about doing automated testing, see if this post answers your question.

Resources