grails controllers errors - grails

I have this DomainClass:
package cm
class XXX{
String city
String website
static constraints = {
city(nullable: true)
website(nullable: true)
}
static mapping = {
id column:'xxx_id'
city column: 'xxx_city'
website column: 'xxx_website'
table "xxx_XXX"
version false
}
}
The Controller:
class ConferenceController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[XXXInstanceList: XXX.list(params), XXXInstanceTotal: XXX.count()]
}
def save = {
def XXXInstance= new XXX(params)
if (!XXXInstance.save(flush: true)) {
render view: 'add', model: [XXXInstance: XXXInstance]
return
}
//flash.message = "${message(code: 'default.created.message', args: [message(code: 'person.label', default: 'Person'), personInstance.id])}"
redirect(uri:"/index.gsp")
}
}
and my add.gsp page:
<head>
<title> xXXx</title>
<meta name="layout" content="main2" />
</head>
...
<g:form controller="XXX" action="save">
<g:hasErrors bean="${XXXInstance}">
<div class="errors">
<g:renderErrors bean="${XXXInstance}" as="list" />
</div>
</g:hasErrors>
year
<g:textField name="year" /><br>
website
<g:textField name="website" /><br>
<g:submitButton name="save2" value="Save" />
</g:form></div>
</div>
...
</body>
With this current code, everything works fine, but when a contraint is failed, it is showns the corresponding error but the written values are gone.. I cant figure this out - i'v tried a lot of things, but im sure the solution is as easy as my question -.- Please help.

I think you should check the *.properties file in YOUR_PROJECT\grails-app\i18n folder. They have the definition for translation. Investigate it for a time and refer to the document if need, you will understand how Grails perform translation.
And uncomment the following line in your controller:
//flash.message = "${message(code: 'default.created.message', args: [message(code: 'person.label', default: 'Person'), personInstance.id])}"

Related

Passing parameters to a form grails

