Grails upload image - grails

I am trying to upload a picture, and save it in database.
If I do the following I get this error:
Failed to convert property value of type
org.springframework.web.multipart.commons.CommonsMultipartFile to
required type java.lang.Byte[] for property picture1; nested exception
is java.lang.IllegalArgumentException: Cannot convert value of type
[org.springframework.web.multipart.commons.CommonsMultipartFile] to
required type [java.lang.Byte] for property picture1[0]:
PropertyEditor
[org.springframework.beans.propertyeditors.CustomNumberEditor]
returned inappropriate value
If i do it this way:
if(request instanceof MultipartHttpServletRequest){
MultipartHttpServletRequest mpr = (MultipartHttpServletRequest)request;
CommonsMultipartFile f = (CommonsMultipartFile) mpr.getFile("picture1");
}
I get this error:
Executing action [save] of controller [com.testapp.RequestController]
caused exception: Cannot cast object '
org.springframework.web.multipart.commons.CommonsMultipartFile#34ae1f02'
with class
'org.springframework.web.multipart.commons.CommonsMultipartFile' to
class 'java.lang.Byte'
What should I do to make this work?
Domain
package com.testapp
class Request{
String requestID
Date dateCreated
String subject
String startedBy
String description
String status
String priority
Productline productline
Topic topic
Subtopic subtopic
String company
Byte [] picture1
Byte [] picture2
Byte [] picture3
String acceptedBy
static constraints = {
requestID(blank:true,nullable:true)
dateCreated(blank:true,nullable:true)
subject()
description(maxSize:5000)
status (blank:true,nullable:true)
priority(inList:["Normal","Urgent","Not urgent"])
productline(blank:true,nullable:true)
topic(blank:true,nullable:true)
subtopic(blank:true,nullable:true)
company(blank:true,nullable:true)
startedBy(blank:true,nullable:true)
acceptedBy(blank:true,nullable:true)
picture1(blank:true,nullable:true)
picture2(blank:true,nullable:true)
picture3(blank:true,nullable:true)
}
}
GSP:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'request.label', default: 'Request')}" />
<title><g:message code="New request" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}"><g:message `code="default.home.label"/></a></span>`
<span class="menuButton"><g:link class="list" action="userList"><g:message code="Lista zahteva" `args="[entityName]" /></g:link></span>`
</div>
<div class="body">
<h1><g:message code="New request" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${requestInstance}">
<div class="errors">
<g:renderErrors bean="${requestInstance}" as="list" />
</div>
</g:hasErrors>
<g:form action="save" method="post" enctype="multipart/form-data">
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="subject"><g:message code="request.subject.label" default="Subject" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'subject', 'errors')}">
<g:textField name="subject" value="${requestInstance?.subject}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="description"><g:message code="request.description.label" default="Opis" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'description', 'errors')}">
<g:textArea name="description" cols="40" rows="5" value="${requestInstance?.description}" />
</td>
</tr>
<tr>
<td valign="top" class="name">
<label for="picture1"><g:message code="request.picture1.label" default="Printscreen" /></label>
</td>
<td valign="top" class="value">
<input type="file" id="picture1" name="picture1"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="priority"><g:message code="request.priority.label" default="Priority" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'status', 'errors')}">
<g:select name="priority" from="${requestInstance.constraints.priority.inList}" value="${requestInstance?.priority}" valueMessagePrefix="request.priority" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="productline"><g:message code="request.productline.label" default="Productline" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'productline', 'errors')}">
<g:select name="productline.id" from="${com.testapp.Productline.list()}" optionKey="id" value="${requestInstance?.productline?.id}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="topic"><g:message code="request.topic.label" default="Topic" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'topic', 'errors')}">
<g:select name="topic.id" from="${com.testapp.Topic.list()}" optionKey="id" value="${requestInstance?.topic?.id}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="subtopic"><g:message code="request.subtopic.label" default="Subtopic" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: requestInstance, field: 'subtopic', 'errors')}">
<g:select name="subtopic.id" from="${com.testapp.Subtopic.list()}" optionKey="id" value="${requestInstance?.subtopic?.id}" />
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
</div>
</body>
Controller:
def save = {
def requestInstance = new Request(params)
def requestNumberInstance = new RequestNumber()
def upPic1 = request.getFile('picture1')
def lastReqNum = RequestNumber.find("from RequestNumber ORDER BY requestNumber desc")
if(lastReqNum){
requestNumberInstance.requestNumber = lastReqNum.requestNumber + 1
}
else{
requestNumberInstance.requestNumber = 110000
}
requestInstance.requestID = "CSC" + requestNumberInstance.requestNumber
def currentUserContact = Contact.findByUser(springSecurityService.getCurrentUser())
requestInstance.startedBy = currentUserContact.realname
requestInstance.company = currentUserContact.company
requestInstance.status = "Opened"
requestInstance.acceptedBy = "Not yet accepted"
requestInstance.picture1 = upPic1
if(requestNumberInstance.save()){
if (requestInstance.save()) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'request.label', default: 'Request'), requestInstance.id])}"
redirect(action: "show", id: requestInstance.id)
}
else {
render(view: "create", model: [requestInstance: requestInstance])
}
}
else{
render(view: "create", model: [requestInstance: requestInstance])
}
}
Please dont mind the spaghetti code. I'm just trying to get some basic concepts. I will clear it later.
Simplified example:
def save = {
def requestInstance = new Request(params)
def requestNumberInstance = new RequestNumber()
if(requestInstance.validate() && requestInstance.save(flush: true)){
println "Saved successfully with ${requestInstance.picture1.length} bytes"
}
else {
println "Save failed"
}

Update after question edit
The error is probably caused by this:
def upPic1 = request.getFile('picture1')
...
requestInstance.picture1 = upPic1
request.getFile() is returning a MultipartFile, and you're trying to assign it to a Byte[] field. Considering my small example (below), you shouldn't even need to try to make this assignment. The def requestInstance = new Request(params) will bind the byte[] automatically.
Uploaded files bind automatically to byte[] fields. Here's a working example:
Domain: grails-app/domain/my/Example.groovy
package my
class Example {
byte[] file
}
Controller: grails-app/controllers/my/ExampleController.groovy
package my
class ExampleController {
def create = { }
def save = {
def example = new Example(params)
if(example.validate() && example.save(flush: true)) {
println "Saved successfully with ${example.file.length} bytes"
} else {
println "Save failed"
}
redirect(action: 'create')
}
}
GSP: grails-app/views/example/create.gsp
<!DOCTYPE html>
<html>
<body>
<g:uploadForm action="save">
<input type="file" name="file"/>
<g:submitButton name="submit"/>
</g:uploadForm>
</body>
</html>
When I upload a small file using the GSP form, I see the following console output:
Saved successfully with 23 bytes
Suggestions
Try using Grails data binding to save your file contents.
Make sure your form is a <g:uploadForm> or has an enctype="multipart/form-data" if you're using a vanilla`.
Make sure you're binding the params using the domain constructor, domain.properties, or bindData().

I am guessing your typing is messed up.
What happens if you simply do:
def f = request.getFile('myFile')
as described in the manual. If you want strong typing it should be MultiPartfile, not CommonsMultiPartFile, as far as I remember (and you get it right from the request object).
This is the interface you're working on: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/multipart/MultipartFile.html
The relevant method for you, should be getBytes().
Edit for edited question:
As I said, you want getBytes(), you are trying to shove a MultiPartFile into a byte array, thats not going to work.
requestInstance.picture = upPic.getBytes() and you should be allright.

It only worked when I changed my domain properties picture1,picture2,picture3 to:
byte [] picture1
byte [] picture2
byte [] picture3
and added those mappings:
static mapping = {
picture1 column:"picture1", sqlType: "blob"
picture2 column:"picture2", sqlType: "blob"
picture3 column:"picture3", sqlType: "blob"
}

Related

Grails Parent Child Form not saving child data

I am trying to create a parent child form with Author and Book domain classes. The view works fine and lets me enter books when I creating a new Author. However, when I add a new author and the books and check the database (dbconsole), I see a new record in the Author table but no records are added to the Book table. Can you please let me know what I am missing or doing wrong here?
Here are my domain classes:
AUTHOR:
package bookauthor1tomany
import org.apache.common.collections.list.*
import org.apache.commons.collections.ListUtils.*
class Author {
static constraints = {
}
String name
String category
List<Book> books = new ArrayList<>()
static hasMany = [ books:Book ]
static mapping = {
books cascade:"all-delete-orphan"
}
def getExpandableBookList() {
return LazyList.decorate(books, FactoryUtils.instantiateFactory(Book.class))
}
String toString(){
return "${name}" - "${category}"
}
}
BOOK
package bookauthor1tomany
class Book {
static constraints = {
}
String title
boolean _deleted
static transients = [ '_deleted' ]
static belongsTo = [ author:Author ]
def String toString() {
return title
}
}
AuthorController
I haven't changed anything with the controller. This is the default generated save method for the Author controller.
#Transactional
def save(Author authorInstance) {
if (authorInstance == null) {
notFound()
return
}
if (authorInstance.hasErrors()) {
respond authorInstance.errors, view:'create'
return
}
authorInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'author.label', default: 'Author'), authorInstance.id])
redirect authorInstance
}
'*' { respond authorInstance, [status: CREATED] }
}
}
GSPs
create.gsp
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'author.label', default: 'Author')}" />
<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-author" 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="${authorInstance}">
<ul class="errors" role="alert">
<g:eachError bean="${authorInstance}" 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 url="[resource:authorInstance, action:'save']" >
<fieldset class="form">
<g:render template="authortemp"/>
</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>
_form.gsp
<%# page import="BookAuthor1ToMany" %>
<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'books', 'error')} ">
<label for="books">
<g:message code="author.books.label" default="Books" />
</label>
<ul class="one-to-many">
<g:each in="${authorInstance?.books?}" var="b">
<li><g:link controller="book" action="show" id="${b.id}">${b?.encodeAsHTML()}</g:link></li>
</g:each>
<li class="add">
<g:link controller="book" action="create" params="['author.id': authorInstance?.id]">${message(code: 'default.add.label', args: [message(code: 'book.label', default: 'Book')])}</g:link>
</li>
</ul>
</div>
<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'name', 'error')} required">
<label for="name">
<g:message code="author.name.label" default="Name" />
<span class="required-indicator">*</span>
</label>
<g:textField name="name" required="" value="${authorInstance?.name}"/>
</div>
_authortemp.gsp
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="name">Name:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'name','errors')}">
<input type="text" id="name" name="name" value="${fieldValue(bean:authorInstance,field:'name')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="category">Category:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'category','errors')}">
<input type="text" id="category" name="category" value="${fieldValue(bean:authorInstance,field:'category')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="books">Books:</label></td>
<td valign="top" class="value ${hasErrors(bean:authorInstance,field:'books','errors')}">
<g:render template="books" model="['authorInstance':authorInstance]" />
</td>
</tr>
</tbody>
</table>
</div>
_books.gsp
<script type="text/javascript">
var childCount = ${authorInstance?.books.size()} + 0;
function addChild() {
var htmlId = "book" + childCount;
var deleteIcon = "${resource(dir:'images/skin', file:'database_delete.png')}";
var templateHtml = "<div id='" + htmlId + "' name='" + htmlId + "'>\n";
templateHtml += "<input type='text' id='expandableBookList[" + childCount + "].title' name='expandableBookList[" + childCount + "].title' />\n";
templateHtml += "<span onClick='$(\"#" + htmlId + "\").remove();'><img src='" + deleteIcon + "' /></span>\n";
templateHtml += "</div>\n";
$("#childList").append(templateHtml);
childCount++;
}
</script>
<div id="childList">
<g:each var="book" in="${authorInstance.books}" status="i">
<g:render template='book' model="['book':book,'i':i]"/>
</g:each>
</div>
<input type="button" value="Add Book" onclick="addChild();" />
_book.gsp
<div id="book${i}">
<g:hiddenField name='expandableBookList[${i}].id' value='${book.id}'/>
<g:textField name='expandableBookList[${i}].title' value='${book.title}'/>
<input type="hidden" name='expandableBookList[${i}]._deleted' id='expandableBookList[${i}]._deleted' value='false'/>
<span onClick="$('#expandableBookList\\[${i}\\]\\._deleted').val('true'); $('#expandableBookList${i}').hide()">Delete</span>
</div>
You don't need the
List<Book> books = new ArrayList<>()
in your domain. The hasMany will give you a books collection by default. That might be causing some issues. Also, have you debugged in your controller to make sure that the books collection is populated when the save occurs.

