How can i trigger a onFailure event for g:formRemote? - grails

I am using grails and i have a basic form remote
<g:formRemote name="add" onSuccess="close();" onFailure="dispayErrors();\"
url="[controller: ctx, action: 'insert']" method="POST" class="add">
//Some inputs + submit button
</g:formRemote>
My question is what shoud the insert method return for so that onFailure is triggered?
Thanx

Change the controller action (temporarily I presume) to
def insert = {
render status: HttpServletResponse.SC_INTERNAL_SERVER_ERROR
// This also works
//response.sendError HttpServletResponse.SC_INTERNAL_SERVER_ERROR
}
If you're using Grails 2.0 it is recommended to define your action as a method rather than a closure:
def insert() {
render status: HttpServletResponse.SC_INTERNAL_SERVER_ERROR
}

Related

Grails validation with composite key

I'm running into a complex scenario where we are using a legacy database with composite keys and the client wants to be able alter two of the databases pks, "expenseDate" and "adjustmentNumber". In order to be able to change the pks, I've have to use a HQL query. Now of course this has caused another slew of issues with validation. My work around was to populate a domain so that I could validate against it.
So far everything works fine until we have a validation error that I'd like to return to the UI.
I have the following URL which uses the recoveryDetail controller and the edit action to render the page.
http://localhost:8080/pisr/recoveryDetail/edit?division=ALBANY&peid=PI0003&orgkey=14046701&expenseDate=07-22-2015&adjustmentNumber=1
Edit action
def edit() {
//Parse clean url expense date
params.expenseDate = new SimpleDateFormat('MM-dd-yyyy').parse(params.expenseDate)
def recoveryDetailInstance = RecoveryDetail.get(new RecoveryDetail(params))
if(recoveryDetailInstance == null) {
redirect(uri:'/')
return
}
[recoveryDetailInstance: recoveryDetailInstance, disabled: isdisabled(recoveryDetailInstance.batchOverride)]
}
And the following update action.
Update action
#Transactional
def update() {
params.pk_expenseDate = getDateParser(params.pk_expenseDate)
params.expenseDate = getDateParser(params.expenseDate)
params.adjustmentNumber = getAdjustementNumber(params)
RecoveryDetail recoveryDetailInstance = new RecoveryDetail(params);
recoveryDetailInstance.division = params.pk_division
recoveryDetailInstance.peid = params.pk_peid
recoveryDetailInstance.orgkey = params.pk_orgkey
recoveryDetailInstance .validate()
if(recoveryDetailInstance .hasErrors()) {
flash.message = "test"
respond view: "edit", model:[recoveryDetailInstance:recoveryDetailInstance]
return
} else {
def sqlParams = [
pk_division:params.pk_division,
pk_peid:params.pk_peid,
pk_orgkey:params.pk_orgkey,
pk_expenseDate:params.pk_expenseDate,
pk_adjustmentNumber:params.int('pk_adjustmentNumber'),
approved:YesNoTypes.valueOf(params.approved),
batchOverride:YesNoTypes.valueOf(params.batchOverride),
adjustmentFlag:params.adjustmentFlag,
adjustmentNumber:params.adjustmentNumber,
projectHours:new BigDecimal(params.projectHours),
percentEffort:new BigDecimal(params.percentEffort),
totalHours:new BigDecimal(params.totalHours),
expenseDate:params.expenseDate
]
RecoveryDetail.executeUpdate(recoveryDetailQuery, sqlParams)
}
Edit gsp
<g:form class="form-horizontal" url="[resource:recoveryDetailInstance, action:'update']" method="PUT">
<!-- hidden fields contain params from url (composite key)-->
<g:hiddenField name="pk_division" value="${recoveryDetailInstance?.division}"/>
<g:hiddenField name="pk_peid" value="${recoveryDetailInstance?.peid}"/>
<g:hiddenField name="pk_orgkey" value="${recoveryDetailInstance?.orgkey}"/>
<g:hiddenField name="pk_expenseDate" value="${formatDate(format:'MM/dd/yyyy',date:recoveryDetailInstance?.expenseDate)}" />
<g:hiddenField name="pk_adjustmentNumber" value="${recoveryDetailInstance?.adjustmentNumber}"/>
<div class="row">
<div class="col-md-6">
<g:render template="form" model="[recoveryDetailInstance: recoveryDetailInstance, 'mode':'edit']"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<g:actionSubmit class="btn btn-primary" action="update" disabled="${disabled}" value="${message(code: 'default.button.update.label', default: 'Update')}"/>
</div>
</div>
</g:form>
The problem
When a user causes a validation error that triggers a server side response, I run into the following problems with the different return types.
redirect - this is returning the flash message, but redirects the gsp to the edit action which in turn fires the initializer query and replaces all the form data with the original data.
Example -
redirect (controller:"recoveryDetail", action:"edit", params:["division":params.pk_division, "peid":params.pk_peid, "orgkey": params.pk_orgkey, "expenseDate":params.expenseDate.format("MM-dd-yyyy"), "adjustmentNumber":params.adjustmentNumber])
respond - So I assumed I needed to just use respond, well it resulted in the following.
URL was changed to http://localhost:8080/pisr/recoveryDetail/update dropping all parameters and a 404 page was returned.
Example
flash.message = "test"
respond view: "edit", model:[recoverDetailInstance:recoverDetailInstance]
return
So my question
How do I throw a server side validation error and return it to the page with the data entered by the user?
You may add the parameters back into the redirect or render call.
redirect (..., params: params)
https://grails.github.io/grails-doc/latest/ref/Controllers/redirect.html
Also, I'd suggest using a service instead of the controller method. Services are already transactional. I would also throw an exception if the hasErrors is true. The exception message or object can be your payload back to the user.
Solution was to use render without the use of params like so.
if(recoveryDetailInstance.hasErrors()) {
render view: "edit", model:[recoveryDetailInstance:recoveryDetailInstance]
return
}

Update progress bar in rails using jQuery

I'm a complete novice in JavaScript/jQuery and I believe it's a very simple question; however I'm not being able to accomplish it.
I have an asynchronous task being performed (by sidekiq) and it's progress is available by a method from the model (percentage_complete) that retrieves its progress from Redis.
I want to display a progress bar in model's show view, and I want it to update every x seconds using AJAX.
The progress bar is being displayed like this on the show.html.erb file:
<div class="progress">
<div class="bar" style="width: <%= #model.percentage_complete %>%;"></div>
</div>
How can I set a jQuery script to update this attribute asynchronously?
EDIT
I also have a a :status attribute which is set do "done" when the task is complete. I would like to stop updating when that happens.
By reading my question it appears that I haven't tried nothing and just want someone to write the code for me. Let me add some comments:
I know I should use setInterval to update the attribute every "x" seconds
I know I should use $('.progress .bar').width(<%= #model.percentage_complete %>%) to set the new percentage
However, since I'm not familiar to jQuery and JavaScript, specially in Rails, I'm not sure if this script should be loaded in a view, or if it should be a view itself.
I solved it by creating an action to retrieve the status
# GET /status/1.json
def status
#batch = Batch.find(params[:id])
respond_to do |format|
format.json
end
end
and using the following JavaScript:
<script type="text/javascript">
function progress(){
var progress = setInterval(function() {
var $bar = $('.bar');
var $pct = $('#pct');
$.get("<%= #batch.id %>/status.json", function(data){
if (data.status == "done") {
location.reload();
} else {
$bar.width(data.progress+"%");
$pct.text(data.progress+"%");
}
});
}, 800);
}
$(document).ready(function() {
$.get("<%= #batch.id %>/status.json", function(data) {
if (data.status == "processing") {
progress();
}
});
});
</script>

Grails: Pass Params from Controller to Controller at end of Webflow

Is it possible to pass 'params' in a redirect at the end of a Webflow? Basically the purpose of this variable or param passing from controller to controller is this, I want to have a variable or ${param.xyz} available on the view page only if the flow has been completed.
class Example1Controller{
def startFlow = {
begin {
....
}
....
....
finished {
action {
flash.message = 'success'
}
redirect(controller: 'example2', action: 'myaccount', params: [author: "Stephen King"])
}
}
}
OTHER CONTROLLER
class Example2Controller{
def myaccount() {
def here = $params.author
return [me:here]
}
}
GSP VIEW
<html>
<body>
<g:if test="${params.me}">
<p>This is what I want to display: **${me}**</p>
<p>But it must come from the first controller, from the flow.</p>
</g:if>
</body>
</html>
Basically the purpose of all this variable passing from controller to controller is this. I want to have a variable or ${param.} available on the view page only if the flow has been completed.
You can use hiddenField .
<g:hiddenField name="myField" value="myValue" />
you can pass value from Example1Controller to Example1Gsp(as hideenField) and from that GSP you can get value in your Example2Controller.
If I remember right, we did this before but we used the flow scope/ flow variables. Something like:
def myFlow = {
fin {
redirect: (controller: "xxx", action: "yyy", params: [someValue: flow.someValue])
}
}
Then, in the receiving end, something like:
def yyy = {
[ aaa: params.someValue ]
}

can this be achieved with formRemote and one gsp?

I have create.gsp for creating domain objects. Besides normal html body and headers I have two div's. One for displaying command object errors and one for data. I want to submit the form with AJAX. When I submit the form with
<g:formRemote name="formName" update="errorsDiv" url="[controller:myController', action:'checkAndForward']">
this gets called:
def checkAndForward= {CmdObject p ->
if (p.hasErrors()) {
render(template:"/error",params:params,model :[p:p])
} else {
forward controller: "test", action: "save", params:params
}
}
def save = {
id=myservice.getData()
render(view: "show", id:id)
}
This is working and the save action is called in the controller but ..
The problem is that after 'save' action 'show.gsp' is displayed into where errors div is located.
I would like to have only 'show' page refreshed as if I was calling /save from browser (with posted params of course)

Rendering command validation errors across a redirect

I cannot render the errors from my command object. It does the job well but my .gsp view does not render the errors I raise.
Here is my controller action:
def handleModifyProfile2 = { CreditProviderModificationCommand cpmc -> // bind params to the command object
if (cpmc.hasErrors()) {
flash.message = "Error modifying your profile:"
redirect(action: "modifyProfile", params: [creditProvider : cpmc])
} ...
Here is how I try to render the errors in my .gsp view:
<g:hasErrors bean="${creditProvider}">
<div class="errors">
<g:renderErrors bean="${creditProvider}" as="list" />
</div>
</g:hasErrors>
How can I get the errors to be displayed in the view?
You can't send the command across in a redirect using params. You have a couple options:
render() in the error condition instead of redirect()ing:
if(cpmc.hasErrors()) {
render(view: 'profile', model: [creditProvider: cpmc])
}
This is the most common idiom for what you're doing.
Store the command in the session to persist it across the redirect:
if(cpmc.hasErrors()) {
session.cpmc = cpmc
redirect(...)
}
// and in your action
def cpmc = session.cpmc ?: null
render(view: 'profile', model: [creditProvider: cpmc])
This option is somewhat questionable. If not done correctly, you can pollute the session and leave objects hanging around, taking up memory. If done correctly, though, it can be a decent way to implement a post-redirect-get.
With Grails 3 (I don't know if this worked earlier) it's possible to use the flash for this. According to the documentation, the flash will be "cleared at the end of the next request".
I like to use a pattern like this:
def save(MyDomain myDomain) {
if (myDomain.validate()) {
myDomain.save()
} else {
flash.errors = myDomain.errors
}
redirect(action: 'edit', id: myDomain.id)
}
def edit(MyDomain myDomain) {
if (flash.errors) {
myDomain.errors = (Errors) flash.errors
}
return [myDomain: myDomain]
}
I don't like to use render() for this kind of error handling, because it makes URLs shown in the browser inconsistent with the shown page. This breaks when users set bookmarks, for example.

Resources