Refreshing zone in layout triggered by an eventlink in a nested component.

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Refreshing zone in layout triggered by an eventlink in a nested component.

balapal
Hello,

Refreshing totalPointsZone from the FullWidthLayout is not working when
participateInSweepstake  async eventlink is clicked
in sweepstake.ShowSweepstake nested component.

Any suggestions how to get both the participateInSweepstakeZone and the
totalPointsZone refreshed when <t:EventLink event="participateInSweepstake"
context="${sweepstake.id}" async="true">Participate is clicked</t:EventLink>

*Here is the relevant code snippets from the app:*

*Page: ShowSweepstakes.tml file:*
<html t:type="layout" xmlns:t="
http://tapestry.apache.org/schema/tapestry_5_4.xsd">
    <t:sweepstake.ShowSweepstakes  />
</html>

*sweepstake.ShowSweepstakes.tml component:*
<t:container>
    <loop>
         <t:sweepstake.ShowSweepstake sweepstake="sweepstake"  />
   </loop>
</t:container>

*sweepstake.ShowSweepstake.tml component:*
   <t:zone t:id="participateInSweepstakeZone"
id="prop:participateInSweepstakeZoneId">
            <t:if test="notParticipatedYet">
                <t:EventLink event="participateInSweepstake" context="${
sweepstake.id}" async="true" class="btn btn-danger btn-lg">
                    Participate for
${sweepstake.priceToParticipateInPoints} points
                </t:EventLink>
            <p:else>
                <button type="button" class="btn btn-danger btn-lg"
disabled="disabled">You're already participating</button>
            </p:else>
            </t:if>
        </t:zone>

