olkoza
olkoza

Reputation: 725

Grails view (show.gsp) doesn't handle instance of expected class, get's null instead

i have a class User in my WebApplication and a class Driver, which is a subclass of User. Here is the code:

package elektrova

class User {

String username
String password
String firstName
String lastName
UserRole role

static belongsTo = [
    hauler:Hauler
]

static hasMany = [
    createdJobs:Job,
    activities:UserActivityHistory
]

static mappedBy = [
    createdJobs:'creationUser'
]

static mapping = { tablePerHierarchy false }

static constraints = {
    username(blank:false, size:4..15,matches:/[\S]+/, unique:true)
    password(blank:false, size:4..15,matches:/[\S]+/)
    firstName(blank:false, nullable:false)
    lastName(blank:false, nullable:false)
    role(blank:false, nullable:false)
}

@Override
def encodeAsHTML(){
    firstName + "  " + lastName
}
}



class Driver extends User{


UserRole role = UserRole.DRIVER

static hasMany = [
    assignedJobs:Job
    ]
static mappedBy = [assignedJobs:'assignedUser']
static constraints = {
}
}

Here is the creation of the tesdata in the bootstrap:

        User dispo = new Driver(hauler: hauler, firstName: "Dis", lastName: "Po", password: "dispo", role: UserRole.ADMIN, username: "dispo" ).save(failOnError: true)
        Driver driver = new Driver(hauler: hauler, firstName: "Kai", lastName: "Uwe", password: "kauw", role: UserRole.DRIVER, username: "kauw" ).save(failOnError: true)
        Driver driver2 = new Driver(hauler: hauler, firstName: "Hans", lastName: "Juergen", password: "haju", role: UserRole.DRIVER, username: "haju" ).save(failOnError: true)
        User admin = new User(hauler: hauler, firstName: "Ad", lastName: "Min", password: "admin", role: UserRole.ADMIN, username: "admin" ).save(failOnError: true)
        User admin2 = new User(hauler: hauler, firstName: "Ad", lastName: "Min", password: "admin", role: UserRole.ADMIN, username: "admin2" ).save(failOnError: true)

And the show.gsp:

<%@ page import="elektrova.User"%>
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName"
value="${message(code: 'user.label', default: 'User')}" />
<title><g:message code="default.show.label" args="[entityName]" /></title>
</head>
<body>
<g:render template="/navi/navbar" />
<a href="#show-user" class="skip" tabindex="-1"><g:message
        code="default.link.skip.label" default="Skip to content&hellip;" /></a>
<div class="nav" role="navigation">
    <ul>

        <li><g:link class="list" action="index">
                <g:message code="view.user.button.label.list" args="[entityName]" />
            </g:link></li>
        <li><g:link class="create" action="create">
                <g:message code="view.user.button.label.create" args="[entityName]" />
            </g:link></li>
    </ul>
</div>
<div id="show-user" class="scaffold-show" role="main">
    <h1>
        <g:message code="default.show.label" args="[entityName]" />${userInstance.toString() }
    </h1>
    <g:if test="${flash.message}">
        <div class="message" role="status">
            ${flash.message}
        </div>
    </g:if>
    <ol class="property-list user">