I am new to grails I am trying to pass an object Id to a form. I have domains called Beer and recipe. Beer hasMany recipes and recipe belongsTo beer. I want to create a link to a new recipe from the beer show page passing the beer id in the url in order to create the association. Right now I have been trying to use this link <g:link controller="recpie" action="create" params="[beerid: selectedBeer.id]">Create Recipe</g:link> and retrieve it in the form as a hidden field to submit with the recipe form using <g:field type="text" name="beer.id" value="${beer}"/>.(I know that the hidden property is not set). What am I doing wrong? Also is there a better way to create this association?
Beer Domain Class
class Beer {
String name
String style
Recipe recipe
String toString() {
name
}
static hasMany = [recipe : Recipe]
static constraints = {
}
}
Recipe Domain Class
class Recipe {
String name
Float grainAmount
String yeast
float boilTime
Float hopAmount
float og
float fg
float ogTemp
float fgTemp
float fermTime
Beer beer
static belongsTo = [ beer: Beer]
static constraints = {
beer nullable: true
}
String toString() {
name
}
}
Beer Controller
package com.example
import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional
#Transactional(readOnly = true)
class BeerController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Beer.list(params), model:[beerCount: Beer.count()]
}
def show(Beer beer) {
respond beer
}
def create() {
respond new Beer(params)
}
#Transactional
def save(Beer beer) {
if (beer == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
if (beer.hasErrors()) {
transactionStatus.setRollbackOnly()
respond beer.errors, view:'create'
return
}
beer.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'beer.label', default: 'Beer'), beer.id])
redirect beer
}
'*' { respond beer, [status: CREATED] }
}
}
def edit(Beer beer) {
respond beer
}
#Transactional
def update(Beer beer) {
if (beer == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
if (beer.hasErrors()) {
transactionStatus.setRollbackOnly()
respond beer.errors, view:'edit'
return
}
beer.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'beer.label', default: 'Beer'), beer.id])
redirect beer
}
'*'{ respond beer, [status: OK] }
}
}
#Transactional
def delete(Beer beer) {
if (beer == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
beer.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'beer.label', default: 'Beer'), beer.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'beer.label', default: 'Beer'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Recipe Controller
package com.example
import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional
#Transactional(readOnly = true)
class RecipeController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Recipe.list(params), model:[recipeCount: Recipe.count()]
}
def show(Recipe recipe) {
respond recipe
}
def create() {
respond new Recipe(params)
}
#Transactional
def save(Recipe recipe) {
if (recipe == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
if (recipe.hasErrors()) {
transactionStatus.setRollbackOnly()
respond recipe.errors, view:'create'
return
}
recipe.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'recipe.label', default: 'Recipe'), recipe.id])
redirect recipe
}
'*' { respond recipe, [status: CREATED] }
}
}
def edit(Recipe recipe) {
respond recipe
}
#Transactional
def update(Recipe recipe) {
if (recipe == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
if (recipe.hasErrors()) {
transactionStatus.setRollbackOnly()
respond recipe.errors, view:'edit'
return
}
recipe.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'recipe.label', default: 'Recipe'), recipe.id])
redirect recipe
}
'*'{ respond recipe, [status: OK] }
}
}
#Transactional
def delete(Recipe recipe) {
if (recipe == null) {
transactionStatus.setRollbackOnly()
notFound()
return
}
recipe.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'recipe.label', default: 'Recipe'), recipe.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'recipe.label', default: 'Recipe'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}
Beer/Show GSP
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'beer.label', default: 'Beer')}" />
<title><g:message code="default.show.label" args="[entityName]" /></title>
</head>
<body>
<g:message code="default.link.skip.label" default="Skip to content…"/>
<div class="nav" role="navigation">
<ul>
<li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
<li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
<li><g:link class="list" action="create" resource="recipe">New Recipe</g:link></li>
</ul>
</div>
<div id="show-beer" class="content scaffold-show" role="main">
<h1><g:message code="default.show.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:link controller="recpie" action="create" resource="recipe" params="[beerid: selectedBeer.id]">Create Recipe</g:link>
<f:display bean="beer" />
<g:form resource="${this.beer}" method="DELETE">
<fieldset class="buttons">
<g:link class="edit" action="edit" resource="${this.beer}"><g:message code="default.button.edit.label" default="Edit" /></g:link>
<input class="delete" type="submit" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" />
</fieldset>
</g:form>
</div>
</body>
</html>
** Recipe/Create GSP**
<%# page import="com.example.Beer" %>
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'recipe.label', default: 'Recipe')}" />
<title><g:message code="default.create.label" args="[entityName]" /></title>
</head>
<body>
<g:message code="default.link.skip.label" default="Skip to content…"/>
<div class="nav" role="navigation">
<ul>
<li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
<li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
</ul>
</div>
<div id="create-recipe" class="content scaffold-create" role="main">
<h1><g:message code="default.create.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:hasErrors bean="${this.recipe}">
<ul class="errors" role="alert">
<g:eachError bean="${this.recipe}" var="error">
<li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li>
</g:eachError>
</ul>
</g:hasErrors>
<g:form action="save" name="recipeForm">
<fieldset class="form">
Name:
<g:field type="text" name="name"/>
Grain Amount:
<g:field type="text" name="grainAmount"/>
Yeast:
<g:field type="text" name="yeast"/>
Boil Time:
<g:field type="text" name="boilTime"/>
Hop Amount:
<g:field type="text" name="hopAmount"/>
OG:
<g:field type="text" name="og"/>
FG:
<g:field type="text" name="fg"/>
OG Temp:
<g:field type="text" name="ogTemp"/>
FG Temp:
<g:field type="text" name="fgTemp"/>
Ferment Time:
<g:field type="text" name="fermTime"/>
beer:
<g:hiddenField name="beerid" value="${params.beerid}" />
</fieldset>
<fieldset class="buttons">
<g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
</fieldset>
</g:form>
</div>
</body>
</html>
You've submitted the beer ID from the beer show paged named beerid so you need to retrieve it from the params on the recipe create page as such:
beer - show.gsp
<g:link controller="recipe" action="create" resource="recipe" params="[beerid: beer.id]">Create Recipe</g:link>
recpie - create.gsp
<g:hiddenField name="beer.id" value="${params.beerid}"/>

HTML5 drag and drop multi-file upload grails plugin