*ShowSweepstake .java:*
public class ShowSweepstake {
    @CommitAfter
    void onParticipateInSweepstake(final Sweepstake sweepstake) {
         ....

componentResources.triggerEvent(ComponentEvents.REFRESH_TOTAL_POINTS, null,
null);
        if (request.isXHR()) {
            ajaxResponseRenderer.addRender(participateInSweepstakeZone);
        }
    }

*Layout.tml component*
<html t:type="FullWidthLayout">
    <div id="layout-container" class="container">
        <div class="row">
            <t:body />
        </div>
    </div>
</html>

*FullWidthLayout.tml component:*
<html><body>
    *<span t:type="zone" t:id="totalPointsZone">${user.totalPoints}</span>*
    <t:body />
</body> </html>

*FullWidthLayout .java component*
public class FullWidthLayout {
    *@OnEvent(ComponentEvents.REFRESH_TOTAL_POINTS)*
    private void refreshTotalPoints() {
        ajaxResponseRenderer.addRender(totalPointsZone);
    }
}

Thanks,
Balazs
Reply | Threaded
Open this post in threaded view
|

Re: Refreshing zone in layout triggered by an eventlink in a nested component.

Chris Poulsen
Hi

I haven't looked much into this, but my guess is that your layout is using
t:body and that confuses things?

As far as I understand t.body makes a component able to wrap its template
around something in its containing component. - That probably means that
your stuff wrapped in the layout isn't a child component of the layout, so
your event does not bubble to the layout.

You can inspect the tapestry component model of the page at runtime (using
a debugger) to see how the actual components are connected.

If it turns out that your layout does not participate the expected place in
your component hierarchy, you could expose various aspects of the layout
using parameters, for example allowing access to the zone or pick some
other construct that matches your needs.

--
Chris



On Thu, Feb 14, 2019 at 10:24 PM Balázs Palcsó <[hidden email]>
wrote:

> Hello,
>
> Refreshing totalPointsZone from the FullWidthLayout is not working when
> participateInSweepstake  async eventlink is clicked
> in sweepstake.ShowSweepstake nested component.
>
> Any suggestions how to get both the participateInSweepstakeZone and the
> totalPointsZone refreshed when <t:EventLink event="participateInSweepstake"
> context="${sweepstake.id}" async="true">Participate is
> clicked</t:EventLink>
>
> *Here is the relevant code snippets from the app:*
>
> *Page: ShowSweepstakes.tml file:*
> <html t:type="layout" xmlns:t="
> http://tapestry.apache.org/schema/tapestry_5_4.xsd">
>     <t:sweepstake.ShowSweepstakes  />
> </html>
>
> *sweepstake.ShowSweepstakes.tml component:*
> <t:container>
>     <loop>
>          <t:sweepstake.ShowSweepstake sweepstake="sweepstake"  />
>    </loop>
> </t:container>
>
> *sweepstake.ShowSweepstake.tml component:*
>    <t:zone t:id="participateInSweepstakeZone"
> id="prop:participateInSweepstakeZoneId">
>             <t:if test="notParticipatedYet">
>                 <t:EventLink event="participateInSweepstake" context="${
> sweepstake.id}" async="true" class="btn btn-danger btn-lg">
>                     Participate for
> ${sweepstake.priceToParticipateInPoints} points
>                 </t:EventLink>
>             <p:else>
>                 <button type="button" class="btn btn-danger btn-lg"
> disabled="disabled">You're already participating</button>
>             </p:else>
>             </t:if>
>         </t:zone>
>
> *ShowSweepstake .java:*
> public class ShowSweepstake {
>     @CommitAfter
>     void onParticipateInSweepstake(final Sweepstake sweepstake) {
>          ....
>
> componentResources.triggerEvent(ComponentEvents.REFRESH_TOTAL_POINTS, null,
> null);
>         if (request.isXHR()) {
>             ajaxResponseRenderer.addRender(participateInSweepstakeZone);
>         }
>     }
>
> *Layout.tml component*
> <html t:type="FullWidthLayout">
>     <div id="layout-container" class="container">
>         <div class="row">
>             <t:body />
>         </div>
>     </div>
> </html>
>
> *FullWidthLayout.tml component:*
> <html><body>
>     *<span t:type="zone" t:id="totalPointsZone">${user.totalPoints}</span>*
>     <t:body />
> </body> </html>
>
> *FullWidthLayout .java component*
> public class FullWidthLayout {
>     *@OnEvent(ComponentEvents.REFRESH_TOTAL_POINTS)*
>     private void refreshTotalPoints() {
>         ajaxResponseRenderer.addRender(totalPointsZone);
>     }
> }
>
> Thanks,
> Balazs
>
Reply | Threaded
Open this post in threaded view
|

Re: Refreshing zone in layout triggered by an eventlink in a nested component.

balapal
Hi Chris,

Yes, that seems to be correct that the layout components with <t:body />
(FullWidthLayout and Layout)  are not represented in the component
container tree (ComponentPageElement.getContainerElement();) where the
event bubbling up happening.

Is there any tapestry design pattern to come over this?

Most of my pages have the same layout (header, menu, footer, etc) that's
why I use these Layout components with the <t:body />, but the
FullWidthLayout has a zone that would need to be refreshed too for a number
of events.

Thanks,
Balázs



On Fri, 15 Feb 2019 at 09:49, Chris Poulsen <[hidden email]> wrote:

> Hi
>
> I haven't looked much into this, but my guess is that your layout is using
> t:body and that confuses things?
>
> As far as I understand t.body makes a component able to wrap its template
> around something in its containing component. - That probably means that
> your stuff wrapped in the layout isn't a child component of the layout, so
> your event does not bubble to the layout.
>
> You can inspect the tapestry component model of the page at runtime (using
> a debugger) to see how the actual components are connected.
>
> If it turns out that your layout does not participate the expected place in
> your component hierarchy, you could expose various aspects of the layout
> using parameters, for example allowing access to the zone or pick some
> other construct that matches your needs.
>
> --
> Chris
>
>
>
> On Thu, Feb 14, 2019 at 10:24 PM Balázs Palcsó <[hidden email]>
> wrote:
>
> > Hello,
> >
> > Refreshing totalPointsZone from the FullWidthLayout is not working when
> > participateInSweepstake  async eventlink is clicked
> > in sweepstake.ShowSweepstake nested component.
> >
> > Any suggestions how to get both the participateInSweepstakeZone and the
> > totalPointsZone refreshed when <t:EventLink
> event="participateInSweepstake"
> > context="${sweepstake.id}" async="true">Participate is
> > clicked</t:EventLink>
> >
> > *Here is the relevant code snippets from the app:*
> >
> > *Page: ShowSweepstakes.tml file:*
> > <html t:type="layout" xmlns:t="
> > http://tapestry.apache.org/schema/tapestry_5_4.xsd">
> >     <t:sweepstake.ShowSweepstakes  />
> > </html>
> >
> > *sweepstake.ShowSweepstakes.tml component:*
> > <t:container>
> >     <loop>
> >          <t:sweepstake.ShowSweepstake sweepstake="sweepstake"  />
> >    </loop>
> > </t:container>
> >
> > *sweepstake.ShowSweepstake.tml component:*
> >    <t:zone t:id="participateInSweepstakeZone"
> > id="prop:participateInSweepstakeZoneId">
> >             <t:if test="notParticipatedYet">
> >                 <t:EventLink event="participateInSweepstake" context="${
> > sweepstake.id}" async="true" class="btn btn-danger btn-lg">
> >                     Participate for
> > ${sweepstake.priceToParticipateInPoints} points
> >                 </t:EventLink>
> >             <p:else>
> >                 <button type="button" class="btn btn-danger btn-lg"
> > disabled="disabled">You're already participating</button>
> >             </p:else>
> >             </t:if>
> >         </t:zone>
> >
> > *ShowSweepstake .java:*
> > public class ShowSweepstake {
> >     @CommitAfter
> >     void onParticipateInSweepstake(final Sweepstake sweepstake) {
> >          ....
> >
> > componentResources.triggerEvent(ComponentEvents.REFRESH_TOTAL_POINTS,
> null,
> > null);
> >         if (request.isXHR()) {
> >             ajaxResponseRenderer.addRender(participateInSweepstakeZone);
> >         }
> >     }
> >
> > *Layout.tml component*
> > <html t:type="FullWidthLayout">
> >     <div id="layout-container" class="container">
> >         <div class="row">
> >             <t:body />
> >         </div>
> >     </div>
> > </html>
> >
> > *FullWidthLayout.tml component:*
> > <html><body>
> >     *<span t:type="zone"
> t:id="totalPointsZone">${user.totalPoints}</span>*
> >     <t:body />
> > </body> </html>
> >
> > *FullWidthLayout .java component*
> > public class FullWidthLayout {
> >     *@OnEvent(ComponentEvents.REFRESH_TOTAL_POINTS)*
> >     private void refreshTotalPoints() {
> >         ajaxResponseRenderer.addRender(totalPointsZone);
> >     }
> > }
> >
> > Thanks,
> > Balazs
> >
>
Reply | Threaded
Open this post in threaded view
|

Re: Refreshing zone in layout triggered by an eventlink in a nested component.

Chris Poulsen
Hmm, generally speaking it is the responsibility of the container (page) to
coordinate its children.

At work we have a couple of places where certain areas of the layout needs
to be interacted with from the pages. We have chosen to handle the matter
with block parameters in the layout. Pages that need to have "stuff" in the
layout use the parameters to add their extra things in the available areas.

In your case you could add a (page) block that contains the zone with the
stuff for the layout, when the page receives the event, the zone is updated.

You could probably also obtain the component resources of the layout
component and manually trigger an event from the page, but that seems less
nice to me.

--
Chris



On Fri, Feb 15, 2019 at 2:53 PM Balázs Palcsó <[hidden email]>
wrote:

> Hi Chris,
>
> Yes, that seems to be correct that the layout components with <t:body />
> (FullWidthLayout and Layout)  are not represented in the component
> container tree (ComponentPageElement.getContainerElement();) where the
> event bubbling up happening.
>
> Is there any tapestry design pattern to come over this?
>
> Most of my pages have the same layout (header, menu, footer, etc) that's
> why I use these Layout components with the <t:body />, but the
> FullWidthLayout has a zone that would need to be refreshed too for a number
> of events.
>
> Thanks,
> Balázs
>
>
>
> On Fri, 15 Feb 2019 at 09:49, Chris Poulsen <[hidden email]>
> wrote:
>
> > Hi
> >
> > I haven't looked much into this, but my guess is that your layout is
> using
> > t:body and that confuses things?
> >
> > As far as I understand t.body makes a component able to wrap its template
> > around something in its containing component. - That probably means that
> > your stuff wrapped in the layout isn't a child component of the layout,
> so
> > your event does not bubble to the layout.
> >
> > You can inspect the tapestry component model of the page at runtime
> (using
> > a debugger) to see how the actual components are connected.
> >
> > If it turns out that your layout does not participate the expected place
> in
> > your component hierarchy, you could expose various aspects of the layout
> > using parameters, for example allowing access to the zone or pick some
> > other construct that matches your needs.
> >
> > --
> > Chris
> >
> >
> >
> > On Thu, Feb 14, 2019 at 10:24 PM Balázs Palcsó <[hidden email]>
> > wrote:
> >
> > > Hello,
> > >
> > > Refreshing totalPointsZone from the FullWidthLayout is not working when
> > > participateInSweepstake  async eventlink is clicked
> > > in sweepstake.ShowSweepstake nested component.
> > >
> > > Any suggestions how to get both the participateInSweepstakeZone and the
> > > totalPointsZone refreshed when <t:EventLink
> > event="participateInSweepstake"
> > > context="${sweepstake.id}" async="true">Participate is
> > > clicked</t:EventLink>
> > >
> > > *Here is the relevant code snippets from the app:*
> > >
> > > *Page: ShowSweepstakes.tml file:*
> > > <html t:type="layout" xmlns:t="
> > > http://tapestry.apache.org/schema/tapestry_5_4.xsd">
> > >     <t:sweepstake.ShowSweepstakes  />
> > > </html>
> > >
> > > *sweepstake.ShowSweepstakes.tml component:*
> > > <t:container>
> > >     <loop>
> > >          <t:sweepstake.ShowSweepstake sweepstake="sweepstake"  />
> > >    </loop>
> > > </t:container>
> > >
> > > *sweepstake.ShowSweepstake.tml component:*
> > >    <t:zone t:id="participateInSweepstakeZone"
> > > id="prop:participateInSweepstakeZoneId">
> > >             <t:if test="notParticipatedYet">
> > >                 <t:EventLink event="participateInSweepstake"
> context="${
> > > sweepstake.id}" async="true" class="btn btn-danger btn-lg">
> > >                     Participate for
> > > ${sweepstake.priceToParticipateInPoints} points
> > >                 </t:EventLink>
> > >             <p:else>
> > >                 <button type="button" class="btn btn-danger btn-lg"
> > > disabled="disabled">You're already participating</button>
> > >             </p:else>
> > >             </t:if>
> > >         </t:zone>
> > >
> > > *ShowSweepstake .java:*
> > > public class ShowSweepstake {
> > >     @CommitAfter
> > >     void onParticipateInSweepstake(final Sweepstake sweepstake) {
> > >          ....
> > >
> > > componentResources.triggerEvent(ComponentEvents.REFRESH_TOTAL_POINTS,
> > null,
> > > null);
> > >         if (request.isXHR()) {
> > >
>  ajaxResponseRenderer.addRender(participateInSweepstakeZone);
> > >         }
> > >     }
> > >
> > > *Layout.tml component*
> > > <html t:type="FullWidthLayout">
> > >     <div id="layout-container" class="container">
> > >         <div class="row">
> > >             <t:body />
> > >         </div>
> > >     </div>
> > > </html>
> > >
> > > *FullWidthLayout.tml component:*
> > > <html><body>
> > >     *<span t:type="zone"
> > t:id="totalPointsZone">${user.totalPoints}</span>*
> > >     <t:body />
> > > </body> </html>
> > >
> > > *FullWidthLayout .java component*
> > > public class FullWidthLayout {
> > >     *@OnEvent(ComponentEvents.REFRESH_TOTAL_POINTS)*
> > >     private void refreshTotalPoints() {
> > >         ajaxResponseRenderer.addRender(totalPointsZone);
> > >     }
> > > }
> > >
> > > Thanks,
> > > Balazs
> > >
> >
>
Reply | Threaded
Open this post in threaded view
|

Re: Refreshing zone in layout triggered by an eventlink in a nested component.

balapal
The idea of passing blocks to layout sounds good to customize content in
different areas.
For event handling I still have to either
1) introduce a super class for all page classes to handle the event and
trigger the refresh
2) or add the event handler on almost all pages I have.

