Hi All,

I admire Travis' ambition, but he's forgetting an ancient programming maxim:
be lazy! By that I mean there no need to implement your own Zone component
(or sub-component). I actually got this to work, but I'm not %100 satisfied.

So the id attribute of zone doesn't allow property expansion; don't despair,
use the default. The Loop component does 'unique-ify' the ids in a
predictable manner, so let's take advantage of that! All we need is a method
to compute what the zone id is expected to be for that iteration. This would
be possible with render variables, if it weren't the fact that the second
iteration starts at zero!

.tml snippet:
    <t:loop t:source="..." t:index="currentIndex" t:value="currentValue">
        <t:zone t:id="myZone">...${currentValue}...</t:zone>
        <t:actionlink t:id="myAction" t:zone="prop:currentZoneId"
t:context="currentValue">...</t:actionlink>
    </t:loop>

.java snippet:
    @Component
    private Zone _myZone;

    private int _currentIndex; // Getters and Setters...
    private Object _currentValue; // Getters and Setters...

    public String getCurrentZoneId() {
        if(_currentIndex == 0)
            return "myZone";
        return "myZone_" + (_currentIndex - 1);
    }

    Object onAction(_currentValue) {
        setCurrentValue(_currentValue);
        return _myZone;
    }

Note that it is important to pass the currentValue through the context
because I have used it's value inside the zone. Any property expansion
inside the zone needs to passed through the context otherwise Tapestry (or
more specifically the Zone component) doesn't know what iteration it is.

Limitations:

 (*) We are stuck with Loop's unique-ifation.
 (*) We must use the generic onAction event handler (unless the OnEvent
annotation does property expansion in the component attribute(?))
 (*) Don't go crazy with the currentValue, stick to the primitives. Having
said that you can always pass the currentIndex through the context and use
that to set the currentValue.

There is a work-around these limitations. If with define an upper limit to
the size of the Loop's source list (not an unreasonable thing to do) then we
could ditch the context and be verbose to the event handler methods like so:

    Object onActionFromMyAction() {
        _currentIndex = 0;
        _currentValue = ...;
        return _myZone;
    }

    Object onActionFromMyAction_0() {
        _currentIndex = 1;
        _currentValue = ...;
        return _myZone;
    }

    Object onActionFromMyAction_1() {
        _currentIndex = 2;
        _currentValue = ...;
        return _myZone;
    }

etc.

In fact, it would be a fairly simple task to do this with java
instrumentation. It would be even better to have a event type attribute on
ActionLink, so that we can specify something other than action, like:

    Object onMyEventFromMyLink(currentIndex) {
        ...
        return _myZone;
    }

Dear Commiters, please add an event attribute to ActionLink.

Cheers,
Corin.