i'm using HTML5 drag and drop multi-file upload plugin, to upload some files in my Grails application , but i want to save the uploaded files to DB but i don't know what is the object that is holding the uploaded files i searched in request and params , here is my the tag in the _form view:
<uploadr:add name="myUploadrName" controller="photos" action="save" direction="up" maxVisible="8" unsupported="/my/controller/action" rating="false" voting="false" colorPicker="false" maxSize="204800" />
here is the create view :
<g:form url="[resource:photosInstance]" enctype="multipart/form-data"><fieldset class="form">
<g:render template="form"/>
</fieldset>
<fieldset class="buttons">
<g:submitButton id = "submitBtn" name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
</fieldset>
</g:form>
here is the save action :
def save(Photos photosInstance) {
if (photosInstance == null) {
notFound()
return
}
if (photosInstance.hasErrors()) {
respond photosInstance.errors, view:'create'
return
}
request.getFileNames().each{
request.getFiles(it).each { file ->
// loop through all files selected
println "name: $file.name Originalfilename: $file.originalFilename contentType: $file.contentType"
photosInstance= new Photos(photo:file.bytes).save(failOnError:true)
}
}
/*request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [
message(code: 'photos.label', default: 'Photos'),
photosInstance.id
])
redirect photosInstance
}
'*' { respond photosInstance, [status: CREATED] }
}*/
}
A little hard to help when I don't have the controller code, but try this:
UploadedFile uploadedFile = UploadedFile.get(params.fileId)
if (!uploadedFile) response.sendError(404, "No uploaded file could be found matching id: ${params.fileId}.")
GridFSFile gridFSFile = uploadedFile.file
if (!gridFSFile) response.sendError(404, "No file attached to UploadedFile")

Multipart post in grails causes 404 error

