You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Ben Pope <be...@REMOVE_MEhotmail.com> on 2005/03/13 18:12:16 UTC

CForms Binding - Cross Referenced Data (duplicate of post on users)

Hi,

First of, sorry for the post here, but I've asked a few times on users and
not had this solved, so I'm gonna cross my fingers and post here:

This is something I've been struggling with, on and off, for some time now.

Assume I have some data as follows:

<project>
   <people>
      <person id="0">
         <name>Me</name>
      </person>
      <person id="1">
         <name>You</name>
      </person>
      <person id="2">
         <name>Him</name>
      </person>
   </people>
   <rooms>
      <room id="0">
         <name>Lounge</name>
         <person idref="0"/>
         <person idref="1"/>
      </room>
      <room id="1">
         <name>Kitchen</name>
         <person idref="2"/>
      </room>
   </rooms>
</project>

That describes a list of people which are in a particular room, so Me and
You are in the Lounge and Him is in the Kitchen.

I want to have a form that displays, for a given room, a list of the people
in it.

A repeater is obviously the first choice, but there are a few
counter-intuitive thingsd going on:

I need to be able to modify the name of the person.  Simply running the
repeater over the rooms is not enough.

Adding a row needs to add an rooms/room/person with an idref - I'll then use
client side javascript and XMLHTTP to allow the user to select a person (by
id) to fit in the space, as all the people are predefined.  I do not want to
have the repeater add /people/persons.

I've toyed with a few ideas:

id is passed in as a parameter, and this code is actually in a stylesheet -
but ignore that for now.

<fb:repeater id="people" parent-path="/project/rooms/room[@id={$id}]"
row-path="/project/people/person[@id=/project/rooms/room[@id={$id}]/person/@
idref]">

This "solution" has the result of being perfect for load.  However, when I
save it, it breaks, because I can't have an xpath predicate.  So what do I
set the row-path-insert to?

If I set row-path-insert="/project/people/person" and bind the id (which is
fb:identity) in both directions, /project/people/ ends up correct, but
/project/rooms/room/ doesn't get updated.

I've tried
      <fb:on-insert-row>
         <fb:context path="/project/rooms/room[@id='0']">
            <fb:insert-node>   <person idref="5"/>
      </fb:insert-node>

But then I get a new node in both /rooms/room[@id='0']/ which is correct,
and another node created in /project/people/ which is the usual binding for
person and is a copy of the existing one.  This doesn't seem correct to me.

Hmm, I've had a little play with fb:javascript but I don't know what I'm
doing... Ideally I would remove all the /rooms/room[@id='x']/person fields
and repopulate with the list of ids in the repeater.  I can't seem to work
out the correct APIs, it always says that such and such method doesn't exist
- can anybody point me in the right direction?

I've toyed with the idea of having two repeaters, and updating one from the
other but it sounds like a recipe for disaster.

Another idea was to have a play around with it in flow, but I suspect I'd
end up with exactly the same problems I have using fb:javascript, with the
disadvantage of distributing the binding code.

Any help is much appreciated, I'm confused by the number of options and
multitude of interfaces.

I can't be the only person working with cross-referenced data!  I'm either
missing something or the repeater just doesn't understand this construct - I
wonder if Sylvain has any ideas on this.

I also started writing my own Repeater (well I copied the existing one,
renamed it, changed some of it's parameters and managed to get Cforms to use
it), but running through with eclipse in debug mode doesn't really throw any
light on the subject - it's all too confusing for me.

Heeeelp!

Ben

RE: CForms Binding - Cross Referenced Data (duplicate of post on users)

Posted by Ben Pope <be...@REMOVE_MEhotmail.com>.
Sylvain Wallez wrote:

> Ben Pope wrote:
> 
> ><project>
> >   <people>
> >      <person id="0">
> >         <name>Me</name>
> >      </person>
> >      <person id="1">
> >         <name>You</name>
> >      </person>
> >      <person id="2">
> >         <name>Him</name>
> >      </person>
> >   </people>
> >   <rooms>
> >      <room id="0">
> >         <name>Lounge</name>
> >         <person idref="0"/>
> >         <person idref="1"/>
> >      </room>
> >      <room id="1">
> >         <name>Kitchen</name>
> >         <person idref="2"/>
> >      </room>
> >   </rooms>
> ></project>
> >
> >That describes a list of people which are in a particular 
> room, so Me 
> >and You are in the Lounge and Him is in the Kitchen.
> >
> >I want to have a form that displays, for a given room, a list of the 
> >people in it.

<SNIP />

> Hmm... I hope I'm not the only one to do fancy CForms stuff :-/
> 
> Your room repeater links to existing people through their ID 
> but should 
> display their name. This is typically a selection-list with IDs as 
> labels and names as values.

Ahh yes.  That makes sense for name.  My first reaction is how do I easily
add fields such as age, favourite colour etc, and have those bind as well.

If I run a repeater over /rooms/person I want bindings that are equivalent
to:

<fb:value id="name" path="/project/people/person[@id=current()/idref]/name"
/>

<fb:value id="age" path="/project/people/person[@id=current()/idref]/age" />

Which would only be valid in an xslt, of course.

As mentioned before, I started work on an "xref-repeater" which would
understand my requirements - it's syntax was something like:

<fb:xref-repeater id="people"
    source-parent-path="/project/rooms[@id='0']"
    source-row-path="person"
    target-parent-path="/project/people"
    target-row-path="person" />

It would then likely need a source-identity and target-identity, or some
other way of specifying the id=idref relationship.

Unfortunately I didn't get very far with it as I don't really understand the
o.a.c.forms.binding package, despite stepping through it in eclipse.

> About the ability to add new names at any level, this looks like a 
> combo-box (editable dropdown). We've hacked such a thing for 
> one of our 
> projects, and basically the idea is to have a single widget 
> be rendered 
> as two inputs.
> 
> Say you have
> <fd:field id="personref">
>   <fd:selection-list src="cocoon:/person-selection-list"/>
> </fd:field>

That's identical (except names) to something I have to display a
selectionlist of person/names now. 

> The editable combobox renders this as :
> - a dropdown list of name "personref" with the contents of the 
> selection-list
> - an input of name "personref.label".
> 
> When the user chooses in the dropdown, the selected value is 
> copied to personref.label.

OK, I'm with you.

> When the user types in the personref.label input, a new 
> option is added 
> to the dropdown, having "new" as a value, and the typed text as label.

OK.

> On form submit, the flowscript checks if "personref" equals 
> "new" and if 
> true creates a new person with the name in the 
> "personref.label" request 
> parameter and replaces the "new" value with the newly created id.
> 
> Binding can then go on as usual.
> 
> This is a bit hacky, but it works :-)

That's excellent, Sylvain.  I'll give it a go later.

Re: CForms Binding - Cross Referenced Data (duplicate of post on users)

Posted by Sylvain Wallez <sy...@apache.org>.
Ben Pope wrote:

>Hi,
>
>First of, sorry for the post here, but I've asked a few times on users and
>not had this solved, so I'm gonna cross my fingers and post here:
>
>This is something I've been struggling with, on and off, for some time now.
>
>Assume I have some data as follows:
>
><project>
>   <people>
>      <person id="0">
>         <name>Me</name>
>      </person>
>      <person id="1">
>         <name>You</name>
>      </person>
>      <person id="2">
>         <name>Him</name>
>      </person>
>   </people>
>   <rooms>
>      <room id="0">
>         <name>Lounge</name>
>         <person idref="0"/>
>         <person idref="1"/>
>      </room>
>      <room id="1">
>         <name>Kitchen</name>
>         <person idref="2"/>
>      </room>
>   </rooms>
></project>
>
>That describes a list of people which are in a particular room, so Me and
>You are in the Lounge and Him is in the Kitchen.
>
>I want to have a form that displays, for a given room, a list of the people
>in it.
>
>A repeater is obviously the first choice, but there are a few
>counter-intuitive thingsd going on:
>
>I need to be able to modify the name of the person.  Simply running the
>repeater over the rooms is not enough.
>
>Adding a row needs to add an rooms/room/person with an idref - I'll then use
>client side javascript and XMLHTTP to allow the user to select a person (by
>id) to fit in the space, as all the people are predefined.  I do not want to
>have the repeater add /people/persons.
>
>I've toyed with a few ideas:
>
>id is passed in as a parameter, and this code is actually in a stylesheet -
>but ignore that for now.
>
><fb:repeater id="people" parent-path="/project/rooms/room[@id={$id}]"
>row-path="/project/people/person[@id=/project/rooms/room[@id={$id}]/person/@
>idref]">
>
>This "solution" has the result of being perfect for load.  However, when I
>save it, it breaks, because I can't have an xpath predicate.  So what do I
>set the row-path-insert to?
>
>If I set row-path-insert="/project/people/person" and bind the id (which is
>fb:identity) in both directions, /project/people/ ends up correct, but
>/project/rooms/room/ doesn't get updated.
>
>I've tried
>      <fb:on-insert-row>
>         <fb:context path="/project/rooms/room[@id='0']">
>            <fb:insert-node>   <person idref="5"/>
>      </fb:insert-node>
>
>But then I get a new node in both /rooms/room[@id='0']/ which is correct,
>and another node created in /project/people/ which is the usual binding for
>person and is a copy of the existing one.  This doesn't seem correct to me.
>
>Hmm, I've had a little play with fb:javascript but I don't know what I'm
>doing... Ideally I would remove all the /rooms/room[@id='x']/person fields
>and repopulate with the list of ids in the repeater.  I can't seem to work
>out the correct APIs, it always says that such and such method doesn't exist
>- can anybody point me in the right direction?
>
>I've toyed with the idea of having two repeaters, and updating one from the
>other but it sounds like a recipe for disaster.
>
>Another idea was to have a play around with it in flow, but I suspect I'd
>end up with exactly the same problems I have using fb:javascript, with the
>disadvantage of distributing the binding code.
>
>Any help is much appreciated, I'm confused by the number of options and
>multitude of interfaces.
>
>I can't be the only person working with cross-referenced data!  I'm either
>missing something or the repeater just doesn't understand this construct - I
>wonder if Sylvain has any ideas on this.
>  
>

Hmm... I hope I'm not the only one to do fancy CForms stuff :-/

Your room repeater links to existing people through their ID but should 
display their name. This is typically a selection-list with IDs as 
labels and names as values.

About the ability to add new names at any level, this looks like a 
combo-box (editable dropdown). We've hacked such a thing for one of our 
projects, and basically the idea is to have a single widget be rendered 
as two inputs.

Say you have
<fd:field id="personref">
  <fd:selection-list src="cocoon:/person-selection-list"/>
</fd:field>

The editable combobox renders this as :
- a dropdown list of name "personref" with the contents of the 
selection-list
- an input of name "personref.label".

When the user chooses in the dropdown, the selected value is copied to 
personref.label.

When the user types in the personref.label input, a new option is added 
to the dropdown, having "new" as a value, and the typed text as label.

On form submit, the flowscript checks if "personref" equals "new" and if 
true creates a new person with the name in the "personref.label" request 
parameter and replaces the "new" value with the newly created id.

Binding can then go on as usual.

This is a bit hacky, but it works :-)

Hope this helps,
Sylvain

-- 
Sylvain Wallez                                  Anyware Technologies
http://www.apache.org/~sylvain           http://www.anyware-tech.com
{ XML, Java, Cocoon, OpenSource }*{ Training, Consulting, Projects }