Quantcast

Databinding Collection of non domain objects

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Databinding Collection of non domain objects

mkwhit
So I've read through the documentation databinding[1] and most of it
pertains to binding to Domain classes.  I have a use case where I want
to bind to a collection of model objects that are not domain classes.
The format for the classes looks like this:

class Foo{
   def String someString
}

class FooEntries{
   def List<Foo> foos
}

I then have a view.gsp that looks like the following:

    <g:form id="${path}">
        <g:each in="${fooEntries.foos}" var="entry" status="count">
            <li>
                <div>
                    <g:textField name="foos[${count}].someString"
value="${entry.someString}" />
                </div>
        </g:each>
        //some submit buttons
    </g:form>

The controller looks like:

class FooController{
def view = {
  def entries = //some logic to retrieve FooEntries
  ["fooEntries": entries]
}

def submit{
FooEntries entries = FooEntries()
entries.foos = new LinkedList<Foo>()
bindData(entries,params, "fooEntries")
}
}

The above code doesn't work because I get "nested exception is
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0" when it is
trying to bind to the list.  If I modify the submit closure to "prime"
the linked list with objects it will work.  The problem is that the
size of the list will be dynamic.  So I won't always know the size of
the list to be able to prime it.  I can embed a hidden input the view
that specifies the "count" value but that seems less than ideal.

So my question is, how can I get collection databinding working
without priming the size of the collection?  Is there a better way I
could be doing the databinding?

I'd also like to remove the "grouper" model object that's purpose is
just to make the databinding to a list easier.  So if anyone has
suggestions on how to bind directly to the list and remove the
FooEntries class that would be helpful.

Thanks,
Micah Whitacre

[1] - http://grails.org/doc/latest/guide/single.html#6.1.6 Data Binding

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Databinding Collection of non domain objects

Dana
I made a test project out of your sample code and was able to get
binding to work. I've attached my sample project (see
FooEntriesController and navigate to /fooEntries/many after starting the
app), but there are two key changes I made to your example to get it to
work. FooEntries also needs the line:

static hasMany = [foos: Foo]

and the bindData line in the controller should look like:

bindData(entries, params, [include: ['foos*']])

