gpc / fields

a spiritual successor to the bean-fields plugin
http://gpc.github.io/fields/
84 stars 105 forks source link

Using g:render with model attribute set inside f:with tags throws java.util.EmptyStackException #194

Open denis111 opened 9 years ago

denis111 commented 9 years ago
<f:with..>
<g:render template="my-template" model="${[bean:'user',property:'name']}"/>
</f:with>

throws excepstion at:
Around line 80 of grails-app/taglib/grails/plugin/formfields/FormFieldsTagLib.groovy

77:            beanStack.push(beanPrefix)
78:         out << body()
79:     } finally {
80:            beanStack.pop()
81:     }
82: }
ghost commented 9 years ago

Could you please provide a sample application or unit test that reproduces this issue? Please also include the stacktrace of the exception.

denis111 commented 9 years ago

Here's the sample project https://drive.google.com/file/d/0B1TIQMQPXvOZemtpQWFUVV9ZeGs/view?usp=sharing . Just navigate to http://localhost:8080/fwith/my/index

davidkron commented 7 years ago

I can confirm that this issue is still present in version 2.1.3 of the fields plugin. In my case the model attribute doesn't even matter. A simple <g:render template="something"/> already causes the exception.

Edit: I believe I have found to source of this problem. This is not necessarily an error in the fields plugin itself, but an implementation error in Grails (not sure which versions are affected, I'm using 3.1.x).

In FormFieldsTagLib#getBeanStack() there is a call to pageScope.hasVariable(STACK_PAGE_SCOPE_VARIABLE). The pageScope variable is of type GroovyPageBinding, which is a hierarchical data structure: with each call to <g:render>, a child pageScope is created with a reference to the parent scope. The Problem: the method hasVariable was never overloaded to consider the hierarchical data structure, which means it will only find variables from child scope, but the bean stack variable was set in the parent scope! Because of this hasVariable always returns false, which results in FormFieldsTagLib#getBeanStack() creating a new empty stack. Setting the page scope variable with the new stack actually works in the hierarchy and overrides the existing one, which finally results in an EmptyStackException because the stack was replaced by an empty one.

I will open an issue in the grails project for this error, as it does seem to be an error in the framework and not fields plugin.

I managed to fix the error in my application with the following groovy metaprogramming hack:

class BootStrap {
    def init = {

        FormFieldsTagLib.metaClass.getBeanStack = { getBeansStackPatched(delegate) }

    }

    private FormFieldsTagLib.BeanAndPrefixStack getBeansStackPatched(FormFieldsTagLib tagLib) {
        Map variables = tagLib.pageScope.variables

        if (!variables.containsKey(FormFieldsTagLib.STACK_PAGE_SCOPE_VARIABLE)) {
            variables[FormFieldsTagLib.STACK_PAGE_SCOPE_VARIABLE] = new FormFieldsTagLib.BeanAndPrefixStack()
        }

        return variables[FormFieldsTagLib.STACK_PAGE_SCOPE_VARIABLE]
    }
}