Hi Nathan, > Anywhere on my site where I display a Donkey, I want to let the user edit the > instance. So then do exactly that... Tapestry 5 is sometimes simpler than you might expect, to me it seems you are complicating things unnecessarily... why are you creating new instances when editing? You shouldn't need to, simply pass in foo and call getDonkey and edit that instance. Your loop should just work, simply bind foo and the correct instance will always be bound, with a new instance of the inlinedit component for every iteration, so you have no need for extended context info in this case.
<t:loop source="fooList" value="fooLoopVar"> Here's a foo: ${fooLoopVar.name}<br/> <t:viewDonkeyInlineEdit foo="fooLoopVar"/><br/><br/> </t:loop> public class ViewDonkeyInlineEdit { @Parameter (allownull=false, required=true) @Property private Foo foo; //wired to your event onEdit etc. @Persist @Property private Boolean isEditing; public void onSaveEditDonkey() { foo.getDonkey().saveToDB(); } } Description: <t:textfield value="foo.donkey.descr" size="50"/> Its that simple. Peter ----- Original Message ----- From: "Nathan Kopp" <nathan.k...@gmail.com> To: users@tapestry.apache.org Sent: Sunday, 14 February, 2010 04:10:02 GMT +02:00 Athens, Beirut, Bucharest, Istanbul Subject: Activation context for a component? Hey everyone! I'm new here and I apologize for not lurking long before posting. I have searched the archives extensively for this issue and have come up empty, so I'm just going to jump in. I'm working in Tapestry 5.1.0.5 (the latest as far as I know) and I've run into a use-case that I can't figure out how to solve cleanly with T5. I actually used Tapestry extensively back in the 3.x and 4.x days, partially because it could solve this particular use case very well when other frameworks would fall flat. Unfortunately, Tapestry works differently now, so I think I need some help solving this in the new paradigm. Also, this is really long, because I wanted to include lots of code to give really good context to my question. Here goes: I have two objects, let's call them Foo and Donkey (shout-out to the Java Posse). Each Foo object contains a Donkey object, like this: public class Donkey { private String name; private String descr; // constructor & getters/setters go here } public class Foo { private String name; private Donkey donkey; // constructor & getters/setters go here } Now... the Donkey object represents user-contributed content. Anywhere on my site where I display a Donkey, I want to let the user edit the instance. To do this, I've created a reusable custom component that displays the Donkey, including inline editing capabilities. It's rudimentary now, but eventually it'll probably use an Ajax modal popup from ChenilleKit. ==================ViewDonkeyInlineEdit.tml=============== <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> Donkey's name: ${donkey.name}<br/> <t:if test="editing"> <t:form> <t:errors/> Description: <t:textfield value="editDonkey.descr" size="50"/> <t:submit value="Save" event="saveEditDonkey"/> <t:eventlink event="cancelEditDonkey">Cancel</t:eventlink><br/> </t:form> <p:else> Description: ${donkey.descr} <t:eventlink event="startEditDonkey">Edit</t:eventlink><br/> </p:else> </t:if> </t:container> ================ViewDonkeyInlineEdit.java===================== public class ViewDonkeyInlineEdit { @Property @Parameter private Donkey donkey; @Persist @Property private Donkey editDonkey; public boolean isEditing() { return (editDonkey!=null && editDonkey.getName().equals(donkey.getName())); } public void onStartEditDonkey() { editDonkey = new Donkey(donkey.getName(), donkey.getDescr()); } public void onSaveEditDonkey() { donkey.setDescr(editDonkey.getDescr()); editDonkey = null; } public void onCancelEditDonkey() { editDonkey = null; } } ============== Now, let's use it in a page: ======== Test.tml ============== <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> Here's a foo: ${firstFoo.name}<br/> <t:viewDonkeyInlineEdit donkey="firstFoo.donkey"/><br/><br/> Here's a foo: ${secondFoo.name}<br/> <t:viewDonkeyInlineEdit donkey="secondFoo.donkey"/><br/><br/> Here's a foo: ${thirdFoo.name}<br/> <t:viewDonkeyInlineEdit donkey="thirdFoo.donkey"/><br/> </html> ================= This works great. No problems. Clearly, though, this is *screaming* to be put into a loop. So, let's try this: ======= Test.tml (version 2) ========= <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter"> <t:loop source="fooList" value="fooLoopVar"> Here's a foo: ${fooLoopVar.name}<br/> <t:viewDonkeyInlineEdit donkey="fooLoopVar.donkey"/><br/><br/> </t:loop> </html> ============== Of course, this doesn't work. (If it did work, I wouldn't be writing this message right now.) When I click the "edit" link to triggger the "startDonkeyEdit" event, I get a null pointer exception. There are two things preventing this from working: First, Tapestry does not replay the loop when processing the eventlink (like it did in the 3.x and 4.x days). Therefore, "fooLoopVar" never gets changed from its default of null. Second, there's only one instance of the ViewDonkeyInlineEdit component inside the loop. All of the eventlink URLs look identical, so there's no way to tell which Donkey should be edited. I tried using the "context" parameter of the eventlink component, but since events start at the deepest level and bubble up, I got the null pointer exception before I could use the context for anything useful at the outer (page) level. Next, I tried adding an activation context to the page. In Test.java, I added: String onPassivate() { return fooLoopVar==null?null:fooLoopVar.getName(); } public void onActivate(String fooName) { for(Foo foo : list) { if(foo.getName().equals(fooName)) { this.fooLoopVar = foo; } } } This worked really well. It appears that the page's onPassivate() method is called for each generation of eventlink, yielding a different URL each time. I thought I had found my solution! Of course, if that were true I wouldn't be writing this email. Because I'll be using this inline-editing component throughout the site, loops will often occur embedded inside a few layers of components instead of at the page level. If you convert Test.java to a component and put it into a page, then the activation context won't work any more. Activation contexts are only for pages. Now I'm back to square one. :-( Basically, I need something that works like the page activation context, but generalized to work with any component at any level of the component tree. Maybe it could be something that bubbles all the info up to the page to be put into a dynamic activation context, and then a mechanism to distribute that information back to the components upon activation. Any ideas? Also note two other requirements: 1) The solution needs to handle multiple nested loops in multiple levers of nested components. 2) The solution can't clutter the main code with lots of boilerplate. I'll be using it all over the place. Thanks much for any ideas that anyone can offer! -Nathan --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org