I'm new to grails.I'm using joda plugin for date and time.So i'm displaying date and time in separate column.Now i want to search date using criteria.
I used below code in gsp page to search date.
<tr class="prop">
<td valign="top" class="name">
<label for="createdOn"><g:message code="asset.createdOn.label" default="Created On" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: assetInstance, field: 'createdOn', 'errors')}">
<joda:datePicker name="createdOn" precision="date" value="${assetInstance?.createdOn}" />
</td>
</tr>
i used below code in controller by using criteria.
def search= {
if (request.method == 'POST'){
def criteria = Asset.createCriteria()
def results = criteria {
or {
gt('createdOn','%' + params.createdOn + '%').toDate()
}
}
render(view:'list', model:[ assetInstanceList: results,assetInstanceTotal: Asset.count() ])
}
}
But it showing error.please guide me to solve this problem.
You probably want to bind your search to a command so that you have an actual bound Date object instead of a String parameter.
class SearchCommand {
LocalDate createdOn
...
}
You'll also need to fix your criteria.
def search = { SearchCommand command ->
...
def results = criteria {
or {
gt('createdOn', command.createdOn)
...
}
}
Related
I want to make a select box in grails. I am using 2.1.0. I have a view page which shows a select box named class. But it does not shows any value. The list I have used in the from attribute of select works fine is browser when I render it as json. Can anyone make my combo box work for me please ? Here is my code below :
my view page >>>
<g:form controller="admistratorAction" action="addStudent">
<table class="centerTable">
<div class="height"></div>
<tr>
<td><label>Full Name :</label></td>
<td><g:textField name="fullname" id="fullname" class="field"/></td>
</tr>
<tr>
<td> <label>Admission Class :</label></td>
<td><g:select name="class" id="class" class="field" from="${classList}" noSelection="['':'-Choose a class-']"/></td>
</tr>
<td colspan="2" align="right"><g:submitButton name="createSubmit" value="Create" class="button" onclick="return confirm('Are you sure???')"/></td>
</tr>
</table>
</g:form>
here is my controller >>
package administrator
import common.classes.Classes
import grails.converters.JSON
class AdmistratorActionController {
def addStudent = {
render "add student"
}
def classList = {
def classes = Classes.executeQuery("SELECT c.classes FROM Classes c")
def all_class = [classes : classes]
render all_class as JSON
}
}
You do not need to convert it to JSON in order to have it in gsp page
class AdmistratorActionController {
def addStudent = {
def n = params.fullName
def c = params.class
// do something with them
}
def classList = {
def classes = Classes.list()
// pass details to view 'classList'
[classList : classes]
}
}
Create -> views/administratorAction/classList.gsp
have you form ready and you will be able to get ${classList} in it
On index.gsp I have this to redirect it to list.gsp so I'm imagining is should be something like this:
${response.sendRedirect("entry/list")}
My filter is just one textField and two datePickers with drop down boxes (DD-MMM-YYYY)
and they should be by default filtered from today's date to infinity. So it should show me only the events that have not happened yet, but the old ones should still be in the database.
Here are the links to my last 3 questions for background information
I have a bunch of data and I need a data filter using Grails
Grails: filter data in a Grails table dynamically
Grails: Edit and Delete links not working
I think the second one is the one with most of my code.
Any help would be greatly appreciated and well rated. Thanks! :)
And a again special thanks to proflux for all the help!
UPDATE
Here is the in the list.gsp
<g:each in="${entryInstanceList}" status="i" var="entryInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td><g:formatDate format="dd-MMMM-yyyy" date="${entryInstance.fechaCambio}" /></td>
<td><b>${fieldValue(bean: entryInstance, field: 'proyectoRuta')}</b></td>
<td>${fieldValue(bean: entryInstance, field: 'summary')}</td>
<td><g:formatDate format="dd-MMM-yyyy HH:mm z" date="${entryInstance.lastUpdated}" /></td>
<td>
<g:form>
<g:hiddenField name="id" value="${entryInstance?.id}" />
<span class="simple"><g:actionSubmit class="editar" action="edit" value="${message(code: 'default.button.editar.label', default: ' ')}" /></span>
<span class="simple"><g:actionSubmit class="eliminar" action="delete" value="${message(code: 'default.button.eliminar.label', default: ' ')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Esta seguro que desea Eliminar?')}');" /></span>
</g:form>
</td>
</tr>
</g:each>
UPDATE
Here is the searchResults code,
def searchResults = {
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
and{if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", params.fechaCambioD, params.fechaCambioH)
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
}
render(view:'searchResults', model:['results':results, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
}
And since I am already using results and is a list, what should I do, give it another name and at the end put it to model after results?? like:
render(view:'searchResults', model:['results':results, 'otherResults':otherResults, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
Or should I just define it inside results??
UPDATE
<table><tbody class="yui-skin-sam">
<tr class="prop">
<td valign="top" class="name"><b>Proyecto/Ruta: </b> <g:textField name="proyectoRutaN" value="${proyectoRutaN}" /></td>
<td></td>
<td valign="top" class="name"><b>Fecha de Implementación: </b></td>
<td></td>
<td valign="top" class="name"><b>Fecha de Última Modificación: </b></td>
</tr>
<tr class="prop">
<td></td>
<td valign="top">Desde:</td>
<td valign="top"><gui:datePicker name="fechaCambioD" value="${params?.fechaCambioD}" formatString="dd/MMM/yyyy"/></td>
<td valign="top">Desde:</td>
<td valign="top"><gui:datePicker name="lastUpdatedD" value="${params?.lastUpdatedD}" default="none"/></td>
</tr>
<tr class="prop">
<td></td>
<td valign="top">Hasta:</td>
<td valign="top"><gui:datePicker name="fechaCambioH" value="${params?.fechaCambioH}" formatString="dd/MMM/yyyy"/></td>
<td valign="top">Hasta:</td>
<td valign="top"><gui:datePicker name="lastUpdatedH" value="${params?.lastUpdatedH}" default="none"/></td>
</tr>
For some reason when I apply the filters then it shows me everything on the list (it does not filter) and the textFields in the filter do not save the date from when the filter was applied.
Any ideas on how I can fix this?
For the fechaCambio in the criteria, what you can do is something like this:
def searchResults = {
def fromCal
if(params?.fechaCambioD) {
fromCal = Calendar.getInstance()
fromCal.setTime(param?.fechaCambioD)
fromCal.set(Calendar.HOUR_OF_DAY,0)
fromCal.set(Calendar.MINUTE,0)
fromCal.set(Calendar.SECOND,0)
fromCal.set(Calendar.MILLISECOND,0)
}
def toCal
if(params?.fechaCambioH) {
toCal = Calendar.getInstance()
toCal.setTime(param?.fechaCambioH)
toCal.set(Calendar.HOUR_OF_DAY,23)
toCal.set(Calendar.MINUTE,59)
toCal.set(Calendar.SECOND,59)
toCal.set(Calendar.MILLISECOND,999)
}
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
and{if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", fromCal.getTime(), toCal.getTime())
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
}
render(view:'searchResults', model:['results':results, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':'', 'lastUpdatedH':params?.lastUpdatedH])
}
Again, this is the quick and dirty approach; not very elegant but if I understand your problem it should do what you want.
Instead of doing the redirect on the GSP, it would probably be better to update your index controller method to do something like:
def index {
redirect(action:'list')
}
This way your controller logic is all in the controller and not in both the GSP and controller.
Your second requirement is a little trickier, but not too bad. Basically, you need up update your criteria logic. I'll assume that you only care about two cases:
you have both fechaCambioD and fechaCambioH
you have fechaCambioD and no fechaCambioH (your default - from today until the end of time)
And you don't care about finding all the entries from the beginning of time up until fechaCambioH
If that's the case, change your controller logic to:
def fechaCambioD = params?.fechaCambioD ?: new Date()
def results = entryCriteria.list {
if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", params.fechaCambioD, params.fechaCambioH)
}
else {
gte("fechaCambio", fechaComabioD)
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
So we essentially:
If you didn't get a fechaCambioD parameter, we're going to default fechaCambioD to the current time.
If we don't have both fechaCambioD and fechaCambioH we're going to find all entries that have a fechaCambio greater than the current time.
I believe that should work, but if not let me know...
UPDATE:
Okay, my mistake, there is no gte comaparator, it's just ge for greater than or equal and gt for greater than. Since your search criteria is simpler for list, you can probably just change the method to:
def list = {
def today = Calendar.getInstance()
today.set(Calendar.HOUR, 0)
today.set(Calendar.MINUTE, 0)
today.set(Calendar.SECOND, 0)
today.set(Calendar.MILLISECOND, 0)
def results = Entry.findAllByFechaCambioGreaterThanEquals(today.getTime())
render(view:'list', model:['entryInstanceList':results])
}
If this doesn't work let me know...
I have a table, with a series of events,
name of my class is Entry.
Here is a picture of my table
is in Spanish, but the basics are the same so it shouldn't be a problem.
(the filter HTML code is not yet so its a mess but it works)
and here are the results that I get when I look for the entry.
Now my problem is that I need the results to be shown/filtered into the same table from pic1, so it would be basically like a table update applying the filters.
If you need more info here is the link to my old question. Thanks proflux!
I have a bunch of data and I need a data filter using Grails
Most of the search code is there,
Any help would be greatly appreciated!
UPDATE:
I have a problem filtering the dates though... I have two dates...
One is the date the event is going to take place, and the other one is lastUpdated which I think is a keyword for grails for the last time you modified the Event. Any help related to filtering dates would be greatly appreciated.
I need to show everything on the first table starting from today's date. And if I want to find something from the past I should be able to use the filter to find it.
Any ideas on how to do this?
UPDATE:
So here is my list.gsp
and here is my searchResults.gsp with the filters applied for the word "Ruta"
So basically everything looks nice and pretty but the date filters are not working.
Here is the code in the controller that is not filtering the dates
def searchResults = {
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
if(params?.proyectoRuta) {
ilike("proyectoRuta","%${params.proyectoRuta}%")
}
}
if(params?.day) {
eq("fechaCambio", params.day)
}
render(view:'searchResults', model:['results':results])
}
is filtering the word but not the dates
proyectoRuta would be the title and fechaCambio would be the date shown in the first column. I have not tried to filter the lastUpdated date yet.
UPDATE:
Ok so here is my controller: Since is a lot of code I will only post the important defs
def search = {
render(view:'search')
}
def searchResults = {
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
if(params?.fechaCambioD && params?.fechaCambioH) {
between("fechaCambio", params.fechaCambioD, params.fechaCambioH)
}
if(params?.lastUpdatedD && params?.lastUpdatedH) {
between("lastUpdated", params.lastUpdatedD, params.lastUpdatedH)
}
if(params?.proyectoRutaN) {
ilike("proyectoRuta","%${params.proyectoRutaN}%")
}
}
render(view:'searchResults', model:['results':results, 'proyectoRutaN':params?.proyectoRutaN, 'fechaCambioD':params?.fechaCambioD, 'fechaCambioH':params?.fechaCambioH, 'lastUpdatedD':params?.lastUpdatedD, 'lastUpdatedH':params?.lastUpdatedH])
}
}
And here is the searchResults.gsp
<tbody>
<g:each in="${results}">
<td><g:formatDate format="dd-MMMM-yyyy" date="${it.fechaCambio}" /></td>
<td><b>${it.proyectoRuta}</b></td>
<td>${it.summary}</td>
<td><g:formatDate format="dd-MMM-yyyy HH:mm z" date="${it.lastUpdated}" /></td>
<td>
<g:form>
<g:hiddenField name="id" value="${it?.id}" />
<span class="simple"><g:actionSubmit class="editar" action="edit" value="${message(code: 'default.button.editar.label', default: ' ')}" /></span>
<span class="simple"><g:actionSubmit class="eliminar" action="delete" value="${message(code: 'default.button.eliminar.label', default: ' ')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Esta seguro que desea Eliminar?')}');" /></span>
</g:form>
</td>
</tr>
</g:each>
</tbody>
Try this using the same gsp for both the results and the search criteria...
<html>
<body>
<h1>Search Criteria</h1>
<g:form controller="entry" action="searchResults">
Title: <g:textField name="title" value="${title}" />
Date: <g:datePicker name="startTime" value="${startTime}" precision="day" />
<g:submitButton name="submit" value="Search" />
</g:form>
<g:if test="${results}">
<h1>Search Results</h1>
<table>
<tr>
<th>Title</th>
<th>Start Date</th>
</tr>
<g:each in="${results}">
<tr>
<td>${it.title}</td>
<td>${it.startTime}</td>
</tr>
</g:each>
</table>
</g:if>
</body>
</html>
And then for your controller closures:
def search = {
render(view:'search')
}
def searchResults = {
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
if(params?.title) {
ilike("title", "%${params.title}%")
}
}
render(view:'search', model:['results':results,
'title':params?.title,
'startTime':params?.startTime])
}
This is from a quick demo i wrote really quick, but from the info I gave you on the other question and the Grails Reference Documentation on using criteria queries, you should be able to figure it out. Let me know if you run into any problems.
Update:
Try something like this for your between criteria:
def entryCriteria = Entry.createCriteria()
def results = entryCriteria.list {
if(params?.title) {
ilike("title", "%${params.title}%")
}
if(params?.day) {
between("day", params.day, params.day+1)
}
}
Sounds like you want to show the search results in a 'list' view, so they show up in a table just like when unfiltered. You can just reuse that view and pass in the filtered results as the instance list.
Do you have views for your Entry domain object? If not, generate some scaffolding views with grails generate-view Entry. Then in your controller, make your searchResults method look something like this:
def searchResults = {
def entryInstanceList = Entry.list() // replace with actual filtering logic
render(view:'list', model: [entryInstanceList: entryInstanceList])
}
My problem is this: I want to create a grails domain instance, defining the 'Many' instances of another domain that it has. I have the actual source in a Google Code Project but the following should illustrate the problem.
class Person {
String name
static hasMany[skills:Skill]
static constraints = {
id (visible:false)
skills (nullable:false, blank:false)
}
}
class Skill {
String name
String description
static constraints = {
id (visible:false)
name (nullable:false, blank:false)
description (nullable:false, blank:false)
}
}
If you use this model and def scaffold for the two Controllers then you end up with a form like this that doesn't work;
My own attempt to get this to work enumerates the Skills as checkboxes and looks like this;
But when I save the Volunteer the skills are null!
This is the code for my save method;
def save = {
log.info "Saving: " + params.toString()
def skills = params.skills
log.info "Skills: " + skills
def volunteerInstance = new Volunteer(params)
log.info volunteerInstance
if (volunteerInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'volunteer.label', default: 'Volunteer'), volunteerInstance.id])}"
redirect(action: "show", id: volunteerInstance.id)
log.info volunteerInstance
}
else {
render(view: "create", model: [volunteerInstance: volunteerInstance])
}
}
This is my log output (I have custom toString() methods);
2010-05-10 21:06:41,494 [http-8080-3] INFO bumbumtrain.VolunteerController - Saving: ["skills":["1", "2"], "name":"Ian", "_skills":["", ""], "create":"Create", "action":"save", "controller":"volunteer"]
2010-05-10 21:06:41,495 [http-8080-3] INFO bumbumtrain.VolunteerController - Skills: [1, 2]
2010-05-10 21:06:41,508 [http-8080-3] INFO bumbumtrain.VolunteerController - Volunteer[ id: null | Name: Ian | Skills [Skill[ id: 1 | Name: Carpenter ] , Skill[ id: 2 | Name: Sound Engineer ] ]]
Note that in the final log line the right Skills have been picked up and are part of the object instance. When the volunteer is saved the 'Skills' are ignored and not commited to the database despite the in memory version created clearly does have the items. Is it not possible to pass the Skills at construction time? There must be a way round this? I need a single form to allow a person to register but I want to normalise the data so that I can add more skills at a later time.
If you think this should 'just work' then a link to a working example would be great.
If I use the HTML Select then it works fine! Such as the following to make the Create page;
<tr class="prop">
<td valign="top" class="name">
<label for="skills"><g:message code="volunteer.skills.label" default="Skills" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: volunteerInstance, field: 'skills', 'errors')}">
<g:select name="skills" from="${uk.co.bumbumtrain.Skill.list()}" multiple="yes" optionKey="id" size="5" value="${volunteerInstance?.skills}" />
</td>
</tr>
But I need it to work with checkboxes like this;
<tr class="prop">
<td valign="top" class="name">
<label for="skills"><g:message code="volunteer.skills.label" default="Skills" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: volunteerInstance, field: 'skills', 'errors')}">
<g:each in="${skillInstanceList}" status="i" var="skillInstance">
<label for="${skillInstance?.name}"><g:message code="${skillInstance?.name}.label" default="${skillInstance?.name}" /></label>
<g:checkBox name="skills" value="${skillInstance?.id.toString()}"/>
</g:each>
</td>
</tr>
The log output is exactly the same! With both style of form the Volunteer instance is created with the Skills correctly referenced in the 'Skills' variable. When saving, the latter fails with a null reference exception as shown at the top of this question.
Hope this makes sense, thanks in advance!
Gav
Replace your create.gsp <g:checkbox...> code by:
<g:checkBox name="skill_${skillInstance.id}"/>
Then inside the save action of your controller, replace def volunteerInstance = new Volunteer(params) by :
def volunteerInstance = new Volunteer(name: params.name)
params.each {
if (it.key.startsWith("skill_"))
volunteerInstance.skills << Skill.get((it.key - "skill_") as Integer)
}
Should work. (code not tested)
I would reader send id list of your has many elements because this can be easily assigned by default in Grails.
Your .gsp should look like:
<g:each in="${skills}" var="skill">
<input type="checkbox"
name="skills"
value="${skill?.id}"
</g:each>
and in your controller you can simply stores the value like this:
person.properties = params
person.validate()
person.save()
It's pretty easy, isn't it? :-)
Grails does not provide data-binding support when you use a checkbox and you want to bind ToMany associations. At least, up to version 2.2.0
Workaround ?
1º option - Write gsp code which behaves like a select component
<g:each var="skillInstance" in="${skillInstanceList}">
<div class="fieldcontain">
<g:set var="checked" value=""/>
<g:if test="${volunteerInstance?.skills?.contains(skillInstance)}">
<input type="hidden" name="_skills" value="${skillInstance?.id}"/>
<g:set var="checked" value="checked"/>
</g:if>
<label for="${skillInstance?.name}">
<g:message code="${skillInstance?.name}.label"
default="${skillInstance?.name}" />
</label>
<input type="checkbox" name="skills" value="${skillInstance?.id}"
${checked} />
</div>
</g:each>
2º Create your own TagLib
/**
* Custom TagLib must end up with the TagLib suffix
*
* It should be placed in the grails-app/taglib directory
*/
class BindingAwareCheckboxTagLib {
def bindingAwareCheckbox = { attrs, body ->
out << render(
template: "/<TEMPLATE_DIR>/bindingAwareCheckboxTemplate.gsp",
model: [referenceColletion: attrs.referenceColletion,
value:attrs.value])
}
}
Where <TEMPLATE_DIR> should be relative to the /grails-app/views directory. Furthermore, templates should be prefixed with _.
Now you can use your custom TagLib as follows
<g:bindingAwareCheckbox
referenceCollection="${skillInstanceList}"
value="${volunteerInstance?.skills}"/>
Once done, binding process will occur automatically. No additional code needed.
GSP
<g:checkBox name="skills" value="${skillInstance.id}" checked="${skillInstance in volunteerInstance?.skills}"/>
Groovy
def volunteerInstance = new Volunteer(params).save()
def skills = Skill.getAll(params.list('skills'))
skills.each{ volunteerInstance.addToSkills(it).save() }
I have an application written in grails.
I want to add a new domain class with default initial values.
These values should appear as default or initial values
under the create view.
I mean, the generated inout field tag should have this value as
an attribute.
The class (simplified) look as follows:
class Whatever{
static constraints = {
myString(blank:false, nullable:false)
}
String myString = "hallo"
The generated view looks as follows:
...
<td valign="top" class="value ${hasErrors(bean: whatEverInstance, field: 'myString', 'errors')}">
<g:textField name="serviceReview" value="${fieldValue(bean: whatEverInstance, field: 'myString')}" />
</td>
For some unknown reason when the source of render page has looks as follows:
<td valign="top" class="value ">
<input type="text" name="myString" value="" id="myString" />
</td>
I was expecting value="hallo".
I mean:
<td valign="top" class="value ">
<input type="text" name="myString" value="hallo" id="myString" />
</td>
What am I doing wrong?
Thanks in advance,
Luis
EDIT:
My create method is as follows:
def create = {
def whateverInstance = new Whatever()
whateverInstance.properties = params
return [whateverInstance: whateverInstance]
}
But the create method is called after the form is filled.
int varm
static mapping = {
table 'Test55'
id column: "kid", name:"kid"
version false
varm column: 'varm', name: 'varm', sqlType: 'numeric(1) default 1'
}
This works
Is you whatEverInstance bean being set in the controller's create setup action?
def create = {
[whateverInstance: new Whatever()]
}
You could test the value of whateverInstance in the gsp with:
${whateverInstance}