So inheritance seems to be a more pragmatic choice to me.
I have also come across template inheritance feature of Tapestry:
http://tapestry.apache.org/component-templates.html
Creating the layout as the super class of pages and use both java and
template inheritance looks like the most pragmatic choice (no need to pass
around blocks, but I can replace the parts in the parent template)

This means the following changes to the code I posted originally:

*Page: ShowSweepstakes.tml file:*
<t:extend xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
xmlns:p="tapestry:parameter">
<t:replace id="LayoutBody">
    <t:sweepstake.ShowSweepstakes  />
</t:replace>
</t:extend>

*Layout.tml component*
<t:extend xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"
xmlns:p="tapestry:parameter">
<t:replace id="FullWidthLayoutBody">
    <div id="layout-container" class="container">
        <div class="row">
            <t:extension-point id="LayoutBody" />
        </div>
    </div>
</t:replace>
</t:extend>

*FullWidthLayout.tml component:*
<html><body>
    <span t:type="zone" t:id="totalPointsZone">${user.totalPoints}</span>
    <t:extension-point id="FullWidthLayoutBody" />
</body> </html>

On the java side the page classes has to also extend the Layout class.


On Sat, 16 Feb 2019 at 13:11, Chris Poulsen <[hidden email]> wrote:

> Hmm, generally speaking it is the responsibility of the container (page) to
> coordinate its children.
>
> At work we have a couple of places where certain areas of the layout needs
> to be interacted with from the pages. We have chosen to handle the matter
> with block parameters in the layout. Pages that need to have "stuff" in the
> layout use the parameters to add their extra things in the available areas.
>
> In your case you could add a (page) block that contains the zone with the
> stuff for the layout, when the page receives the event, the zone is
> updated.
>
> You could probably also obtain the component resources of the layout
> component and manually trigger an event from the page, but that seems less
> nice to me.
>
> --
> Chris
>
>
>
> On Fri, Feb 15, 2019 at 2:53 PM Balázs Palcsó <[hidden email]>
> wrote:
>
> > Hi Chris,
> >
> > Yes, that seems to be correct that the layout components with <t:body />
> > (FullWidthLayout and Layout)  are not represented in the component
> > container tree (ComponentPageElement.getContainerElement();) where the
> > event bubbling up happening.
> >
> > Is there any tapestry design pattern to come over this?
> >
> > Most of my pages have the same layout (header, menu, footer, etc) that's
> > why I use these Layout components with the <t:body />, but the
> > FullWidthLayout has a zone that would need to be refreshed too for a
> number
> > of events.
> >
> > Thanks,
> > Balázs
> >
> >
> >
> > On Fri, 15 Feb 2019 at 09:49, Chris Poulsen <[hidden email]>
> > wrote:
> >
> > > Hi
> > >
> > > I haven't looked much into this, but my guess is that your layout is
> > using
> > > t:body and that confuses things?
> > >
> > > As far as I understand t.body makes a component able to wrap its
> template
> > > around something in its containing component. - That probably means
> that
> > > your stuff wrapped in the layout isn't a child component of the layout,
> > so
> > > your event does not bubble to the layout.
> > >
> > > You can inspect the tapestry component model of the page at runtime
> > (using
> > > a debugger) to see how the actual components are connected.
> > >
> > > If it turns out that your layout does not participate the expected
> place
> > in
> > > your component hierarchy, you could expose various aspects of the
> layout
> > > using parameters, for example allowing access to the zone or pick some
> > > other construct that matches your needs.
> > >
> > > --
> > > Chris
> > >
> > >
> > >
> > > On Thu, Feb 14, 2019 at 10:24 PM Balázs Palcsó <
> [hidden email]>
> > > wrote:
> > >
> > > > Hello,
> > > >
> > > > Refreshing totalPointsZone from the FullWidthLayout is not working
> when
> > > > participateInSweepstake  async eventlink is clicked
> > > > in sweepstake.ShowSweepstake nested component.
> > > >
> > > > Any suggestions how to get both the participateInSweepstakeZone and
> the
> > > > totalPointsZone refreshed when <t:EventLink
> > > event="participateInSweepstake"
> > > > context="${sweepstake.id}" async="true">Participate is
> > > > clicked</t:EventLink>
> > > >
> > > > *Here is the relevant code snippets from the app:*
> > > >
> > > > *Page: ShowSweepstakes.tml file:*
> > > > <html t:type="layout" xmlns:t="
> > > > http://tapestry.apache.org/schema/tapestry_5_4.xsd">
> > > >     <t:sweepstake.ShowSweepstakes  />
> > > > </html>
> > > >
> > > > *sweepstake.ShowSweepstakes.tml component:*
> > > > <t:container>
> > > >     <loop>
> > > >          <t:sweepstake.ShowSweepstake sweepstake="sweepstake"  />
> > > >    </loop>
> > > > </t:container>
> > > >
> > > > *sweepstake.ShowSweepstake.tml component:*
> > > >    <t:zone t:id="participateInSweepstakeZone"
> > > > id="prop:participateInSweepstakeZoneId">
> > > >             <t:if test="notParticipatedYet">
> > > >                 <t:EventLink event="participateInSweepstake"
> > context="${
> > > > sweepstake.id}" async="true" class="btn btn-danger btn-lg">
> > > >                     Participate for
> > > > ${sweepstake.priceToParticipateInPoints} points
> > > >                 </t:EventLink>
> > > >             <p:else>
> > > >                 <button type="button" class="btn btn-danger btn-lg"
> > > > disabled="disabled">You're already participating</button>
> > > >             </p:else>
> > > >             </t:if>
> > > >         </t:zone>
> > > >
> > > > *ShowSweepstake .java:*
> > > > public class ShowSweepstake {
> > > >     @CommitAfter
> > > >     void onParticipateInSweepstake(final Sweepstake sweepstake) {
> > > >          ....
> > > >
> > > > componentResources.triggerEvent(ComponentEvents.REFRESH_TOTAL_POINTS,
> > > null,
> > > > null);
> > > >         if (request.isXHR()) {
> > > >
> >  ajaxResponseRenderer.addRender(participateInSweepstakeZone);
> > > >         }
> > > >     }
> > > >
> > > > *Layout.tml component*
> > > > <html t:type="FullWidthLayout">
> > > >     <div id="layout-container" class="container">
> > > >         <div class="row">
> > > >             <t:body />
> > > >         </div>
> > > >     </div>
> > > > </html>
> > > >
> > > > *FullWidthLayout.tml component:*
> > > > <html><body>
> > > >     *<span t:type="zone"
> > > t:id="totalPointsZone">${user.totalPoints}</span>*
> > > >     <t:body />
> > > > </body> </html>
> > > >
> > > > *FullWidthLayout .java component*
> > > > public class FullWidthLayout {
> > > >     *@OnEvent(ComponentEvents.REFRESH_TOTAL_POINTS)*
> > > >     private void refreshTotalPoints() {
> > > >         ajaxResponseRenderer.addRender(totalPointsZone);
> > > >     }
> > > > }
> > > >
> > > > Thanks,
> > > > Balazs
> > > >
> > >
> >
>