Create dynamic table with webflow and grails

I'm trying to create a webflow with a dynamic table on one page. Which looks like this
def startFlow = {
contact {
on('next') {
flow.developer = params.developer
flow.project = params.project
flow.division = params.division
flow.projectResponsible = params.projectResponsible
flow.email = params.email
[flow : flow]
}.to('ipcount')
on('cancel').to('finish')
}
ipcount{
on('next'){
flow.ipcount = params.int('ipcount')
[flow: flow]
}.to('systems')
on('cancel').to('finish')
}
systems{
on('next') {
flow.hoster= params.hoster
flow.ip = params.ip
flow.os = params.os
flow.dns = params.dns
flow.systemDate = params.systemDate
[flow : flow]
}.to('url')
on('cancel').to('finish')
} ....
The problem is I that the number of systems could be different every time (1...n).
One idea was to ask the page before how many entries should be created (ipcount).
My view looks like this
<g:set var="count" value="${flow.ipcount}" />
<g:each in="${(1..'${count}')}">
<tr class="prop">
<td valign="top" class="name">
<label for="ip">IP Adresse:</label>
</td>
<td valign="top">
<td valign="top" class="value ${hasErrors(bean:hosterInstance,field:'ip','errors')}">
<input type="text" id="ip" name="ip" value="${params.ip}" />
</td>
<td valign="top" class="name">
<label for="dns">DNS:</label>
</td>
<td valign="top">
<input type="text" id="dns" name="dns" value="${params.dns}" />
</td>
<td valign="top" class="name">
<label for="os">Operating System:</label>
</td>
<td valign="top">
<input type="text" id="dns" name="dns" value="${params.os}" />
</td>
</tr>
</g:each>
Beside that this is not working as I get an Internal server error: (java.lang.String cannot be cast to java.lang.Integer) it would be nicer if I could at the table row dynamically on the page.
Here is the question: Is this possible with webflow and how? Especially I don't know how to handle the flow parameter and how to save the collected entries at the end of the webflow to the database.
If you need to work with a list of objects, command objects is the way to go. It supports databinding from the incoming request and will handle lists.
You can check a related question that show's you how to do it.
And in your view, you will need to handle the index in the name of your input. Example:
Consider
class System {
String ip
String dns
...
}
And commandInstance.systems a List<System>.
<g:each in="${commandInstance.systems}" var="command" status="i">
<input type="text" id="ip$i" name="systems[$i].ip" value="${command.ip}" />
</g:each>

How to retrieve struts2 checkboxlist values

I will create a list with checkboxlist. For those I use following code:
<s:form action="accept" namespace="/manager/course">
<s:checkboxlist list="courseRequests" name="acceptList" listValue="studentNickname" listKey="studentId" theme="checkbox-fix"/>
<s:url action="accept" namespace="/manager/course" var="accList" />
<s:a href="%{accList}"><s:text name="Accept"/> </s:a>
</s:form>
It work fine a create a check box list, that you can see its pic in the following:
and this is html code generated by above code:
<form id="accept" name="accept" action="/ESA/manager/course/accept.action" method="post">
<table class="wwFormTable">
<table class="gradienttable">
<tr>
<th class="row"><p>Row</p></th>
<th style="width: 240px;"><p>Student</p></th>
<th ><p>Accept</p></th>
</tr>
<tr>
<td id="row"><p><label>1</label></p></td>
<td style="width:250px;"><p>
<label for="acceptList-1" class="checkboxLabel">Mansour Barzegar</label>
</p></td>
<td style="text-align:center;"><p>
<input type="checkbox" name="acceptList" value="5" id="acceptList-1" </p></td>
</tr>
<tr>
<td id="row"><p><label>2</label></p></td>
<td style="width:250px;"><p>
<label for="acceptList-2" class="checkboxLabel">Ali Mahmoudi</label>
</p></td>
<td style="text-align:center;"><p>
<input type="checkbox" name="acceptList" value="6" id="acceptList-2" </p></td>
</tr>
<tr>
<td id="row"><p><label>3</label></p></td>
<td style="width:250px;"><p>
<label for="acceptList-3" class="checkboxLabel">Masih Zare</label>
</p></td>
<td style="text-align:center;"><p>
<input type="checkbox" name="acceptList" value="7" id="acceptList-3" </p></td>
</tr>
</table>
Accept
</table>
</form>
In the Action Class I tried to retrive seleced checkbox value by following code:
private int[] acceptList;
public void setAcceptList(int[] acceptList){
this.acceptList=acceptList;
}
and several other code but I all states I get null.
Do I use wrong code?
in your markup, do this:
<input type="checkbox" name="thename" id="checkbox_id1" value="value1" />
<input type="checkbox" name="thename" id="checkbox_id2" value="value2" />
in your action (or object) do this:
// action/object code...
Set<String> cbox = new HashSet();
public void setThename(String[] thenames) {
for (String thename : thenames) {
cbox.add(thename);
}
}
// action/object code...
notice the checkbox name matches the setter name, e.g. element name == someName and method == setSomeName
Same would apply for Set<Integer>, but you use int[] thenames as the argument. You could also use Integer[] thenames for the argument.
to test output:
if (cbox != null) {
for (String s : cbox) {
log.info(s);
}
}
http://struts.apache.org/release/2.2.x/docs/using-checkboxes.html

Grails file upload Kendoui

can someone please tell me a running example of upload file in kendoui?
As I have tried to upload file and its uploading in view page but when I click on save button I can't find that file in show page.I searched on it on internet then I found some problem of server.So someone please tell me how to use server in my case.I'm working on a grails project
Code That I have Used.:--
<tr class="prop">
<td valign="top" class="name">
<label>File Upload</label>
<input name="photos[]" id="photos" type="file" /><script>$(document).ready(function ()$("#photos").kendoUpload({
autoUpload:true,
upload: onUpload,
error: onError
});
function onError(e) {
// Array with information about the uploaded files
var files = e.files;
if (e.operation == "upload") {
alert("Failed to uploaded " + files.length + " files");
}
// Suppress the default error message
e.preventDefault();
},
function onUpload(e) {
var files = e.files;
if (e.operation == "upload") {
alert("Successfully uploaded " + files.length + " files");
}
});</script>
</td>
</tr>
i'm ading the view file :- create.gsp
<%# page import="ten.SkeletonBill"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="billing" />
<get var="entityName"
value="${message(code: 'skeletonBill.label', default: 'SkeletonBill')}" />
<title><g:message code="default.create.label"
args="[entityName]" /></title>
<script src="source/kendo.all.js"></script>
<link href="styles/kendo.common.css" rel="stylesheet" />
<link href="styles/kendo.default.css" rel="stylesheet" />
</head>
<body>
<content tag="menu-function">
<li><span class="k-link"><a href="#"
onclick="SkeletonBillForm.submit();return false;"><i
class="icon-plus-sign"></i>
<g:message code="default.button.save.label" /></a></span></li>
</content>
<div class="body">
<h1>
<g:message code="default.create.label" args="[entityName]" />
</h1>
<g:if test="${flash.message}">
<div class="message">
${flash.message}
</div>
</g:if>
<g:hasErrors bean="${skeletonBillInstance}">
<div class="alert alert-error">
<a class="close" data-dismiss="alert">Ă—</a>
<g:renderErrors bean="${skeletonBillInstance}" as="list" />
</div>
</g:hasErrors>
<g:uploadForm name="SkeletonBillForm" action="save" method="post">
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="bones"><g:message
code="skeletonBill.bones.label" default="Bones" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: skeletonBillInstance, field: 'bones', 'errors')}">
<g:textField name="bones"
value="${fieldValue(bean: skeletonBillInstance, field: 'bones')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="dateOfBirth"><g:message
code="skeletonBill.dateOfBirth.label" default="Date Of Birth" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: skeletonBillInstance, field: 'dateOfBirth', 'errors')}">
<g:textField name="dateOfBirth"
value="${skeletonBillInstance?.dateOfBirth}" /> <script>$(document).ready(function () {$("#dateOfBirth").kendoDatePicker({format:"yyyy-MM-dd"})});</script>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label>File Upload</label>
<input name="excelSheet" id="excelSheet" type="file" />
<script>
$(document).ready(function() {
$("#excelSheet").kendoUpload();
},
function onError(e) {
// Array with information about the uploaded files
var files = e.files;
if (e.operation == "upload") {
alert("Failed to uploaded " + files.length + " files");
}
// Suppress the default error message
e.preventDefault();
},
function onUpload(e) {
var files = e.files;
if (e.operation == "upload") {
alert("Successfully uploaded " + files.length + " files");
}
});
</script>
</td>
</tr>
</tbody>
</table>
</div>
</g:uploadForm>
</div>
</body>
</html>
And Also Controller.gsp
def save = {
def skeletonBillInstance = new SkeletonBill(params)
if(!skeletonBillInstance.empty){
println "Name: ${skeletonBill.bones}"
flash.message = "${message(code: 'default.created.message', args: [message(code: 'skeletonBill.label', default: 'SkeletonBill'), skeletonBillInstance.id])}"
redirect(action: "show", id: skeletonBillInstance.id)
}
} else {
render(view: "create", model: [skeletonBillInstance: skeletonBillInstance])
}
}
Couple of things
1) If you want to use KendoUI, I wouldn't use the gsp tags. Please use the normal form tags to define your form, if you do this grails resorts to using the prototype plugin for.
2) I will not mix the script code with the tags.
3) If you are using grails 2.0, you can use the KendoUI plugin, you can find more information at http://grails.org/plugin/kendo-ui
Hope that helps.

