Search code examples
grailsgsp

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>
            <a href="#show-beer" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
            <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>
        <a href="#create-recipe" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
        <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>

Solution

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