createLink expression in <g:select> tag - grails

Grails newbie - I'm trying to create URIs for the value attributes in my select markup (iterating over each object of a domain class). I tried using the createLink tag in my tag like so:
<g:select name="project.id" from="${Project.list(sort:'start', order:'desc')}" optionValue="${createLink(controller:'project',action:'show')}/${it.id}" noSelection="['null': 'select project']/>
Obviously I get a GSP exception, which explains that "/[mycontroller]/[myaction]/null" is not a property of the object.
Is there any other way of constructing these URI's inside a <g:select> (e.g. <option value="/my/uri/"> without resorting to a loop and constructing the values "manually"?

It can be easily done by adding an extra method to Project domain:
class Project {
static transients = ['optionValue']
String getOptionValue() {
def g = ApplicationHolder.application.mainContext.getBean(
'org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
return g.createLink(controller:'project',action:'show', id: id)
}
}
and using it as:
<g:select name="project.id" from="${Project.list(sort:'start', order:'desc')}" optionValue="optionValue" noSelection="['null': 'select project']" />

Related

Grails : Use value from session in drop down list option

In my controller:
def billingDetails() {
def traineeDetails = session.traineeDetais
println "session data::"+traineeDetails
[traineeNames:traineeDetails.name]
}
This prints: [numberOfTrainees:2, submit_trainee_details:Next: Billing Details �, phone:[999999, 99999], email:[tester1#test.com, tester2#test.com], name:[Jack, Rob], jobTitle:[SE, SE], action:processTraineeDetails, controller:trainingOrder]
now in my GSP i want to have a select tag which will have name (jack, Rob) as options
<g:select name="traineeName"
from="${traineeNames}"
value=""
/>
which is not working fine.. How to make this to work so that i will get the names as options in dropdown list
make use of the optionKey optionValue fields in your g:select
reference:
http://grails.org/doc/2.2.1/ref/Tags/select.html
and do something like
<g:select optionKey="value" optionValue="value"
name="traineeName" from="${traineeNames}" />
I don't know the exact problem but one possible reason is that your session.traineeDetais.name is not a list. Check weather session.traineeDetais.name is a list or not, like
println "Check: ${session.traineeDetais.name instanceof List}"
if it is list then your code should work and if it is string then your drop down contain string characters as values.

how to pass params using action button in grails

been having toruble with the button that has action. I have several btns which I want to know its paramaeter. In grails tutorial it says it should be like this:
<g:actionSubmit action="action" value="${message(code: 'default.button.edit.label', default: 'Edit')}" params="['actionTaken':editPhone]"/>
I tried using remotelink, submitButton, submitToRemote tags but none works. I always get null when I try parsing it in my controller:
def action=
{
def actionTaken = params.actionTaken
def employeeId= params.employeeId
MySession session = MySession.getMySession(request, params.employeeId)
profileInstance = session.profileInstance
switch(actionTaken)
{
case "editPhone" :
isEditPhone=true
break
case "editEmail" :
isEditEmail=true
break
}
render(view:"profile", model:[profileInstance:session.profileInstance, isEditPhone:isEditPhone, isEditEmail:isEditEmail])
}
What am I missing? is my params code wrong? Is my code in parsing params wrong? this just gets me in circles with no progress. help. thanks.
The Grails documentation doesn't list params as one of the attributes accepted by actionSubmit.
It is possible to inject the value you want in your params list in the controller by exploiting what that tag actually does:
def editPhone = { forward(action:'action', params:[actionTaken: 'editPhone'])}
def editEmail = { forward(action:'action', params:[actionTaken: 'editEmail'])}
You may also just want to just code completely separate logic into the editPhone and editEmail actions if that makes your code cleaner.
Updated View Code:
<g:actionSubmit action="editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" />
The params attribute is parsed as a map, where the keys are treated a strings but the values are treated as expressions. Therefore
params="['actionTaken':editPhone]"
is trying to define the key named actionTaken to have the value from the variable named editPhone in the GSP model. Since there is no such variable you're getting null. So the fix is to move the quotes:
params="[actionTaken:'editPhone']"
which will set the value to the string "editPhone".
You could also pass the parameter inside the POST-data using
<input type="hidden" name="actionTaken" value="editPhone" />
inside the form. Then it is also accessible through the params variable.
It works for me.
I just had a similar problem (I needed to submit a delete together with an id) and found a solution using HTML5's "formaction" attribute for submit-inputs.
They can be given a value that can include a controller, action, additional parameters, etc.
In General, to add a parameter to a submit button such as a edit of a specific sub-object on a form would looks like this:
<input type="submit" formaction="/<controller>/<action>/<id>?additionalParam1=...&additionalParam2=..." value="Action" >
and in your example:
<input type="submit" formaction="action?actionTaken=editPhone" value="${message(code: 'default.button.edit.label', default: 'Edit')}" >
In my situation, I had a single form element with multiple actions on each row of a table (i.e. data table edit buttons). It was important to send the entire form data so I couldn't just use links. The quickest way I found to inject a parameter was with javascript. Not ideal, but it works:
function injectHiddenField(name, value) {
var control = $("input[type=hidden][name='" + name + "']");
if (control.size() == 0) {
console.log(name + " not found; adding...");
control = $("<input type=\"hidden\" id=\"" + name + "\" name=\"" + name + "\">");
$("form").append(control);
}
control.val(value);
}
Your button can look like this:
<g:each in="${objects}" var="object">
...
<g:actionSubmit value="edit" action="anotherAction"
onclick="injectHiddenField('myfield', ${object.id})"/>
</g:each>

a list of checkboxes

I have two domain classes
class Contract {
String number
static hasMany = [statements:Statement]
}
class Statement {
String code
static hasMany = [contracts:Contract]
}
I would like to show all statements available in my gsp with a checkbox next to each, allowing the user to choose which statements are applicable to the contract. So something like:
[ ] Statement Code 1
[ ] Statement Code 2
[ ] Statement Code 3
I started off with this:
<g:each in="${Statement.list()}" var="statement" status="i">
<g:checkBox name="statements[${i}].id" value="${statement.id}" checked="${contractInstance.statements.contains(statement.id)}" />
<label for="statements[${i}]">${statement.code}</label>
</g:each>
But i just cannot get a list of checked statements to the controller (there are null elements in the list, there are repeated statements...).
Any idea how to achieve this?
This is possible, but it does require a bit of a hack. First off, every checkbox must have the same name, "statements":
<g:each in="${org.example.Statement.list(sort: 'id', order: 'asc')}" var="statement" status="i">
<g:checkBox name="statements" value="${statement.id}" checked="${contract.statements.contains(statement)}" />
<label for="statements">${statement.content}</label>
</g:each>
Second, in the controller you have to remove the "_statements" property before binding:
def contract = Contract.get(params.id)
params.remove "_statements"
bindData contract, params
contract.save(failOnError: true)
The check box support hasn't been designed for this use case, hence the need for a hack. The multi-select list box is the one typically used for this type of scenario.
I personally prefer to get the list of Id's in this case.
<g:each var="book" in="${books}">
<g:checkBox name="bookIds" value="${book.id}" ...
</g:each>
Command Object:
class BookCommand {
List<Serializable> bookIds
}
In controller action:
BookCommand bc ->
author.books = Book.getAll(bc.bookIds)
Change the checkbox to something like this.
<g:checkBox name="statements.${statement.id}" value="true" checked="${contractInstance.statements.contains(statement)?:''}" />
and then in the controller, in params.statements you will get a list with the IDs of the checked statements.
Also notice the ?:'' in the checked property, it's a good idea to add it because any value(even 'false') in the checked property is interpreted as checked.
Are you mapping request directly to Contract? It's much more secure to map incoming request into an Command object.
As about mapping a list - values are mapped only to existing elements. I mean it cannot create new list elements. You need to prepare it before mapping. If you know that there is always 3 elements, you can make:
class ContractCommand {
List statements = [
new Statement(),
new Statement(),
new Statement(),
]
}
and map request to this object

Grails 2.0 update select with submitToRemote and render

I face a problem using Grails 2 submitToRemote tag.
The following code is what I use in the controller:
def getProposal = {
def layouts = importService.getLayoutsFor(params.product as int)
render(contentType: "text/xml") {
for (layout in layouts) {
option("${layout}")
}
}
}
and in the GSP:
<g:submitToRemote action="getProposal" update="layouts"
onLoading="showProgress();" onComplete="hideProgress();"
value="Do It" />
<select id="layouts" name="layout" required="">
</select>
Using jquery this results in:
showProgress();;jQuery.ajax({type:'POST',data:jQuery(this).parents('form:first').serialize(), url:'/app/controller/getProposal',success:function(data,textStatus){jQuery('#layouts').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){},complete:function(XMLHttpRequest,textStatus){hideProgress();}});return false
which not works and returns am error:
Node cannot be inserted at the specified point in the hierarchy
But if I use another render method like:
render(status: 0, text: "<option value='1'>Layout 1</option>")
it works.
In both cases the expected answer is transmitted back.
I did not understand why it will not work with the first nicer method. Could anyone explain what I do wrong?
Thx
Edit:
I noted that if I use render(contentType: "text/text") instead it will work. May be it has something to do, that the xml is not properly formatted (no root node?). But why does it work in Grails 1.3.7?
Grails 1.3.7 had a different default javascript provider (prototype). The jquery ajax call trys to infer the type of the response based on what it receives

How can I change the way GRAILS GSP fieldValue formats Integers?

I have a field in my domain object which I define as an Integer...
Integer minPrice
I then access it in a GSP page as follows:
${fieldValue(bean: myBean, field: 'minPrice')}
and what I get in my HTML is...
100,000
which is not an Integer, it's a String. Worse still it's a formatted String in a particular locale.
This is a problem because I have a SELECT control on an HTML FORM which has a (non-ordinal) range of values for minPrice which I want to store in my domain object as integers, and I don't want to store an index to some array of values that I have to repeatedly map back and forth between, I want the value itself.
My select control looks like this...
<g:select name="minPrice"
value="${fieldValue(bean: personInstance, field: 'minPrice')}"
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':100000],
['name':'200,000', 'id':200000],
['name':'300,000', 'id':300000]
]}"
optionKey="id" optionValue="name"
/>
When I get the value from the SELECT field to post back to the server it correctly has an Integer value, which I persist. However the return trip never pre-selects the right row in the drop-down because the value is this comma separated String.
This works fine elsewhere in my code for small numbers where the comma formatting doesn't come into play, and the round-trip in and out of the SELECT is successful. But values >999 don't work.
The docs say "This tag will inspect a bean which has been the subject of data binding and obtain the value of the field either from the originally submitted value contained within the bean's errors object populating during data binding or from the value of a bean's property. Once the value is obtained it will be automatically HTML encoded."
It's that last bit that I want to avoid as it appears to format Integers. So, what little bit of Grails/GSP magic do I need to know so I can get my Integer to be rendered as an integer into my SELECT and pre-select the right row?
EDIT:
I have tried some further things based on the answers below, with pretty disappointing results so far...
If I put the <gformatNumber/> tag in my <g:select/> I get the page code as text in the browser.
<g:select name="minPrice"
value='<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />'
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':100000],
['name':'200,000', 'id':200000],
['name':'300,000', 'id':300000],
]}"
optionKey="id" optionValue="name"
/>
Using the number format tag from GSP on my Integer value of 100000 like this...
var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;
gives 100. Remember that the fieldValue gives back 100,000, so this is not a surprise.
If I use the jsp taglib like this...
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
var y = <fmt:formatNumber value="${fieldValue(bean: personInstance, field: 'minPrice')}" pattern=".00"/>;
I get an error from the page compiler Cannot format given Object as a Number.
I guess I have a wider concern than I can't seem to get an Integer value as a genuine integer into my code if it is greater than 999 because of the default (and unconfigurable) behaviour of the fieldValue directive. However my specific problem of not being able to pre-select an Integer value in a SELECT control is not going away. At the moment I'm at a bit of a loss.
Anyone have any further ideas?
Do you want to show the raw number? like 100000?
You can get the field directly:
${myBean.minPrice}
I think you have at least two possible solutions.
One is to use the JSTL taglib as described in the docs.
Another, cooler way is to use the 'formatNumber' tag included with grails - also in the docs.
For your purpose, the use of that tag might look like this:
<g:formatNumber number="${fieldValue(bean: myBean, field: 'minPrice')}" format="######" />
Use the 'groupingUsed' attribute in combination with your format:
<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}"
format="#"
groupingUsed="true" />
Better use custom PropertyEditor in order not to bother with formatNumber tag every time you output a value.
Like, declare a bean in resources.groovy:
myOwnCustomEditorRegistrar(CustomEditorRegistrar)
And create your class:
class CustomEditorRegistrar implements PropertyEditorRegistrar {
void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(BigDecimal.class, new MyBigDecimalEditor(BigDecimal.class))
}
}
Change
var x = <g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}" format="#" />;
to
var x = <g:formatNumber number="${personInstance.minPrice}" format="#" />;
I found the best way to handle this was doing what Victor Sergienko (upped btw) hinted at with using a PropertyEditor.
Create an editor for Integer, put in src/groovy:
class IntegerEditor extends PropertyEditorSupport {
void setAsText(String s) {
if (s) value = s as Integer
}
public String getAsText() {
value
}
}
and register it using a PropertyEditorRegistrar (also in src/groovy):
class MyEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry reg) {
reg.registerCustomEditor(Integer, new IntegerEditor())
}
}
add your registrar into the spring config (grails-app/conf/spring/resource.groovy):
beans = {
customEditorRegistrar(MyEditorRegistrar)
}
From now on any Integers that are bound, receive errors (or not) and then redisplayed with the fieldValue tag should be displayed by Integer's default toString - you can customise this behaviour in the editor by amending the getAsText implementation.
Personally I would create a wrapper for this kind of thing so you can set up an editor just for that type rather than across the board for a frequently used type. Though I realise this would mean a little bit of mapping when persisting to the DB...
I have a solution/work-round... The answer seems to be, "do nothing".
Instead of trying to parse the stringified number back into an integer, I left it as a formatted string for the purposes of the select. This meant I had to change my from values as follows:
<g:select name="minPrice"
value="${fieldValue(bean: personInstance, field: 'minPrice')}"
onchange="setDirty()"
noSelection='${['0':'Select a number...']}'
from="${[
['name':'100,000', 'id':'100,000'],
['name':'200,000', 'id':'200,000'],
['name':'300,000', 'id':'300,000']
]}"
optionKey="id" optionValue="name"
/>
Of course when I post back to the server the value that gets sent is "100,000" as an escaped String. What I realised was that Grails, or Spring, or Hibernate, or something in the stack, would do the coersion of the String back into the right Integer type prior to persistence.
This works just fine for my purposes, however I think it is basically a work-round rather than a solution because of locale issues. If my thousand separator is a "." and my decimal separator is ",", which it is for much of Europe, then my code won't work.
Use like this :
<g:formatNumber number="${fieldValue(bean: personInstance, field: 'minPrice')}"
format="#.##"/>;

Resources