Grails - custom Flash.message rendering - grails

I am running a query to check if an entity id exists for multiple account ids. If the result set is not null, then I need to either throw an error or display a flash message.
The code for the method is as below:
def save() {
def SAMLInfoInstance = new SAMLInfo(params)
def account = Account.get(params?.accountId)
SAMLInfoInstance.setAccount(account)
def samlInfoInstanceList = SAMLInfo.executeQuery("from SAMLInfo " +
" where account.id <> ? " +
" and entityId = ?", [SAMLInfoInstance.accountId, SAMLInfoInstance.entityId])
if (samlInfoInstanceList?.size > 0){
flash.message = message(code: 'test.not.created.message', args: [message(code: 'SAMLInfo.label', default: 'SAMLInfo'), SAMLInfoInstance.entityId])
/*flash.message = "default.not.created.message"
flash.args = ["SAMLInfo", SAMLInfoInstance.entityId]
flash.default = "SAMLInfo cannot be created"
*/
render(view: "create", model: [SAMLInfoInstance: SAMLInfoInstance])
return
}
if (!SAMLInfoInstance.save(flush: true)) {
render(view: "create", model: [SAMLInfoInstance: SAMLInfoInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'SAMLInfo.label', default: 'SAMLInfo'), SAMLInfoInstance.entityId])
redirect(action: "list", id: SAMLInfoInstance.account.id)
}
In my view I render the flash message and the errors in the following manner:
<g:if test="${flash.message}">
<br/>
<div class="message" role="status">
<g:message code="${flash.message}" args="${flash.args}" default="${flash.default}"/>
</div>
<br/>
</g:if>
<br/>
<g:renderErrors bean="${SAMLInfoInstance}" as="list" />
In my message.properties file, I have the following line:
test.not.created.message=The SP url {1} is not allowed for this account. Please enter a different value.
When I run this code, the flash message displays as the string I pass as message i.e. "test.not.created.message". Also, this string is passed on to display whenever I navigate to any other page which displays flash.message.
I am new to grails and groovy and would appreciate any help with this.
Thanks!

2 problems then:
1 - the message is not being retrieved from your message.properties:
You must have other message.properties files in your project. Give it a check. Because if it's not found, grails shows the code itself instead of the message, since it did not find one. Maybe it's looking for your message in some other properties file, like the one specific for your Locale (ex: pt_BR or en_US). Other than that, you're doing it right using the message(code:...) construct.
2 - Your flash message doesn't disappear:
Instead of flash.message, use request.message.

I think the example you want to follow is here.
You only need to resolve the message once ie in the controller or in the view.
So in the controller:
flash.message = "test.not.created.message"
flash.args = ["SAMLInfo"]
flash.default = "<default text>"
And in the view:
<g:message code="${flash.message}" args="${flash.args}"
default="${flash.default}"/>
The flash scope is cleared at the end of the next request which may explain why you still see the message on the next page.

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
}

Grails pagination not working

I've been a week trying to fix this pagination problem, but with no good results,
I have this in my controller
PatientController{
def show(Long id) {
def patientInstance = Patient.get(id)
if (!patientInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'patient.label', default: 'Patient'), id])
return
}
def historyInstance = History.findAllByPatient(patientInstance)
def total = historyInstance.size()
[historyInstance: historyInstance,patientInstance: patientInstance,historyInstanceTotal: total]
}
}
And I have this code on the view
<g:each in="${historyInstance}" var="historyInstances" status="i">
...
</g:each>
<div class="pagination">
<g:paginate total="${historyInstanceTotal}"/>
</div>
I used params, the max on the <g:paginate>, I tried everything but with no result it always shows the whole set of History in the view.
You need to pass your params to the finder you're using, otherwise it won't know how to paginate. The <g:paginate> tag is only for displaying pagination links, not actually performing the pagination. Also the total will need to use a countBy because the result set will be limited to the current page of results.
def historyInstance = History.findAllByPatient(patientInstance, params)
def total = History.countByPatient(patientInstance)

Creating a new entry with preset values