(I verified that the bindData include was working by adding another
property to FooEntries and making sure it didn't get saved.) The test
project was hastily put together so please let me know if you have any
questions about it.

-Dana

On 2/4/11 9:18 AM, Micah Whitacre wrote:

> So I've read through the documentation databinding[1] and most of it
> pertains to binding to Domain classes.  I have a use case where I want
> to bind to a collection of model objects that are not domain classes.
> The format for the classes looks like this:
>
> class Foo{
>     def String someString
> }
>
> class FooEntries{
>     def List<Foo>  foos
> }
>
> I then have a view.gsp that looks like the following:
>
>      <g:form id="${path}">
>          <g:each in="${fooEntries.foos}" var="entry" status="count">
>              <li>
>                  <div>
>                      <g:textField name="foos[${count}].someString"
> value="${entry.someString}" />
>                  </div>
>          </g:each>
>          //some submit buttons
>      </g:form>
>
> The controller looks like:
>
> class FooController{
> def view = {
>    def entries = //some logic to retrieve FooEntries
>    ["fooEntries": entries]
> }
>
> def submit{
> FooEntries entries = FooEntries()
> entries.foos = new LinkedList<Foo>()
> bindData(entries,params, "fooEntries")
> }
> }
>
> The above code doesn't work because I get "nested exception is
> java.lang.IndexOutOfBoundsException: Index: 0, Size: 0" when it is
> trying to bind to the list.  If I modify the submit closure to "prime"
> the linked list with objects it will work.  The problem is that the
> size of the list will be dynamic.  So I won't always know the size of
> the list to be able to prime it.  I can embed a hidden input the view
> that specifies the "count" value but that seems less than ideal.
>
> So my question is, how can I get collection databinding working
> without priming the size of the collection?  Is there a better way I
> could be doing the databinding?
>
> I'd also like to remove the "grouper" model object that's purpose is
> just to make the databinding to a list easier.  So if anyone has
> suggestions on how to bind directly to the list and remove the
> FooEntries class that would be helpful.
>
> Thanks,
> Micah Whitacre
>
> [1] - http://grails.org/doc/latest/guide/single.html#6.1.6 Data Binding
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>      http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

databindingcollections-bug-report-04022011.zip (28K) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Databinding Collection of non domain objects

mkwhit
Thanks for the example and the quick feedback.  The one problem I have
with applying your solution is that the model objects are not domain
classes.  They exist inside of src/groovy and do not correspond to
anything stored in the underlying data store.  They are merely
convenience objects for passing information from the form to the
controller so the controller can do the processing.  What you have
provided aligns well with what the reference guide displays in section
6.1.6 ("Data Binding and Many-ended Associations") but not quite what
I'm looking for.

Thanks,
Micah

On Fri, Feb 4, 2011 at 1:18 PM, Dana Berke <[hidden email]> wrote:

> I made a test project out of your sample code and was able to get binding to
> work. I've attached my sample project (see FooEntriesController and navigate
> to /fooEntries/many after starting the app), but there are two key changes I
> made to your example to get it to work. FooEntries also needs the line:
>
> static hasMany = [foos: Foo]
>
> and the bindData line in the controller should look like:
>
> bindData(entries, params, [include: ['foos*']])
>
> (I verified that the bindData include was working by adding another property
> to FooEntries and making sure it didn't get saved.) The test project was
> hastily put together so please let me know if you have any questions about
> it.
>
> -Dana
>
> On 2/4/11 9:18 AM, Micah Whitacre wrote:
>>
>> So I've read through the documentation databinding[1] and most of it
>> pertains to binding to Domain classes.  I have a use case where I want
>> to bind to a collection of model objects that are not domain classes.
>> The format for the classes looks like this:
>>
>> class Foo{
>>    def String someString
>> }
>>
>> class FooEntries{
>>    def List<Foo>  foos
>> }
>>
>> I then have a view.gsp that looks like the following:
>>
>>     <g:form id="${path}">
>>         <g:each in="${fooEntries.foos}" var="entry" status="count">
>>             <li>
>>                 <div>
>>                     <g:textField name="foos[${count}].someString"
>> value="${entry.someString}" />
>>                 </div>
>>         </g:each>
>>         //some submit buttons
>>     </g:form>
>>
>> The controller looks like:
>>
>> class FooController{
>> def view = {
>>   def entries = //some logic to retrieve FooEntries
>>   ["fooEntries": entries]
>> }
>>
>> def submit{
>> FooEntries entries = FooEntries()
>> entries.foos = new LinkedList<Foo>()
>> bindData(entries,params, "fooEntries")
>> }
>> }
>>
>> The above code doesn't work because I get "nested exception is
>> java.lang.IndexOutOfBoundsException: Index: 0, Size: 0" when it is
>> trying to bind to the list.  If I modify the submit closure to "prime"
>> the linked list with objects it will work.  The problem is that the
>> size of the list will be dynamic.  So I won't always know the size of
>> the list to be able to prime it.  I can embed a hidden input the view
>> that specifies the "count" value but that seems less than ideal.
>>
>> So my question is, how can I get collection databinding working
>> without priming the size of the collection?  Is there a better way I
>> could be doing the databinding?
>>
>> I'd also like to remove the "grouper" model object that's purpose is
>> just to make the databinding to a list easier.  So if anyone has
>> suggestions on how to bind directly to the list and remove the
>> FooEntries class that would be helpful.
>>
>> Thanks,
>> Micah Whitacre
>>
>> [1] - http://grails.org/doc/latest/guide/single.html#6.1.6 Data Binding
>>
>> ---------------------------------------------------------------------
>> To unsubscribe from this list, please visit:
>>
>>     http://xircles.codehaus.org/manage_email
>>
>>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Databinding Collection of non domain objects

Dana
I'm sorry about that Micah. I'll try to read your whole question (or at
least the entire subject line) before answering this time...

I tried binding the form data to a command object with a property
List<Foo> foos, but I got the same IndexOutOfBoundsException as you. So,
without knowing how to proceed with binding associations, I looped
through the params object and bound one Foo at a time.

The src/groovy class:
class Foo {
     String someString
     String someOtherString
}

The GSP:
<g:form>
...
<% 5.times { count -> %>
<g:textField name="foos.${count}.someString"/>
<g:textField name="foos.${count}.someOtherString"/><br/>
<% } %>
</g:form>

The controller:
     def save = {
         List<Foo> foos = params.foos?.values()?.findAll {
             it instanceof GrailsParameterMap
         }.collect { fooParams ->
             def foo = new Foo()
             bindData(foo, fooParams, [include: ['someString',
'someOtherString']])
             foo // return the Foo
         }
         // do something with your foos
     }

It's not the prettiest solution. On the bright side, it doesn't need a
hidden count field in the form, and the counts don't even need to be
sequential: you could use Javascript to add or remove sets of fields and
not have to reset  ${count} (although it does need to be unique per foo).

-Dana

On 2/4/11 4:14 PM, Micah Whitacre wrote:

> Thanks for the example and the quick feedback.  The one problem I have
> with applying your solution is that the model objects are not domain
> classes.  They exist inside of src/groovy and do not correspond to
> anything stored in the underlying data store.  They are merely
> convenience objects for passing information from the form to the
> controller so the controller can do the processing.  What you have
> provided aligns well with what the reference guide displays in section
> 6.1.6 ("Data Binding and Many-ended Associations") but not quite what
> I'm looking for.
>
> Thanks,
> Micah
>
> On Fri, Feb 4, 2011 at 1:18 PM, Dana Berke<[hidden email]>  wrote:
>> I made a test project out of your sample code and was able to get binding to
>> work. I've attached my sample project (see FooEntriesController and navigate
>> to /fooEntries/many after starting the app), but there are two key changes I
>> made to your example to get it to work. FooEntries also needs the line:
>>
>> static hasMany = [foos: Foo]
>>
>> and the bindData line in the controller should look like:
>>
>> bindData(entries, params, [include: ['foos*']])
>>
>> (I verified that the bindData include was working by adding another property
>> to FooEntries and making sure it didn't get saved.) The test project was
>> hastily put together so please let me know if you have any questions about
>> it.
>>
>> -Dana
>>
>> On 2/4/11 9:18 AM, Micah Whitacre wrote:
>>> So I've read through the documentation databinding[1] and most of it
>>> pertains to binding to Domain classes.  I have a use case where I want
>>> to bind to a collection of model objects that are not domain classes.
>>> The format for the classes looks like this:
>>>
>>> class Foo{
>>>     def String someString
>>> }
>>>
>>> class FooEntries{
>>>     def List<Foo>    foos
>>> }
>>>
>>> I then have a view.gsp that looks like the following:
>>>
>>>      <g:form id="${path}">
>>>          <g:each in="${fooEntries.foos}" var="entry" status="count">
>>>              <li>
>>>                  <div>
>>>                      <g:textField name="foos[${count}].someString"
>>> value="${entry.someString}" />
>>>                  </div>
>>>          </g:each>
>>>          //some submit buttons
>>>      </g:form>
>>>
>>> The controller looks like:
>>>
>>> class FooController{
>>> def view = {
>>>    def entries = //some logic to retrieve FooEntries
>>>    ["fooEntries": entries]
>>> }
>>>
>>> def submit{
>>> FooEntries entries = FooEntries()
>>> entries.foos = new LinkedList<Foo>()
>>> bindData(entries,params, "fooEntries")
>>> }
>>> }
>>>
>>> The above code doesn't work because I get "nested exception is
>>> java.lang.IndexOutOfBoundsException: Index: 0, Size: 0" when it is
>>> trying to bind to the list.  If I modify the submit closure to "prime"
>>> the linked list with objects it will work.  The problem is that the
>>> size of the list will be dynamic.  So I won't always know the size of
>>> the list to be able to prime it.  I can embed a hidden input the view
>>> that specifies the "count" value but that seems less than ideal.
>>>
>>> So my question is, how can I get collection databinding working
>>> without priming the size of the collection?  Is there a better way I
>>> could be doing the databinding?
>>>
>>> I'd also like to remove the "grouper" model object that's purpose is
>>> just to make the databinding to a list easier.  So if anyone has
>>> suggestions on how to bind directly to the list and remove the
>>> FooEntries class that would be helpful.
>>>
>>> Thanks,
>>> Micah Whitacre
>>>
>>> [1] - http://grails.org/doc/latest/guide/single.html#6.1.6 Data Binding
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe from this list, please visit:
>>>
>>>      http://xircles.codehaus.org/manage_email
>>>
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe from this list, please visit:
>>
>>     http://xircles.codehaus.org/manage_email
>>
>>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>      http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Databinding Collection of non domain objects

paul@questern
In reply to this post by mkwhit
Try using something like this:

import org.apache.commons.collections.ListUtils
import org.apache.commons.collections.FactoryUtils

def submit{
List foos = ListUtils.lazyList([], FactoryUtils.instantiateFactory(Foo))
bindData(foos,params)
}

This works for me when binding request parameters to a list in a Command Object so I'm hoping it works for you in the controller.

The imports are already in the Grails framework so you don't need to go looking for them

This is the post I got my info from here

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Databinding Collection of non domain objects

mkwhit
To follow up, I still had trouble getting it to bind directly to the
list but I was able to get everything working using the grouper object
("Foos") and the lazy list.  Thanks for the help.

On Sat, Feb 5, 2011 at 10:14 AM, paul@questern <[hidden email]> wrote:

>
> Try using something like this:
>
> import org.apache.commons.collections.ListUtils
> import org.apache.commons.collections.FactoryUtils
>
> def submit{
> List foos = ListUtils.lazyList([], FactoryUtils.instantiateFactory(Foo))
> bindData(foos,params)
> }
>
> This works for me when binding request parameters to a list in a Command
> Object so I'm hoping it works for you in the controller.
>
> The imports are already in the Grails framework so you don't need to go
> looking for them
>
> This is the post I got my info from
> http://stateyourbizness.blogspot.com/2009/02/binding-to-collection-fields-on-command.html
> here
>
>
> --
> View this message in context: http://grails.1312388.n4.nabble.com/Databinding-Collection-of-non-domain-objects-tp3260578p3261963.html
> Sent from the Grails - user mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Loading...