Saving object having many-to-many relationship in grails

I am trying to save objects having Many-Many Relationship . A SellingCompany can have many Accounts and an Account can be associated with many SellingCompanies. So there is a many-many relationship between the tables stored in SellingCompaniesAccount
My Account_Info domain is as follows:
class AccountInfo {
static mapping ={
table 'AccountInfo'
version false
//id column:'accountInfoID'
}
String evi_pass_phrase
String evi_username
String security_key
// to make sure fields show up in a particular order
static constraints = {
//accountInfoID(insert:false,update:false)
evi_pass_phrase()
evi_username()
security_key()
}
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
String toString() {
return "${evi_username}"
}
}
My SellingComapanies domain is as follows:
class SellingCompanies
{
static mapping = {
table 'SellingCompanies'
version false
}
String name
//static belongsTo = AccountInfo
//static hasMany = [accounts: AccountInfo]
static hasMany = [sellingcompaniesaccount:SellingCompaniesAccount]
static constraints = {
name(blank:false, validator:
{ val, obj ->
def similarSellingCompanies = SellingCompanies.findByNameIlike(val)
return !similarSellingCompanies || (obj.id == similarSellingCompanies.id)
})
}
//String toString() { name }
}
The table that holds the Many-Many relationship is as follows:
class SellingCompaniesAccount {
static constraints = {
// ensure the group of sellingCompaneis and accountInfo values are unique
agency_name(unique:['sellingCompanies','accountInfo'])
}
int agency_id
String agency_name
String consultant_id
String code
Boolean isActive
String iata
ContactInfo contactinfo
static belongsTo = [sellingCompanies:SellingCompanies, accountInfo:AccountInfo]
}
}
The form in the create.gsp file contains the code that actually iterates over all the different SellingCompanies and displays as a check-box.
<g:form action="save" method="post">
<div class="dialog">
<table width="500px" border="0px" color="red">
<tbody>
<tr class="prop">
<td valign="top" class="name"><label for="accountInfo"><g:message
code="sellingCompaniesAccount.accountInfo.label"
default="Account Info" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'accountInfo', 'errors')}">
<g:select name="accountInfo.id"
from="${content_hub_admin.AccountInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.accountInfo?.id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="sellingCompanies"><g:message
code="sellingCompaniesAccount.sellingCompanies.label"
default="Selling Companies" /></label></td>
<td valign="top"
class="">
<g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i">
${++i}. ${item.name} <g:checkBox name="sellingcompanies_${++i-1}" optionKey="id" value="${item.id}" /> <br>
</g:each>
<!-- end here by rsheyeah -->
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="code"><g:message
code="sellingCompaniesAccount.code.label" default="Code" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'code', 'errors')}">
<g:textField name="code"
value="${sellingCompaniesAccountInstance?.code}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_name"><g:message
code="sellingCompaniesAccount.agency_name.label"
default="Agencyname" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_name', 'errors')}">
<g:textField name="agency_name"
value="${sellingCompaniesAccountInstance?.agency_name}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="isActive"><g:message
code="sellingCompaniesAccount.isActive.label" default="Is Active" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'isActive', 'errors')}">
<g:checkBox name="isActive"
value="${sellingCompaniesAccountInstance?.isActive}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="agency_id"><g:message
code="sellingCompaniesAccount.agency_id.label" default="Agencyid" /></label>
</td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'agency_id', 'errors')}">
<g:textField name="agency_id"
value="${fieldValue(bean: sellingCompaniesAccountInstance, field: 'agency_id')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="iata"><g:message
code="sellingCompaniesAccount.iata.label" default="Iata" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'iata', 'errors')}">
<g:textField name="iata"
value="${sellingCompaniesAccountInstance?.iata}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="consultant_id"><g:message
code="sellingCompaniesAccount.consultant_id.label"
default="Consultantid" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'consultant_id', 'errors')}">
<g:textField name="consultant_id"
value="${sellingCompaniesAccountInstance?.consultant_id}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name"><label for="contactinfo"><g:message
code="sellingCompaniesAccount.contactinfo.label"
default="Contactinfo" /></label></td>
<td valign="top"
class="value ${hasErrors(bean: sellingCompaniesAccountInstance, field: 'contactinfo', 'errors')}">
<g:select name="contactinfo.id"
from="${content_hub_admin.ContactInfo.list()}" optionKey="id"
value="${sellingCompaniesAccountInstance?.contactinfo?.id}" /></td>
</tr>
</tbody>
</table>
</div>
<div class="buttons"><span class="button"><g:submitButton
name="create" class="save"
value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
Lastly the controller which handles the save and list functions.
class SellingCompaniesAccountController {
private static Logger log = Logger.getLogger(SellingCompaniesAccountController.class)
//def index = { }
//def scaffold = true
def index = { redirect(action:list,params:params) }
//To limit access to controller actions based on the HTTP request method.
def allowedMethods = [save:'POST']
//create.gsp exists
def create = {
render(view:"create")
}
//edit.gsp exists
//def edit = {}
//list.gsp exists
def list = {
[ sellingCompaniesAccountInstanceList: SellingCompaniesAccount.list( max:15) ]
}
//show.gsp exists
//def show={}
//save.gsp exists
def save = {
log.info "Saving: " + params.toString()
println("Saving: " + params.toString())
def sellingCompaniesAccount = params.sellingCompaniesAccount
println(sellingCompaniesAccount)
def sellingCompanies = params.sellingCompanies
log.info "sellingCompanies: " + sellingCompanies
println(sellingCompanies)
def sellingCompaniesAccountInstance = new SellingCompaniesAccount(name: params.name)
println(params.name)
params.each {
if (it.key.contains("_sellingcompanies"))
//sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
if (it.key.contains("sellingcompanies_"))
sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
}
log.info sellingCompaniesAccountInstance
if (sellingCompaniesAccountInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'sellingCompaniesAccountInstance.label', default: 'sellingCompaniesAccountInstance'), sellingCompaniesAccountInstance.id])}"
redirect(action: "show", id: sellingCompaniesAccountInstance.id)
log.info sellingCompaniesAccountInstance
}
else {
render(view: "create", model: [sellingCompaniesAccountInstance: sellingCompaniesAccountInstance])
}
}
}
Now, I am getting the following error, due to the empty hidden values appearing like _sellingcompanies_1 etc.:
Error Logs:
Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"]
null
null
null
2011-03-15 17:13:44,620 [http-8080-2] ERROR org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver - For input string: "_5"
java.lang.NumberFormatException: For input string: "_5"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:449)
at java.lang.Integer.valueOf(Integer.java:554)
at content_hub_admin.SellingCompaniesAccountController$_closure4_closure5.doCall(content_hub_admin.SellingCompaniesAccountController:70)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController:66)
at content_hub_admin.SellingCompaniesAccountController$_closure4.doCall(content_hub_admin.SellingCompaniesAccountController)
at java.lang.Thread.run(Thread.java:680)
First of all, where does the hidden values come from and is this approach fine to commit the the Many-Many relationship info in SellingCompaniesAccount controller class. Any better technique of doing this.
The create.gsp resolves to this in the browser:
Thanks in advance
If anyone else is having the same problem then the above answer by Daniel is absolutely correct just adding few changes as of Grails 2.7.8.
All checkbox will have same value = "${item.id}" and name ="sellingcompanies" as shown below:
<!-- ... snip ... -->
<tr class="prop">
<td valign="top" class="name"><label for="sellingCompanies"><g:message
code="sellingCompaniesAccount.sellingCompanies.label"
default="Selling Companies" /></label></td>
<td valign="top"
class="">
<g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i">
${++i}. ${item.name} <g:checkBox name="sellingcompanies" optionKey="id" value="${item.id}" /> <br>
</g:each>
<!-- end here by rsheyeah -->
</td>
</tr>
<!-- ... snip ... -->
Best part is that in present version of Grails you wont need to use flatten() as string as mentioned by Daniel and these values will automatically be handled by Grails and will be persisted in the join table. All you need to be sure is your checkbox have correct name and value.
Hope it helps!
The problem is with this piece of code:
params.each {
if (it.key.contains("_sellingcompanies"))
//sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
if (it.key.contains("sellingcompanies_"))
sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
}
Your params from the form post are:
Saving: ["accountInfo.id":"1", "accountInfo":["id":"1"], "_sellingcompanies_5":"", "_isActive":"", "code":"test", "agency_name":"test", "sellingcompanies_4":"4", "sellingcompanies_5":"5", "create":"Create", "isActive":"on", "iata":"test", "agency_id":"test", "contactinfo.id":"1", "contactinfo":["id":"1"], "consultant_id":"test", "sellingcompanies_2":"2", "_sellingcompanies_1":"", "sellingcompanies_3":"3", "_sellingcompanies_2":"", "_sellingcompanies_3":"", "sellingcompanies_1":"1", "_sellingcompanies_4":"", "action":"save", "controller":"sellingCompaniesAccount"]
The test in your loop first checks if the parameter key contains "_sellingcompanies" and then it does nothing; the second part of that checks if the parameter key contains "sellingcompanies_" and then it tries to pull the suffixed number off of that parameter value. In the case of a parameter with the key value "_sellingcompanies_5", both the first test and the second test evaluate to true, so in the second test, you are subtracting the "sellingcompanies_" off of the parameter key value and you are left with "_5", which you are then trying to evaluate to an Integer. Unfortunately, "_5" is not a valid Integer value, and hence your given error.
You can solve this very simply by doing the following:
params.each {
if (it.key.startsWith("sellingcompanies")) {
sellingCompaniesAccountInstance.sellingCompaniesId << SellingCompanies.get((it.key - "sellingcompanies_") as Integer)
}
}
Probably a better way to handle this though would be to change your gsp to give the same named parameter for each of the sellingcompanies, and then looping through the actual applied values. Something like this:
<!-- ... snip ... -->
<tr class="prop">
<td valign="top" class="name"><label for="sellingCompanies"><g:message
code="sellingCompaniesAccount.sellingCompanies.label"
default="Selling Companies" /></label></td>
<td valign="top"
class="">
<g:each in="${content_hub_admin.SellingCompanies.list()}" var="item" status="i">
${++i}. ${item.name} <g:checkBox name="sellingcompanies" optionKey="id" value="${item.id}" /> <br>
</g:each>
<!-- end here by rsheyeah -->
</td>
</tr>
<!-- ... snip ... -->
Then in your controller do something like this:
params.sellingcompanies = [params.sellingcompanies].flatten() as String[]
sellingCompaniesAccountInstance.sellingCompaniesId = params.sellingcompanies.collect { SellingCompanies.get(it) }
This should ensure that you are properly evaluating the appropriate values that have been passed from your model object, and not hacking in a retrieval method.
Hope this helps!

Resources