Solved. I really don't know what I did, but it worked. One of those moments...
My domain object is "Actiune". I want to add a new entry with 4 preset fields from another entry that has the given id to "createNewEtapa" method. So I query that entry (with the id) and get it's values, preset them in the form and the hit save but it does not work. The save will not work.
What am I doing wrong ? I've been going around this for hours. Please help again stackOverflow :)
If you don't feel like checking all the code, please just let me know how is this done or link me a example :)
Thx!
These are the relevalt parts of my code
This is my GSP form:
<g:form action="save" enctype="multipart/form-data">
<fieldset class="form">
<g:applyLayout name="form">
<tmpl:/templates/form />
</g:applyLayout>
</fieldset>
<fieldset class="buttons">
<g:submitButton name="create" class="save"
value="${message(code: 'default.button.adauga.label', default: 'Adauga')}" />
<g:actionSubmit name="create" class="save" action="savenew" value="Adauga2" />
</fieldset>
</g:form>
This is my controller:
def create(Long id){
def actiuneInstance = Actiune.findById(id)
def c = Actiune.createCriteria()
def instanceList = c {
eq("idActiune", actiuneInstance.idActiune)
order("termenPornire", "asc")
}
params.idActiune = actiuneInstance.idActiune
params.tema = actiuneInstance.tema
params.firma = actiuneInstance.firma
params.user = User.findById(springSecurityService.currentUser.id)
[instance: new Actiune(params)]
}
def save() {
def actiuneInstance = new Actiune(params)
actiuneInstance.user = User.findById(springSecurityService.currentUser.id)
actiuneInstance.termenPornire = new Date()
def actiuneBD = Actiune.findByIdActiune(params.idActiune)
actiuneInstance.idActiune = actiuneBD.idActiune
actiuneInstance.tema = actiuneBD.tema
actiuneInstance.firma = actiuneBD.firma
print "in params " + params
print "\\"
print "in save... " + actiuneInstance.properties
if (!actiuneInstance.save(flush: true)) {
render(view: "create", model: [firInstance: actiuneInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'actiune.label', default: 'Actiune'), actiuneInstance.idActiune])
redirect(action: "completeShow", id: actiuneInstance.id)
}
This is the output from the print statements in the save() method
*...*
in params [termenLimita_month:11, termenPornire_month:11, termenPornire_day:1, documentPath:, tema:Promotie A, termenLimita_day:1, _action_savenew:Adauga2, id:, observatii:uuu, termenLimita:date.struct, termenPornire_year:2012, etapa.id:1, etapa:[id:1], contact.id:1, contact:[id:1], termenPornire:date.struct, firma:Google, idActiune:zt8h, termenLimita_year:2012, action:save, controller:actiune]
\
....
in save... [user:Flavian, termenPornire:Thu Nov 01 22:57:45 EET 2012, contact:null, documentPath:null, temaId:1, etapaId:null, firmaId:3, firma:Google, contactId:null, termenLimita:null, idActiune:zt8h, observatii:null, userId:null, etapa:null, tema:Promotie A]
Call actiuneInstance.validate() and then look at the actiuneInstance.errors object to see the result of any validation errors on your object.

Grails code fails when updating (works with a new object) a List with a no matching editors or conversion strategy found

I have a domain object User that
static hasMany = [following:User]
When I create a new User I can select the users I want to follow in my multi select and it works fine but if I then update my User and change the 'following' list it errors on binding the parameters with
java.lang.IllegalStateException: Cannot convert value of type
[org.codehaus.groovy.grails.web.binding.ListOrderedSet] to required
type [java.util.Set] for property 'following': no matching editors or
conversion strategy found
at UserController.update(UserController.groovy:76) at
java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:68
gsp code
<div class="clearfix ${hasErrors(bean: userInstance, field: 'following', 'error')} ">
<label for="following">
<g:message code="user.following.label" default="Following" />
</label>
<div class="input">
<g:select name="following" from="${com.social.User.list()}" multiple="multiple"
optionKey="id" size="5" value="${userInstance?.following*.id}" class="many-to-many" />
</div>
</div>
Here is the controller code for the update:
def update() {
def userInstance = User.get(params.id)
if (!userInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), params.id])
redirect(action: "list")
return
}
if (params.version) {
def version = params.version.toLong()
if (userInstance.version > version) {
userInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'user.label', default: 'User')] as Object[],
"Another user has updated this User while you were editing")
render(view: "edit", model: [userInstance: userInstance])
return
}
}
userInstance.properties = params
// It will work if I blacklist the following param as below
//bindData(userInstance, params, ['following'])
if (!userInstance.save(flush: true)) {
render(view: "edit", model: [userInstance: userInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'user.label', default: 'User'), userInstance.id])
redirect(action: "show", id: userInstance.id)
}
I currently have a workaround of
// Auto saving of following seems to fail so blacklist following param
// then set the list manually...
bindData(userInstance, params, ['following'])
userInstance.following = User.getAll(params.get("following") as List<BigInteger>)
in the update within the user controller but seems like there could be a better way???
I had this problem, and it was ultimately caused by the json-rest-api plugin that didn't handle correctly hibernate proxies obtained from the database.
See https://github.com/padcom/grails-json-rest-api/issues/14

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