Re: Newbie help with For and TextField components
Hi Daniel, thanks very much for this, it makes things much clearer (best explanation of it I've seen by a long chalk!). I'll take your advice and create a holder object for my string that uses a UUID to identify the strings. Thanks! jim Daniel Tabuenca wrote: Eventually I'm going to be saving the values in RDF - the values won't have a persistent key, they're just literal values. I'm also keen to avoid using any session persistence but will do if it's necessary. Can tapestry deal with simple values that don't have identity in this way? To understand this you need to know what that key is really used for. Say I have a Person Object... public class Person(){ public String name; public String id; } and two person Object sin a list; Person bob; Person alice; List personList; bob.setName("Bob"); bob.setId("1"); alice.setName("Alice"); alice.setId("2"); personList.add(bob); personList.add(alice); Now... when if I set my keyExpression to be person.id when tapestry renders out the form it stores a hidden field with the ids in order.. in this case it would have "1" and "2". Why is this important? Well you have to understand what happens during a rewind. It is nothing magical really and the simplistic way to explain is that your page just renders out again but with the form bindings reversed so in your case when it gets to that input field... instead of reading the "value" parameter with getName() it simply writes that paremeter with setName(). so say I got the page and it has a list of my current users * [ BOB ] * [ALICE ] now I want to change Bob's name to Robert so I edit. * [ROBERT] * [ALICE] and hit submit.. What happens if while I was changing the name on the form.. someone somehow change the original person list so that the names are backwards so the objects are { alice, bob}. If tapestry allowed it this would be bad! Because you meant to change Bob's name but as far as Tapestry is concerned you just instructed it to set the name on the first item on the list to "Robert". And if the order on the list was somehow changed it's your Alice object that will get it's name change. This is why tapestry stores extra information in the hidden field. What it does, is before actually calling setName() it will check that your keyExpression submitted with the form matches the keyExpression of the object from the list. If the list got changed this will be different and tapestry will detect this and throw you an exception so you don't accidentally delete or modify th wrong object. Of course... if you don't have a real key you can either make one up. Or if your oject converts automatically to a string tapestry will just store that in the hidden fields. Of course, if you don't care about what I just described or you know for certain that nobody will modify the list, you could set keyExpression to something constant so it always matches no matter what. I hope this explenation was useful. The whole rewind idea seemed magical to me until someone explained it to me like this. - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
Eventually I'm going to be saving the values in RDF - the values won't have a persistent key, they're just literal values. I'm also keen to avoid using any session persistence but will do if it's necessary. Can tapestry deal with simple values that don't have identity in this way? To understand this you need to know what that key is really used for. Say I have a Person Object... public class Person(){ public String name; public String id; } and two person Object sin a list; Person bob; Person alice; List personList; bob.setName("Bob"); bob.setId("1"); alice.setName("Alice"); alice.setId("2"); personList.add(bob); personList.add(alice); Now... when if I set my keyExpression to be person.id when tapestry renders out the form it stores a hidden field with the ids in order.. in this case it would have "1" and "2". Why is this important? Well you have to understand what happens during a rewind. It is nothing magical really and the simplistic way to explain is that your page just renders out again but with the form bindings reversed so in your case when it gets to that input field... instead of reading the "value" parameter with getName() it simply writes that paremeter with setName(). so say I got the page and it has a list of my current users * [ BOB ] * [ALICE ] now I want to change Bob's name to Robert so I edit. * [ROBERT] * [ALICE] and hit submit.. What happens if while I was changing the name on the form.. someone somehow change the original person list so that the names are backwards so the objects are { alice, bob}. If tapestry allowed it this would be bad! Because you meant to change Bob's name but as far as Tapestry is concerned you just instructed it to set the name on the first item on the list to "Robert". And if the order on the list was somehow changed it's your Alice object that will get it's name change. This is why tapestry stores extra information in the hidden field. What it does, is before actually calling setName() it will check that your keyExpression submitted with the form matches the keyExpression of the object from the list. If the list got changed this will be different and tapestry will detect this and throw you an exception so you don't accidentally delete or modify th wrong object. Of course... if you don't have a real key you can either make one up. Or if your oject converts automatically to a string tapestry will just store that in the hidden fields. Of course, if you don't care about what I just described or you know for certain that nobody will modify the list, you could set keyExpression to something constant so it always matches no matter what. I hope this explenation was useful. The whole rewind idea seemed magical to me until someone explained it to me like this. - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
Well it's because you are using strings directly. Here's what the equivalent of what is going on in JAVA: List names = NAMES; for( String name : names ){ name = " NEW VALUE FROM INPUT FIELD"; // but strings are immutable so name has a new string reference and does not modify // the one that is part of the list. } What happens is your new name is stored only in the "name" property and never makes it back to your list try printing out getName() and you will see that it is probably set to your last name on the list. On 1/10/07, Jim Downing <[EMAIL PROTECTED]> wrote: Hi Daniel, thanks for the help! Daniel Tabuenca wrote: > The Chart example the plotValues property is persisted: > > class="org.apache.tapestry.workbench.chart.ChartPage"> > > > > ... > > > I am assuming you are not persisting since from your logs: > > 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. > Rewind? true > 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names > list It's true, I wasn't persisting them. To be ultra-simplistic I've done this by storing them back in a static field: - Home.java: ... public static List NAMES = new ArrayList(); public void pageBeginRender(PageEvent event) { LOG.info("Begin render. Rewind? " + getRequestCycle().isRewinding() +". Names: "+ getNames()); LOG.info("Initializing names list to "+ NAMES); setNames(NAMES); } public void submit() { LOG.info("Submitted. List is: " + getNames()); NAMES = getNames(); } public void add() { LOG.info("Adding a blank name to " + getNames()); List nms = getNames(); nms.add(""); setNames(nms); } ... Unfortunately that doesn't seem to be at the heart of the problem - the list now grows as desired when the 'add' listener is called, but the values in the list aren't changed. It looks like the form values aren't correctly set after the rewind, e.g. in the log excerpt below I'd be expecting to see the (non-blank) form values in the message at the highlighted point: - INFO com.example.pages.Home - Begin render. Rewind? true. Names: null INFO com.example.pages.Home - Initializing names list to [, , ] --> INFO com.example.pages.Home - Submitted. List is: [, , ] <-- INFO com.example.pages.Home - Begin render. Rewind? false. Names: [, , ] INFO com.example.pages.Home - Initializing names list to [, , ] Eventually I'm going to be saving the values in RDF - the values won't have a persistent key, they're just literal values. I'm also keen to avoid using any session persistence but will do if it's necessary. Can tapestry deal with simple values that don't have identity in this way? Thanks, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
Hi Firas, Firas Adiler wrote: I'm a bit puzzled myself, but how did you manage to run the app without providing setters for 'idx' and 'name' fields: public abstract void setIdx(int idx) public abstract void setName(String name)? Seems to work fine just defining the getter in other situations - I think the setter appears by reflection magic. jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
Hi Daniel, thanks for the help! Daniel Tabuenca wrote: The Chart example the plotValues property is persisted: class="org.apache.tapestry.workbench.chart.ChartPage"> ... I am assuming you are not persisting since from your logs: 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names list It's true, I wasn't persisting them. To be ultra-simplistic I've done this by storing them back in a static field: - Home.java: ... public static List NAMES = new ArrayList(); public void pageBeginRender(PageEvent event) { LOG.info("Begin render. Rewind? " + getRequestCycle().isRewinding() +". Names: "+ getNames()); LOG.info("Initializing names list to "+ NAMES); setNames(NAMES); } public void submit() { LOG.info("Submitted. List is: " + getNames()); NAMES = getNames(); } public void add() { LOG.info("Adding a blank name to " + getNames()); List nms = getNames(); nms.add(""); setNames(nms); } ... Unfortunately that doesn't seem to be at the heart of the problem - the list now grows as desired when the 'add' listener is called, but the values in the list aren't changed. It looks like the form values aren't correctly set after the rewind, e.g. in the log excerpt below I'd be expecting to see the (non-blank) form values in the message at the highlighted point: - INFO com.example.pages.Home - Begin render. Rewind? true. Names: null INFO com.example.pages.Home - Initializing names list to [, , ] --> INFO com.example.pages.Home - Submitted. List is: [, , ] <-- INFO com.example.pages.Home - Begin render. Rewind? false. Names: [, , ] INFO com.example.pages.Home - Initializing names list to [, , ] Eventually I'm going to be saving the values in RDF - the values won't have a persistent key, they're just literal values. I'm also keen to avoid using any session persistence but will do if it's necessary. Can tapestry deal with simple values that don't have identity in this way? Thanks, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
The Chart example the plotValues property is persisted: ... I am assuming you are not persisting since from your logs: 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names list The list gest reinitialized on each request. I think you may be misunderstanding the @For loop. @For inside a form will not create a List or populate it for you. @For works with an existing list and in your case the existing list is a new list on each beginRender. @For then iterates over an empty list which means nothing inside it will ever get executed (all your form input fields never get rewound). The keyExpression is used only to verify that the list that you are working with has not changed since you rendered the page. In your case it HAS! When you rendered the page originally your list had a blank name in it. On the rewind it has no elements. Maybe tapestry should be smarter and throw an error when the size of the list doesn't match the size from when it was rendered. On 1/10/07, Jim Downing <[EMAIL PROTECTED]> wrote: Hi Jesse, Jesse Kuhnert wrote: > I think the problem with this approach is that the For component has > no way of uniquely identifying your list values. > > I would try using the keyExpression or converter parameters of the For > component to do this instead. > http://tapestry.apache.org/tapestry4.1/components/general/for.html Thanks for the pointers, but I'm still having problems, using either a custom converter or a keyExpression (using "toString()"). As I understand it either of these methods rely on there being a single reference to the object that doesn't change between requests. This obviously doesn't exist for value objects. What I'm finding confusing is that this looks to me just like the example in the charts pane of the tapestry workbench demo; there a list of PlotValues is edited without keyExpresssion or a converter. Why is this different? Would a reasonable hack be to bind the value of the text field input to a property with a concrete setter that used the @For index to build the new list items manually? Thanks, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
RE: Newbie help with For and TextField components
Jim, I'm a bit puzzled myself, but how did you manage to run the app without providing setters for 'idx' and 'name' fields: public abstract void setIdx(int idx) public abstract void setName(String name)? Regards, -Original Message- From: Jim Downing [mailto:[EMAIL PROTECTED] Sent: Tuesday, January 09, 2007 3:47 PM To: users@tapestry.apache.org Subject: Newbie help with For and TextField components Hi, I'm trying to get some simple list editing working using For and TextField but not having much luck. Home.html: - http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en"> Example app Hello world! List of names: 1 Home.java public abstract class Home extends BasePage implements PageBeginRenderListener { private static final Logger LOG = Logger.getLogger(Home.class); public void pageBeginRender(PageEvent event) { LOG.info("Begin render. Rewind? " + getRequestCycle().isRewinding()); if(getNames()== null) { LOG.info("Initializing names list"); setNames(new ArrayList()); } } public void submit() { LOG.info("Submitted. List is: " + getNames()); } public void add() { LOG.info("Adding a blank name to "+ getNames()); List nms = getNames(); nms.add(""); setNames(nms); } public abstract List getNames(); public abstract void setNames(List nms); public abstract String getName(); public abstract int getIdx(); } When I click the "Add" button I get: - 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9627423 [btpool0-3] INFO com.example.pages.Home - Adding a blank name to [] 9627424 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9627424 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false Then changing the value in the form input and clicking submit produces this: - 9638858 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9638858 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9638859 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9638859 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false I'm puzzled - why hasn't the list been populated from the TextFields in the For loop? Any pointers gratefully received. cheers, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
I don't get it. What are you trying to do? You aren't persisting your list anywhere so at each render or rewind your lists starts off null. so by the time it gets to the @For loop the @For loop has nothing to iterate over. Am I missing something? On 1/9/07, Jim Downing <[EMAIL PROTECTED]> wrote: Hi, I'm trying to get some simple list editing working using For and TextField but not having much luck. Home.html: - http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en"> Example app Hello world! List of names: 1 Home.java public abstract class Home extends BasePage implements PageBeginRenderListener { private static final Logger LOG = Logger.getLogger(Home.class); public void pageBeginRender(PageEvent event) { LOG.info("Begin render. Rewind? " + getRequestCycle().isRewinding()); if(getNames()== null) { LOG.info("Initializing names list"); setNames(new ArrayList()); } } public void submit() { LOG.info("Submitted. List is: " + getNames()); } public void add() { LOG.info("Adding a blank name to "+ getNames()); List nms = getNames(); nms.add(""); setNames(nms); } public abstract List getNames(); public abstract void setNames(List nms); public abstract String getName(); public abstract int getIdx(); } When I click the "Add" button I get: - 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9627423 [btpool0-3] INFO com.example.pages.Home - Adding a blank name to [] 9627424 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9627424 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false Then changing the value in the form input and clicking submit produces this: - 9638858 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9638858 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9638859 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9638859 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false I'm puzzled - why hasn't the list been populated from the TextFields in the For loop? Any pointers gratefully received. cheers, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
Hi Jesse, Jesse Kuhnert wrote: I think the problem with this approach is that the For component has no way of uniquely identifying your list values. I would try using the keyExpression or converter parameters of the For component to do this instead. http://tapestry.apache.org/tapestry4.1/components/general/for.html Thanks for the pointers, but I'm still having problems, using either a custom converter or a keyExpression (using "toString()"). As I understand it either of these methods rely on there being a single reference to the object that doesn't change between requests. This obviously doesn't exist for value objects. What I'm finding confusing is that this looks to me just like the example in the charts pane of the tapestry workbench demo; there a list of PlotValues is edited without keyExpresssion or a converter. Why is this different? Would a reasonable hack be to bind the value of the text field input to a property with a concrete setter that used the @For index to build the new list items manually? Thanks, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Newbie help with For and TextField components
I think the problem with this approach is that the For component has no way of uniquely identifying your list values. I would try using the keyExpression or converter parameters of the For component to do this instead. http://tapestry.apache.org/tapestry4.1/components/general/for.html On 1/9/07, Jim Downing <[EMAIL PROTECTED]> wrote: Hi, I'm trying to get some simple list editing working using For and TextField but not having much luck. Home.html: - http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en"> Example app Hello world! List of names: 1 Home.java public abstract class Home extends BasePage implements PageBeginRenderListener { private static final Logger LOG = Logger.getLogger(Home.class); public void pageBeginRender(PageEvent event) { LOG.info("Begin render. Rewind? " + getRequestCycle().isRewinding()); if(getNames()== null) { LOG.info("Initializing names list"); setNames(new ArrayList()); } } public void submit() { LOG.info("Submitted. List is: " + getNames()); } public void add() { LOG.info("Adding a blank name to "+ getNames()); List nms = getNames(); nms.add(""); setNames(nms); } public abstract List getNames(); public abstract void setNames(List nms); public abstract String getName(); public abstract int getIdx(); } When I click the "Add" button I get: - 9627410 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9627411 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9627423 [btpool0-3] INFO com.example.pages.Home - Adding a blank name to [] 9627424 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9627424 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false Then changing the value in the form input and clicking submit produces this: - 9638858 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? true 9638858 [btpool0-3] INFO com.example.pages.Home - Initializing names list 9638859 [btpool0-3] INFO com.example.pages.Home - Submitted. List is: [] 9638859 [btpool0-3] INFO com.example.pages.Home - Begin render. Rewind? false I'm puzzled - why hasn't the list been populated from the TextFields in the For loop? Any pointers gratefully received. cheers, jim - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] -- Jesse Kuhnert Tapestry/Dojo team member/developer Open source based consulting work centered around dojo/tapestry/tacos/hivemind. http://blog.opencomponentry.com - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]