Grails Parent Child Form not saving child data - grails

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.

Related

Formatting phone numbers in Groovy/Grails

I have been trying to format phone numbers in a grails app by using the following taglib
package rewards
class MasksTagLib {
static defaultEncodeAs = [taglib:'html']
//static encodeAsForTags = [tagName: [taglib:'html'], otherTagName:
[taglib:'none']]
def phone334 = { attrs ->
String phone = attrs.phone
def formatted = "("+phone.substring(0,3)+") "+pho ne.substring(3,6)+"-"+phone.substring(6)
out << formatted
}
}
In addition I have used the import statement to use the taglib in my views, however the format is not changing the display in the index.gsp for the CustomerController
This works in the following view, but I can't get it to work elsewhere the view is profile.gsp
<%# page import="rewards.Customer" %>
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'customer.label', default: 'Customer')}" />
<title>Customer Profile</title>
</head>
<body>
<div id="edit-customer" class="content scaffold-edit" role="main">
<h1>Customer Profile</h1>
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<g:hasErrors bean="${customerInstance}">
<ul class="errors" role="alert">
<g:eachError bean="${customerInstance}" 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:customerInstance, action:'updateProfile']" method="PUT" >
<g:hiddenField name="version" value="${customerInstance?.version}" />
<fieldset class="buttons">
<g:actionSubmit class="save" action="updateProfile" value="${message(code: 'default.button.update.label', default: 'Update')}" />
</fieldset>
<fieldset class="form">
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'firstName', 'error')} ">
<label for="firstName">
<g:message code="customer.firstName.label" default="First Name" />
</label>
<g:textField name="firstName" value="${customerInstance?.firstName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'lastName', 'error')} ">
<label for="lastName">
<g:message code="customer.lastName.label" default="Last Name" />
</label>
<g:textField name="lastName" value="${customerInstance?.lastName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'phone', 'error')} required">
<span id="phone-label" class="property-label"><g:message code="customer.phone.label" default="Phone" /></span>
<span class="property-value" aria-labelledby="phone-label"><g:phone334 phone="${customerInstance?.phone}"/></span>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'email', 'error')} ">
<label for="email">
<g:message code="customer.email.label" default="Email" />
</label>
<g:textField name="email" value="${customerInstance?.email}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: customerInstance, field: 'totalPoints', 'error')} required">
<span id="totalPoints-label" class="property-label"><g:message code="customer.totalPoints.label" default="Total Points" /></span>
<span class="property-value" aria-labelledby="totalPoints-label"><g:fieldValue bean="${customerInstance}" field="totalPoints"/></span>
</div>
</fieldset>
</g:form>
</div>
<div id="list-award" class="content scaffold-list" role="main">
<g:if test="${flash.message}">
<div class="message" role="status">${flash.message}</div>
</g:if>
<table>
<thead>
<tr>
<g:sortableColumn property="type" title="${message(code: 'award.type.label', default: 'Type')}" />
<g:sortableColumn property="awardDate" title="${message(code: 'award.checkinDate.label', default: 'Award Date')}" />
<th><g:message code="award.customer.label" default="Phone" /></th>
<g:sortableColumn property="points" title="${message(code: 'award.points.label', default: 'Points')}" />
</tr>
</thead>
<tbody>
<g:each in="${customerInstance.awards}" status="i" var="checkinInstance">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td>${fieldValue(bean: checkinInstance, field: "type")}</td>
<td>${fieldValue(bean: checkinInstance, field: "awardDate")}</td>
<td><g:phone334 phone="${customerInstance?.phone}"/></td>
<td>${fieldValue(bean: checkinInstance, field: "points")}</td>
</tr>
</g:each>
</tbody>
</table>
</div>
</body>
I would appreciate any help I can get.
Here is the Controller. It is called CustomerController.groovy and it is displaying the list using "Index". How can I apply the phone format to this list
package rewards
class CustomerController {
static scaffold = true
def calculationsService
def lookup() {
// def customerInstance = Customer.list(sort: "lastName", order:
"desc", max: 5, offset: 5)
// dynamic queries
// def customerInstance = Customer.findAllByLastName("Foster")
// def customerInstance = Customer.findAllByTotalPoints(5, [sort: "lastName", order: "desc"])
// def customerInstance = Customer.findAllByPhone(params.id) // for one row return findBy if rows > 1 only first
// def customerInstance = Customer.findAllByLastNameLike("H%") // Case Sensitive
// def customerInstance = Customer.findAllByLastNameIlike("b%") // Case Insensitive
// def customerInstance = Customer.findAllByTotalPointsGreaterThanEquals(3, [sort: "totalPoints"])
// def customerInstance = Customer.findAllByTotalPointsBetween(2, 4, [sort: "totalPoints"])
def customerInstance = Customer.findAllByFirstNameIlikeAndTotalPointsGreaterThanEquals("b%", 3)
[customerInstanceList: customerInstance]
}
def customerLookup(Customer lookupInstance) {
// Query customer by Phone number - service
// If no result, - controller
// create a new customer - service
// create welcome message - service
// add award reward - service
// save customer - service
// send welcome to kiosk - controller
// if customer found - controller
// calculate total ponts - service
// create welcome message - service
// add award reward - service
// save customer - controller
// send welcome to kiosk - controller
def (customerInstance, welcomeMessage) = calculationsService.processCheckin(lookupInstance)
render(view: "checkin", model:[customerInstance: customerInstance, welcomeMessage: welcomeMessage])
}
def index() {
params.max = 10
[customerInstanceList: Customer.list(params), customerInstanceCount: Customer.count()]
}
def create() {
[customerInstance: new Customer()]
}
def save(Customer customerInstance) {
customerInstance.save()
redirect(action: "show", id: customerInstance.id)
}
def show(Long id) {
def customerInstance = Customer.get(id)
customerInstance = calculationsService.getTotalPoints(customerInstance)
[customerInstance: customerInstance]
}
def edit(Long id) {
def customerInstance = Customer.get(id)
[customerInstance: customerInstance]
}
def update(Long id) {
def customerInstance = Customer.get(id)
customerInstance.properties = params
customerInstance.save()
redirect(action: "show", id: customerInstance.id)
}
def delete(Long id) {
def customerInstance = Customer.get(id)
customerInstance.delete()
redirect(action: "index")
}
def profile() {
def customerInstance = Customer.findByPhone(params.id)
[customerInstance: customerInstance]
}
def updateProfile(Customer customerInstance) {
customerInstance.save()
render(view: "profile", model:[customerInstance: customerInstance])
}
def checkin() {}
}
Customer List Display
If you want to customize your index view, your best bet in this case is to stop using the scaffolded page, yes. (There are alternatives, but they're overly complicated for this situation, and don't offer you nearly as much control over the resulting view.)
In general, it's not a good idea long-term to have half-scaffolded content. By this, I mean either scaffolded controllers, or scaffolded views, but not the other side. Long term, one or the other is likely to change, and your pages will break.

