This is in the gsp
<g:if test="${hasError}">
<div class="errors">
<g:renderErrors bean="${eventInstance}" />
</div>
</g:if>
<g:else >
<div id="messageBox" class="message" style="display:none;">
<g:message code="legalevent.save.success" args="[entityName]" default="Event saved successfully" />
</div>
</g:else>
<g:formRemote name="eventForm" id="eventForm" url="[controller : 'search', action : 'saveLegalEvent']"
update="eventFormDiv" action="${createLink(controller: 'search', action: 'saveLegalEvent')}" method="POST"
onSuccess="jQuery('#messageBox').show()">
I am rendering a page for update with this :
def saveLegalEvent = {
def paramsView = params
def eventPattern = /(event\.).*/
def event = LegalEvent.findByLevId(params["levId"])
def corrTxt = params["corrTxt"] as CorrectionText
if(corrTxt.getCorrId()){
corrTxt = CorrectionText.findByCorrId(corrTxt.getCorrId())
}
event.setCorrTxt(corrTxt)
event.properties = params["event"]
def dataList = []
def hasError = false
def validated = event.validate()
validated &= event.validateHistoryParams()
if(validated)
event.save(flush:true)
else
hasError = true
def errorsView = event.errors
render(view:'leform', model:[attributeDataInstanceList:event.tags, lecInstance:event.leCode, eventInstance:event, hasError: hasError])
}
validateHistoryParams validate some more params that are usually not needed in the domain class.
def validateHistoryParams = { ->
if(!changeRef || !changeRef.trim()) {
this.errors.rejectValue('changeRef', 'event.changeRef.blank')
}
if(!corrTxt || !(corrTxt.corrTxt.trim() || corrTxt.corrId )) {
this.errors.rejectValue('corrTxt', 'event.corrTxt.null')
}
!(this.hasErrors())
}
The problem with all this is that the errors are not rendered in the gsp.
All other tags are rendered fine, when debugging I can see that the errors are actually in the error stack. But in the end, the tag isn't rendering them.
As you can see, there is no redirection, so I can't understand why the errors would somehow be erased between the response creation and the rendering ...
In your Groovy code, parameter returned is named hasError, and GSP checks for hasErrors. I'd recommend not to use extra variables, and just query the bean itself in GSP.
I also believe that you need to have that errors div inside the formRemote element in order to re-render after form submission.
Related
I've added the Active and Archived buttons to this page for extra filtering.
The existing search box functionality uses the following js.coffee which calls the controller index to pull the data from the db.
triggerSearch: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
query_text = $(e.currentTarget).val()
el_id = $(e.currentTarget)[0].id
$.get( "companies", "q[name_cont]": query_text )
), 500, e)
I have added 2 similar js.coffee methods which set an active flag to true or false depending on which button was pressed.
Here is one of those methods.
triggerShowActive: (e) ->
if searchCompanyTimer
window.clearTimeout searchCompanyTimer
searchCompanyTimer = window.setTimeout(( ->
searchCompanyTimer = null
$.get( '/companies', {active: true} )
), 500, e)
here is part of my controller.
class CompaniesController < ApplicationController
respond_to :js, :json, :html
$active_inactive_flag = true
def index
puts "params are: #{params}"
puts "active param is: #{params[:active]}"
puts "#active_inactive_flag pre any conditions is: #{$active_inactive_flag}"
$active_inactive_flag = params[:active] ? params[:active] : $active_inactive_flag
puts "#active_inactive_flag after check on params[:active] is: #{$active_inactive_flag}"
if $active_inactive_flag.try :nonzero?
$active_inactive_flag = true
end
puts "#active_inactive_flag after check if it has a value, else true - is: #{$active_inactive_flag}"
#companies =
Company.search(params[:q])
.result
.order('created_at DESC')
.page(params[:page])
.per(Settings.paginate_limit)
.where(is_active: $active_inactive_flag)
end
here is my index.html.erb file where the buttons and search code is.
<div class="row">
<div class="col-md-3">
<label>Filter By:</label>
<button class="show_active btn btn-primary" style="border-radius:10px">Active</button>
<button class="show_inactive btn btn-primary" style="border-radius:10px">Archived </button>
</div>
<div class="col-md-9">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Filter by name</span>
<input class="form-control" id="q_name_cont" name="q[name_cont]" type="text">
</div>
</div>
</div>
I am using a global variable (tried instance and class variables also) in the controller but it's not working as expected. Sometimes the value in $active_inactive_flag switches from false to true or vice versa incorrectly. I don't want to use a global variable but I am at a loss as to how to combine both filter and search correctly. When the page loads we want Active button to be on by default and return active companies. The problem I am having is knowing what button was pressed when the search box is used.
Edit:
Here is an example of what is happening.
Any direction would be grateful.
Thanks Dave,
in js.coffee added the following:
returnActiveInctiveFlag = true
and then to the triggerSearch updated to:
$.get( "companies", { "q[name_cont]": query_text, active: returnActiveInctiveFlag } )
In my new methods updated returnActiveInctiveFlag accordingly.
Was really overcomplicating it. Thanks again for getting me thinking in the correct way.
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
}
I've just started coding in Rails 4, so I may need very specific instruction. I'm using AJAX to take user input from a text field, modify it (for simplicity's sake I'm just adding " yay!" on the end of it), and return the response as a string to the text field.
I believe I am sending my AJAX request correctly, but something in my controller or view isn't modifying or displaying the result.
Here is my code:
View (solve.html.erb):
<head>
<script>
function myFunction() {
var xmlhttp = new XMLHttpRequest();
var url = "solve";
var params = "field1=" + document.getElementById("field1").value;
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("field1").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET", url + "?" + params, true);
xmlhttp.send();
}
</script>
</head>
<h1>Solve!</h1>
<p>Type it in, type it in.</p>
<div class="col-lg-6">
<div class="input-group">
<input id="field1" type="text" class="form-control" placeholder="1+1">
<span class="input-group-btn">
<button class="btn btn-default" type="button" onclick="myFunction()">Solve</button>
</span>
</div>
</div>
Routes (routes.rb):
resources :equations
#rest of my routes code
get 'solve' => 'equations#solve'
Controller (equations_controller.rb):
class EquationsController < ApplicationController
def solve
if request.post?
new_var = params[:field1]
other_var = "#{new_var} yay!"
return other_var
end
end
end
If you have any questions, I'll get right back to you. Thank you so much for your help! Anything is greatly appreciated.
You're making a GET request but checking if the request is POST in your EquationsController#solve method. Update controller code as follows:
class EquationsController < ApplicationController
def solve
if request.get? # Change to `request.get?`
new_var = params[:field1]
other_var = "#{new_var} yay!"
return other_var
end
end
end
Then the second problem is, you should be writing the output instead of returning it from the controller. As you are expecting text/plain mime type, you should render text instead:
class EquationsController < ApplicationController
def solve
if request.get?
new_var = params[:field1]
other_var = "#{new_var} yay!"
render plain: other_var # Change to `render plain: other_var`
end
end
end
This however, is not the recommended way to handle AJAX requests/responses in Rails. Please refer to: Working with JavaScript in Rails. Also, please see The response object for details on the same.
Try this in myFunction()
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("field1").value = xmlhttp.responseText;
}
I think this works
ok, I think the controller's action is rendering the default 'solve.html.erb' view. so you should return just the value you wish like
class EquationsController < ApplicationController
def solve
if request.post?
new_var = params[:field1]
other_var = "#{new_var} yay!"
render html: other_var
end
end
end
and in js
if (xmlhttp.readyState==4 && xmlhttp.status==200) {
document.getElementById("field1").value = xmlhttp.responseText;
}
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 ]
}
Using markup with the render is not adding the form tag.
I tried this with contentType "text/html", "txt/xml" and it does not work.
I have this in my controller:
def myTest= {
render(contentType: "text/plain") {
div(id:"myDiv") {
p "somess text inside the div"
form (action:'get') {
p "inside form"
}
}
}
And I'm getting this:
<div id='myDiv'><p>somess text inside the div</p><p>inside form</p></div>
I want this:
<div id='myDiv'><p>somess text inside the div</p><form><p>inside form</p></form></div>
Does anybody know why is not adding the form and how to add it?
Thanks,
Federico
i found this problem earlier and the work around was to use the builder directly
def test = {
def sw = new StringWriter()
def b = new MarkupBuilder(sw)
b.html(contentType: "text/html") {
div(id: "myDiv") {
p "somess text inside the div"
b.form(action: 'get') {
p "inside form"
}
}
}
render sw
}
will render the following HTML
<html contentType='text/html'>
<div id='myDiv'>
<p>somess text inside the div</p>
<form action='get'>
<p>inside form</p>
</form>
</div>
</html>