I try to upload data to a grails app. This works very good and the object is created in the database and the uploaded document is also present. Unfortunately i get a 404 error directly after the creation.
I am using grails 2.3.5 with the following code:
Action to save:
#Transactional
def save(Book bookInstance) {
if (bookInstance == null) {
notFound()
return
}
if (bookInstance.hasErrors()) {
respond bookInstance.errors, view:'create'
return
}
if(!bookInstance.id){
bookInstance.id = UUID.randomUUID().toString()
}
bookInstance.save flush:true
request.withFormat {
form {
flash.message = message(code: 'default.created.message', args: [message(code: 'Book.label', default: 'Book'), bookInstance.id])
redirect bookInstance
}
'*' {
respond bookInstance, [status: CREATED]
}
}
}
GSP:
<g:uploadForm action="save" class="form-horizontal">
<g:render template="form"/>
<div class="form-actions margin-top-medium">
<g:submitButton name="create" class="btn btn-primary" value="${message(code: 'default.button.create.label', default: 'Create')}" />
<button class="btn" type="reset"><g:message code="default.button.reset.label" default="Reset" /></button>
</div>
</g:uploadForm>
When the tag is used instead of the it works. The enctype="multipart/form-data" causes the error.
What can I try to solve this?
Thanks
You should check conf/Config.groovy and check that multipartForm: 'multipart/form-data', is present as a value for grails.mime.types, e.g.:
grails.mime.types = [
...
form: 'application/x-www-form-urlencoded',
multipartForm: 'multipart/form-data',
]
You also need to specify that it's a multipart form in your withFormat declaration:
request.withFormat {
form multipartForm
You can try 'render' or 'forward' instead of "respond" .

How to use Grails Searchable plugins in more than 2 domains

I am completely new in Grails, start learning grails from past couple of days. I am trying to add search feature by using searchable plugin in my demo grails application. I successfully added searchable plugins on user search where user can search other users and follow them. I am doing like this ..
grails install-plugin searchable
Domain Person.groovy --
package org.grails.twitter
class Person {
transient springSecurityService
String realName
String username
String password
boolean enabled
boolean accountExpired
boolean accountLocked
boolean passwordExpired
static hasMany = [followed:Person, status:Status]
static searchable = [only: 'realName']
static constraints = {
username blank: false, unique: true
password blank: false
}
static mapping = {
password column: '`password`'
}
Set<Authority> getAuthorities() {
PersonAuthority.findAllByPerson(this).collect { it.authority } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
}
view/searchable/index.gsp ---
<html>
<head>
<meta name="layout" content="main" />
<title>What Are You Doing?</title>
<g:javascript library="jquery" plugin="jquery" />
</head>
<body>
<h1>Search For People To Follow</h1>
<div class="searchForm">
<g:form controller="searchable">
<g:textField name="q" value=""/>
</g:form>
</div>
<h1>What Are You Doing?</h1>
<div class="updateStatusForm">
<g:formRemote onSuccess="document.getElementById('messageArea').value='';" url="[action: 'updateStatus']" update="messageLists" name="updateStatusForm">
<g:textArea name="message" value="" id="messageArea" /><br/>
<g:submitButton name="Update Status" />
</g:formRemote>
</div>
<div id="messageLists">
<g:render template="messages" collection="${messages}" var="message"/>
</div>
</body>
</html>
It works fine. Now My problem starts. Now I want to add this searchable in my Post domain also where user can search post items. I am doing like this ...
Domain Post.groovy --
package groovypublish
class Post {
static hasMany = [comments:Comment]
String title
String teaser
String content
Date lastUpdated
Boolean published = false
SortedSet comments
static searchable = [only: 'title']
static constraints = {
title(nullable:false, blank:false, length:1..50)
teaser(length:0..100)
content(nullable:false, blank:false)
lastUpdated(nullable:true)
published(nullable:false)
}
}
and here is form view
view/post/list.gsp --
------ some code -----
<g:form controller="searchable" class="navbar-search pull-left">
<g:textField name="q" value="" class="search-query" placeholder="Search Posts"/>
</g:form>
------ some code ------
Now when I try to search post by post title then it showing error. It overrides searchable action. How to solve this problem ?
You can implement your own search method using searchable, call a controller function from your search form and perform search in that:
Let say you have two search forms:
<g:form controller="postsController" action="postAction" class="navbar-search pull-left">
<g:textField name="q" value="" class="search-query" placeholder="Search Posts"/>
</g:form>
and
<g:form controller="searchable">
<g:textField name="q" value=""/>
</g:form>
then in the PostCOntroller you can have postAction method to perform search:
def postAction (Integer max) {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
params.sort = "id"
params.order = "desc"
if(params?.q){
def result = Post .search(params.q , order:"desc" )
return [searchResults: result.results, searchResultsCount: result.total, popup : params.popup?.toBoolean()]
}else{
[searchResults: Post .list(params), searchResultsCount: Post .count(), popup : params.popup?.toBoolean()]
}
and same you can have a different function for another search, if you use remote form then you need to have two different div's on the search page, and you can render the result page out there.
let say you have:
<g:formRemote name="postSearchForm" update="postSearchResultsDiv" url="[controller: 'post', action:'postAction' , params: [popup: false]]">
<label for="searchText">Search Post:</label>
<input name="q" type="text" id="searchText" class="input-medium search-query"/>
<input id="searchButton" type="submit" class="btn-info" value="Search"/>
</g:formRemote>
<div id="postSearchResultsDiv">--Your search result for the form will display here--</div>
This remote form will call postAction method in your controller, you can have postAction.gsp page on the controller's view folder and print the result out there.
and on your search page, postSearchResultsDiv will have the search result(postAction GSP page output)
I solved my own problem...
I have done like this ..
PostController ---
import org.compass.core.engine.SearchEngineQueryParseException
class PostController
{
def searchableService
def searchpost = {
if (!params.q?.trim()) {
return [:]
}
try {
return [searchResult: searchableService.search(params.q, params)]
} catch (SearchEngineQueryParseException ex) {
return [parseException: true]
}
render(view:'searchpost')
}
.......
}
Search form ---
<g:form controller="post" action="searchpost" class="navbar-search pull-left">
<g:textField name="q" value="" class="search-query" placeholder="Search Posts"/>
</g:form>
searchpost.gsp //for showing result
<html>
<head>
<r:require modules="bootstrap"/>
<meta name="layout" content="main"/>
</head>
<body>
<g:render template="/layouts/header" />
<div class="well">
<g:each var="post" in="${searchResult?.results}">
<div>
<h2>${post.title}</h2>
<p>${post.teaser}</p>
<p>Last Updated: ${post.lastUpdated}</p>
<g:link controller="post" action="view" id="${post.id}" class="btn btn-success">
View this post
</g:link>
<g:if test="${post.author == currentLoggedInUser }">
<g:link controller="post" action="edit" id="${post.id}" class="btn btn-danger">
Edit this post
</g:link>
<g:actionSubmit action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" class="btn btn-inverse" />
</g:if>
<g:form>
<g:hiddenField name="id" value="${post?.id}" />
</g:form>
</div>
</g:each>
</div>
</body>
</html>
And it works :)

Grails empty entry into the database

I have been struggling with trying to create/save multiple instances at once in Grails, now I am almost there with the following code but when I hit save an empty row of options is created, can anyone help me with this
please see these two questions to see what I want to achieve
How to save multiple object from one view using Grails
Grails one to many relationship view
<g:textField name="question" value="${multipleChoiceQuestionInstance?.question}"/><br/>
<div class="fieldcontain ${hasErrors(bean: multipleChoiceQuestionInstance, field: 'options', 'error')} ">
<label for="options">
<g:message code="multipleChoiceQuestion.options.label" default="Options" />
</label>
<ul class="one-to-many">
<g:set var="counter" value="${0}" />
<g:each status="i" in="${multipleChoiceQuestionInstance?.options?}" var="o">
<li>
<g:textField controller="multipleChoiceOption" name="options[${i}].answerOption" action="show" id="${o.id}" value="${o?.encodeAsHTML()}"/>
<g:checkBox name="options[${i}].correctOption" value="${o.correctOption}"/><br/>
</li>
<g:set var="counter" value="${++counter}" />
</g:each>
<li>
<g:textField name="options[${++counter}].answerOption" value=""/>
<g:checkBox name="options[${counter}].correctOption" /><br/>
</li>
<li class="add">
<g:link controller="multipleChoiceOption" action="create" params="['multipleChoiceQuestion.id': multipleChoiceQuestionInstance?.id]">${message(code: 'default.add.label', args: [message(code: 'multipleChoiceOption.label', default: 'MultipleChoiceOption')])}</g:link>
</li>
</ul>
</div>
If you prefer not to click on the link here are the domain classes
Class MultipleChoiceQuestion {
String question
static constraints = {
...
}
static hasMany = [options:MultipleChoiceOption]
class MultipleChoiceOption{
String answerOption
boolean correctOption
MultipleChoiceQuestion question
static constraints = {
...
}
}
}
I am using automatically generated code by grails for the controller, it is as bellow
def create() {
[multipleChoiceQuestionInstance: new MultipleChoiceQuestion(params)]
}
def save() {
println params
def multipleChoiceQuestionInstance = new MultipleChoiceQuestion(params)
if (!multipleChoiceQuestionInstance.save(flush: true)) {
render(view: "create", model: [multipleChoiceQuestionInstance: multipleChoiceQuestionInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'multipleChoiceQuestion.label', default: 'MultipleChoiceQuestion'), multipleChoiceQuestionInstance.id])
redirect(action: "show", id: multipleChoiceQuestionInstance.id)
}
def update() {
def multipleChoiceQuestionInstance = MultipleChoiceQuestion.get(params.id)
if (!multipleChoiceQuestionInstance) {
.... //deleted for real estate
return
}
if (params.version) {
//version checking stuff
}
}
multipleChoiceQuestionInstance.properties = params
if (!multipleChoiceQuestionInstance.save(flush: true)) {
render(view: "edit", model: [multipleChoiceQuestionInstance: multipleChoiceQuestionInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'multipleChoiceQuestion.label', default: 'MultipleChoiceQuestion'), multipleChoiceQuestionInstance.id])
redirect(action: "show", id: multipleChoiceQuestionInstance.id)
}
The reason that you are getting an empty row of options is because you are leaving a textfield with name option[counter] blank. This empty value is passed as a parameter to the controller action and it creates a new row with these blank values.
You should remove any blank options before calling
multipleChoiceQuestionInstance.properties = params
You can use something like this :
def emptyOptions = params.options.findAll{!it.answerOption}
params.options.removeAll(emptyOptions)

Resources