iterate over Grails controller model - grails

Say I have a controller action like the following:
def someAction = {
if (someCondition) {
[foo: 1, bar: 2]
} else {
[foo2: 4, bar2: 6, baz2: 6]
}
}
In someAction.gsp I don't know what the keys of the model are. Is there some way that I can iterate over the keys and values of the model without knowing the key names?

All model attributes are available in the request object. You can iterate this object like this:
<g:each var="item" in="${request}">
${item.key} = ${item.value}<br/>
</g:each>
Note that the request object will hold all request attributes, a lot of information that you're probably not interested in.
Another way to accomplish what you want is putting all your model attributes in one map, like this:
if (someCondition) {
[result:[foo: 1, bar: 2]]
} else {
[result:[foo2: 4, bar2: 6, baz2: 6]]
}
This way you can isolate your attributes from other request attributes. In this case you'll have to iterate your model keys using the result map:
<g:each var="item" in="${result}">

Use pageScope:
<ul>
<g:each var="item" in="${pageScope.variables}">
<li>${item.key} = ${item.value}</li>
</g:each>
</ul>
But note that you won't be able to distinguish between the controller model values and those from the framework.

Related

limit select on certain Domain Class Items

My Domain Classes simplified look like this:
class ScheduleTimeExp{
Data date
}
class ScheduleAction{
ScheduleTimeExp scheduleTimeExp
}
I have a select box in my gsp:
<g:select id="scheduleTimeExp" name="scheduleTimeExp.id" from="${tao.marketing.ScheduleTimeExp.list()}" optionKey="id" required="" value="${tao.marketing.ScheduleAction?.scheduleTimeExp?.id}" class="many-to-one"/>
Rather that letting the user select from all ScheduleTimeExp
from="${tao.marketing.ScheduleTimeExp.list()}"
I would like to show only those ScheduleTimeExp where a relation between ScheduleTimeExp and ScheduleAction does not exist for any other ScheduleAction. In other words only those time expressions which have not yet been selected in another ScheduleAction.
To enforce logic from presentation separation, you want to fill the list inside controller action:
def dataSource
def someAction() {
final Sql sql = new Sql( dataSource )
def scheduleTimeExps = sql.rows( 'select ste.id, ste.date from Schedule_Time_Exp ste where ste.id not in (select sa.Schedule_Time_Exp_id from Schedule_Action sa)' ).collect{ it as ScheduleTimeExp }
[ ..., scheduleTimeExps:scheduleTimeExps ]
}
Note: make sure the case and naming of the tables and columns are correct
and then in gsp:
<g:select .. from="${scheduleTimeExps}"/>

get rid of unwanted params in grails pagination

I have a search webpage where user's can filter the search results by a person's ethnicity, as a checkbox group. There are 12 'ethnicity' checkboxes. The params get passed into g:paginate as the following, so that the user can page through the results and preserve what was checked in the ethnicity checkboxes:
<g:paginate controller="search" action="list" total="${resultCount}" params="${params}"/>
What gets output for the links includes a bunch of unnecessary data for each built URL:
2
I'd like the pagination link URLs to be output without all the extra _ethnicity variables that get passed back in the original search post:
2
How can I get the params into the paginate tag without all the extra unnecessary fields? Functionally it works, but the URLs for the paginate get requests are too long and look hideous.
Try this..,.
<g:paginate controller="search" action="list" total="${resultCount}" params="${params.findAll { it.key == 'ethnicity' && it.value }}"/>
it gives you
2
One of the dirty way to achieve what you want is
<g:paginate controller="search" action="list" params="${
params.findAll { a ->
if (a.value instanceof Collection) {
def c = a.value.findAll { b ->
return b
}
if (c) {
return c
}
} else {
return a.value
}
}
}"/>
EDIT:
spock99 answer is much better than mine, one more way is
params="${params.findAll { !it.key.toString().startsWith("_") }}"
Per the previous user, this works to filter out the extra fields, though it is ugly.
params="${params.findAll { a ->
if (!a.key.toString().startsWith("_")) {
return a.value
}
}
}"
EDIT:
Actually a cleaner way is to put this in the controller:
params.keySet().asList().each { if (it.toString().startsWith("_")) params.remove(it) }
Then in the g:paginate you can stick with
params="${params}"