How can I pass some values from gsp to controller in a g:form?

I know there are some questions like mine, but I'm hoping to get an explanation of how to do it in Grails.
What I want to do is pass values from a gsp datepicker and textfield to the controller when upon Submit button press.
My thanks in advance
GSP:
<g:form action="generateReport">
<ol>
<li class="fieldcontain">
<div class="fieldcontain required">
<label for="date" style="padding-left: 210px;">
<g:message code="Data" default="Data"/>
<span class="required-indicator">*</span>
<g:datePicker id="data" name="datapicker" precision="day"></g:datePicker>
</label>
</div>
</li>
<li class="fieldcontain">
<div class="fieldcontain required">
<label for="date" style="padding-left: 195px;">
<g:message code="Time" default="Time"/>
<g:textField name="minutes"/>
</label>
</div>
</li>
</ol>
<div class="content scaffold-show" style="padding-left: 50px;">
<g:submitButton value="search" name="button"/>
</div>
</g:form>
Controller:
def generateReport(){
log.info("Button pressed")
redirect(action: "list")
}
I just found the answer:
the view:
<g:textField name="name" />
the controller:
Controller:
class SomeController {
def someAction() {
def name = params.name
// do something with name
}
}

grails g:field error not showing

I have this setup, but the validation is not working
index.gsp:
<g:form name="loginForm" autocomplete="off" controller="company" action ="save">
<table >
<tr>
<td><g:field type="text" name="company" required="true" value="${c?.companyName}" /></td>
</tr>
</table>
Controller:
def index = {
def c =new Company()
//c=params
return [c:c]
}
def save ={}
You need to check for errors using hasErrors method in your form:
<div class="fieldcontain ${hasErrors(bean: yourBean, field: 'yourField', 'error')} required">
<label for="yourField">
<g:message code="yourBean.yourField.label" default="yourField" />
<span class="required-indicator">*</span>
</label>
<g:textField name="content" required="" value="${yourBean?.yourField}"/>
</div>
Grails hasErrors documenttaion.
And check in your controller (save action) if the validation has passed using:
if (!yourBean.save(flush: true)) {
render(view: "create", model: [yourBean: yourBean])
return
}

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.

Grails upload image

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"
}

Resources