<% System.out.println "user : " + userInstance %>
        <g:if test="${userInstance?.hauler}">
            <li class="fieldcontain"><span id="hauler-label"
                class="property-label"><g:message code="hauler.label"
                        default="Hauler" /></span> <span class="property-value"
                aria-labelledby="hauler-label"><g:link controller="hauler"
                        action="show" id="${userInstance?.hauler?.id}">
                        ${userInstance?.hauler?.encodeAsHTML()}
                    </g:link></span></li>
        </g:if>

        <g:if test="${userInstance?.username}">
            <li class="fieldcontain"><span id="username-label"
                class="property-label"><g:message code="user.username.label"
                        default="Username" /></span> <span class="property-value"
                aria-labelledby="username-label"><g:fieldValue
                        bean="${userInstance}" field="username" /></span></li>
        </g:if>

        <g:if test="${userInstance?.password}">
            <li class="fieldcontain"><span id="password-label"
                class="property-label"><g:message code="user.password.label"
                        default="Password" /></span> <span class="property-value"
                aria-labelledby="password-label"><g:fieldValue
                        bean="${userInstance}" field="password" /></span></li>
        </g:if>

        <g:if test="${userInstance?.firstName}">
            <li class="fieldcontain"><span id="firstName-label"
                class="property-label"><g:message
                        code="user.firstName.label" default="First Name" /></span> <span
                class="property-value" aria-labelledby="firstName-label"><g:fieldValue
                        bean="${userInstance}" field="firstName" /></span></li>
        </g:if>

        <g:if test="${userInstance?.lastName}">
            <li class="fieldcontain"><span id="lastName-label"
                class="property-label"><g:message code="user.lastName.label"
                        default="Last Name" /></span> <span class="property-value"
                aria-labelledby="lastName-label"><g:fieldValue
                        bean="${userInstance}" field="lastName" /></span></li>
        </g:if>

        <g:if test="${userInstance?.role}">
            <li class="fieldcontain"><span id="role-label"
                class="property-label"><g:message code="user.role.label"
                        default="Role" /></span> <span class="property-value"
                aria-labelledby="role-label"><g:fieldValue
                        bean="${userInstance}" field="role" /></span></li>
        </g:if>


        <g:if test="${userInstance?.createdJobs}">
            <li class="fieldcontain"><span id="createdJobs-label"
                class="property-label"><g:message
                        code="user.createdJobs.label" default="Created Jobs" /></span> <g:each
                    in="${userInstance.createdJobs}" var="c">
                    <span class="property-value" aria-labelledby="createdJobs-label"><g:link
                            controller="job" action="show" id="${c.id}">
                            ${c?.encodeAsHTML()}
                        </g:link></span>
                </g:each></li>
        </g:if>

        <g:if test="${userInstance?.createdJobs}">
            <li class="fieldcontain"><span id="createdJobs-label"
                class="property-label"><g:message
                        code="user.createdJobs.label" default="Created Jobs" /></span> <g:each
                    in="${userInstance.createdJobs}" var="c">
                    <span class="property-value" aria-labelledby="createdJobs-label"><g:link
                            controller="job" action="show" id="${c.id}">
                            ${c?.encodeAsHTML()}
                        </g:link></span>
                </g:each></li>
        </g:if>

    </ol>
    <g:form url="[resource:userInstance, action:'delete']" method="DELETE">
        <fieldset class="buttons">
            <g:link class="edit" action="edit" resource="${userInstance}">
                <g:message code="default.button.edit.label" default="Edit" />
            </g:link>
            <g:actionSubmit class="delete" 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?')}');" />
        </fieldset>
    </g:form>
</div>

And the controller:

import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional

@Transactional(readOnly = true)
class UserController {

def beforeInterceptor = [action: this.&auth]

def auth = {
    if(!session.user) {
        redirect(controller:"login", action: "index")
    }
}

static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
boolean isAdmin



def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    model:[userInstanceList: User.list(params),userInstanceCount: User.count()]
}

def show(User userInstance) {
    println 'User: ' + userInstance
    respond userInstance
}

def create() {
    respond new User(params)
}

@Transactional
def save(User userInstance) {
    if (userInstance == null) {
        notFound()
        return
    }

    if (userInstance.hasErrors()) {
        respond userInstance.errors, view:'create'
        return
    }

    userInstance.save flush:true

    request.withFormat {
        form {
            flash.message = message(code: 'default.created.message', args: [message(code: 'userInstance.label', default: 'User'), userInstance.id])
            redirect userInstance
        }
        '*' { respond userInstance, [status: CREATED] }
    }
}

def edit(User userInstance) {
    respond userInstance
}

@Transactional
def update(User userInstance) {
    if (userInstance == null) {
        notFound()
        return
    }

    if (userInstance.hasErrors()) {
        respond userInstance.errors, view:'edit'
        return
    }

    userInstance.save flush:true

    request.withFormat {
        form {
            flash.message = message(code: 'default.updated.message', args: [message(code: 'User.label', default: 'User'), userInstance.id])
            redirect userInstance
        }
        '*'{ respond userInstance, [status: OK] }
    }
}

@Transactional
def delete(User userInstance) {

    if (userInstance == null) {
        notFound()
        return
    }

    userInstance.delete flush:true

    request.withFormat {
        form {
            flash.message = message(code: 'default.deleted.message', args: [message(code: 'User.label', default: 'User'), userInstance.id])
            redirect action:"index", method:"GET"
        }
        '*'{ render status: NO_CONTENT }
    }
}

protected void notFound() {
    request.withFormat {
        form {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'userInstance.label', default: 'User'), params.id])
            redirect action: "index", method: "GET"
        }
        '*'{ render status: NOT_FOUND }
    }
}
}

Now everything works fine in the UserController, the right User instance is always passed to the view. But the gsp receives null for all objects, except for the two admins, they are rendered correctly. At first I thought its because the view cant handle subclasses, but the Dispo User is also not shown. Changing the role of the other instances still wont display them. Any suggestions what to do?

Upvotes: 3

Views: 1646

Answers (1)

sebnukem
sebnukem

Reputation: 8323

It's an old question but I just ran into the same problem and "fixed" it by adding a model argument to the respond statement:

def show(User userInstance) {
    println 'User: ' + userInstance
    respond userInstance, model:[userInstance:userInstance]
}

In my case, the "User" class is a base class while I'm actually dealing with its inherited classes in the base class controller. respond is completely confused by that.

Upvotes: 3

Related Questions