Grails using query in g:select with service

Fairly new to the Grails model, and having a little trouble getting around using a service for my database transactions.
Service:
class ReportService {
def dataSource
def listDatatypeValues(Datatype dt) {
def sql = new Sql(dataSource)
def list = sql.rows (dt.statement)
return list
}
}
Controller:
def run(Long id) {
def reportInstance = Report.get(id)
def listPromptValues = populatePrompts(reportInstance)
if (!reportInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'report.label', default: 'Report'), id])
return
}
[reportInstance: reportInstance, listPromptValues: listPromptValues]
}
def populatePrompts(Report rp){
//for a prompt in the report, go out and get it's values
rp.prompts.each {
List list = reportService.listDatatypeValues(it.datatype)
}
}
View snippet:
<g:if test="${reportInstance?.prompts}">
<li class="fieldcontain">
<g:each var="prompt" in="${reportInstance.prompts}">
<g:if test="${prompt.datatype.type == 'DropDown'}">
<g:select id="prompt.name" from="${listPromptValues}" name="prompt.name" value="" noSelection="['':'']"/>
</g:if>
</g:each>
</li>
</g:if>
We have a report object, that contains prompts, which in turn contain a datatype. For any given report, when it is pulled up on the UI, it will give the report details, and then list the prompt value for a give prompt. The problem is the current setup is listing the object reference as the prompt value and not the list of values returned from the service.
And example would be Report 1 has 2 prompts, Starting Term Code and Ending Term Code. Both of them use Term Code as the datatype since it is the same SQL query, and the list returned from listDataTypeValues would be a list of 70+ term codes that are stored in the database.
Any thoughts or direction?
I tried following along with this but I can't get it to work.
Thanks!
Your populatePrompts function isn't returning a meaningful value. If you iterate with collectMany instead of each the value of the expression will be the concatenation of all the results from your queries. Try something like this:
def populatePrompts(Report rp){
rp.prompts.collectMany {
reportService.listDatatypeValues(it.datatype)
} //.unique()
}
You may also want to call unique on the result to avoid duplicates in your g:select input.

MVC 2 - Display first result only in for each loop

I've got what appears to be a fairly basic loop:
<% foreach (var item in Model.Items.OrderByDescending(b => b.ItemDateTime)) {%>
Instead of looping through all the items I just want to output the first item, how can I do this?
You can use FirstODefault() method of your collection. Try something like this:
// check if the model contains any item
if (Model.Items.Count() > 0)
{
//show the item...
var firstItem = Model.Items.OrderByDescending(b => b.ItemDateTime).FirstOrDefault();
}
To expand on Felipe's comment, it's better design usually to put things like this in your view models or controllers, not the views.
You could put this on your viewmodel
public Item EarliestItem
{
get { return Items.OrderByDescending(b => b.ItemDateTime).FirstOrDefault(); }
}
Then use this in your view
Or whatever it is you want to do with the earliest item.

Problem with Grails g:each tag

I'm struggling to get a g:each tag to work. What I'm passing to the view is a list of hashmaps (something like this [ [ : ] , [ : ] ] ).
Each hashmap is of the form [location: somelocation , artist: someartist].
The code is the following:
CONTROLLER
in the controller I'm passing the following:
[searchedResults : results.searchedResults]
VIEW
<g:each status="i" in="${searchedResults}" var="results">
if(results.location!=null){
var point${results.location.id} = new google.maps.LatLng(${results.location.lat}, ${results.location.lng});
var myMarkerOptions${results.location.id} = {
position: point${results.location.id},
map: map
};
var marker${results.location.id} = new google.maps.Marker(myMarkerOptions${results.location.id});
}
</g:each>
Any ideas why this wouldn't work?
Thanks!
GrailsGuy is right in that you can't write groovy code in the body of an each tag like that. But let me try and convert it to something for you, since it looks like your doing some javascript in there as well...I think all you need to fix is your if statement
<g:each status="i" in="${searchedResults}" var="results">
<g:if test="${results.location}">
//everything else seems like it would work, assuming your javascript
// code is accurate
</g:if>
</g:each>

Resources