Travis McLeskey wrote:
> 
> (I wasn't subscribed to the list, so I'm sorry I'm not quoting the  
> rest of the thread here.)
> 
> I ran into the same problem as Adriaan: it wouldn't let me use a  
> property expansion for the zone's id attribute. The only way around  
> this that I found was to create my own MyZone component (based on  
> Tapestry's Zone.java) and add a "customId" attribute. Then, in  
> MyZone.beginRender(), I replaced this:
> 
>          _clientId =  
> _pageRenderSupport.allocateClientId(_resources.getId());
> 
> with something like:
> 
>          if( _resources.isBound("customId") )
>              _clientId = _customId;
>          else
>              _clientId =  
> _pageRenderSupport.allocateClientId(_resources.getId());
> 
> Then, I made my loop look more like this:
> 
> <t:loop source="items" value="item">
>   <t:actionlink zone="myzone:${item.id}">go!</t:actionlink>
>   <t:myzone customid="myzone:${item.id}">in the zone?</t:zone>
>   <br />
> </t:loop>
> 
> Which worked quite nicely, and it let me make a few other tweaks to  
> how the Zone was rendered, like making it a  instead of a <div>.
> 
> 
> 
> However, that was only the first Zone-related hurdle. The next was  
> that I couldn't find any examples in the documentation of how to  
> actually provide the new content for the zone when the user clicks the  
> link. After a lot of time digging through the code (and learning  
> javascript!), I found the (or at least *a*) way to do it. I added this  
> method to my class:
> 
>      public Object onActionFromUpdatezone(final long id) {
>          JSONObject result = new JSONObject();
>          result.put("content", "The new content for the Zone's <div>.  
> Fresh from the server!");
>          return result;
>      }
> 
> (Note: I gave the ActionLink an id: "updatezone")
> 
> 
> 
> The next problem was that Zones in Tapestry currently can't do much  
> other than query the server for new content, put that content in the  
> <div>, and then call your "show" or "update" methods, if you specified  
> them. You can't have it do something other than hit the server when  
> the link is clicked, and you can't process the content before putting  
> it in the <div>. Well, at least you can't do these things without the  
> magic of javascript. My eventual solution is probably going to break  
> in some future release of Tapestry, and it may provoke some frowns,  
> but I circumvented all of the Zone-specific javascript code in  
> Tapestry be redefining Tapestry.initializeZones. The javascript below  
> is for an ActionLink that works as an expand/collapse button for the  
> Zone. The first time you expand the zone, it downloads the content  
> from the server and stores it in memory. After that, it doesn't need  
> to hit the server again. Note that this code doesn't support the inner  
> "t-zone-update" <div> that Tapestry's built-in javascript supports.
> 
> 
> MyObj = {
>    linkZone: function (link, zone) {
>      zone = $(zone);
>      link = $(link);
>      var expanded = false;
>      var origHTML = zone.innerHTML;
>      var fullHTML;
> 
>      link.onclick = function(event) {
>        if( expanded ) {
>          zone.innerHTML = origHTML;
>          link.innerHTML = "expand";
>          expanded = false;
>        } else {
>          if( !zone.everPopulated ) {
>            var successHandler = function(transport) {
>              var response = transport.responseText;
>              fullHTML = eval("(" + response + ")").content;
>              zone.innerHTML = fullHTML;
>            };
>            var request = new Ajax.Request(link.href, { onSuccess :  
> successHandler });
>            zone.everPopulated = true;
>          } else {
>            zone.innerHTML = fullHTML;
>          }
>          link.innerHTML = "collapse"
>          expanded = true;
>        }
>        return false;
>      };
>    }
> };
> 
> Tapestry.initializeZones = function(zoneSpecs, linkSpecs) {
>    $A(linkSpecs).each(function (spec)
>    {
>        MyObj.linkZone(spec[0],spec[1]);
>    });
> };
> 
> 
> 
> Hope that helps!
> Travis
> 
> 
> 
> 
> 
> On Feb 8, 2008, at 11:40 PM, Travis McLeskey wrote:
> 
>> When an ActionLink and Zone appear together in a loop like this:
>>
>> <t:loop source="items" value="item">
>>  <t:actionlink zone="myzone">go!</t:actionlink>
>>  <t:zone t:id="myzone">in the zone?</t:zone>
>>  <br />
>> </t:loop>
>>
>> Clicking the "go!" link from any iteration only affects the Zone  
>> from the first iteration. How do I connect each ActionLink to its  
>> corresponding Zone? I tried injecting the Zone into the java class  
>> and then using zone="${thezone.id}" in the actionlink, but then each  
>> ActionLink was connected to the Zone from the *previous* iteration.
>>
>> Thanks!
>> Travis
>>
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [EMAIL PROTECTED]
> For additional commands, e-mail: [EMAIL PROTECTED]
> 
> 
> 

-- 
View this message in context: 
http://www.nabble.com/T5%3A-ActionLink-Zone-components-inside-a-loop-tp15374193p15459968.html
Sent from the Tapestry - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to