You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Ákos Maróy <da...@tyrell.hu> on 2006/12/02 09:08:56 UTC

target-page based listener semantics

Hi,

I have a question about listener semantics in Tapestry, about how one
could achieve a simple usage semantics, when the target of a link or a
form is a different page / component, than the invoker.

The current typical usage for listeners in Tapestry is that the listener
would be in the same page / component, as source of invocation (the
form, link, etc.) This pattern in encouraged by the listener: binding as
well.

What I would like to have is a pattern where one could simply say that
'I will submit this to page Foo', instead of submitting to 'myself'. For
example, currently a typical Tapestry invocation is something like:

<a jwcid="@DirectLink" listener="listener:fooListener"
parameters="blahblah">link back to myself</a>

whereas if I'm at page Bar, but wold would still like to submit to
fooListener at page Foo, it would be nice to have something like:

<a jwcid="@DirectLink" target="Foo" listener="listener:fooListener"
parameters="blahblah">link to page Foo</a>

of course, one can achieve the above with some magic, specifically:

<a jwcid="@DirectLink"
listener="ognl:requestCycle.getPage('Foo').listeners.fooListener"
parameters="blahblah">link to page Foo</a>

but well, this is obviously a workaround :)

similarly, it would be nice if the target page would define the scope of
form parameter as well. let's take the example:

<form jwcid="@Form" success="listener:fooListener">
<input jwcid="myParam@TextField" value="ognl:myParam"/>
<input jwcid="@Submit"/>
</form>

submits back to the same page. now one could have:

<form jwcid="@Form" target="Foo" success="listener:fooListener">
<input jwcid="myParam@TextField" value="ognl:myParam"/>
<input jwcid="@Submit"/>
</form>

to submit to Foo.fooListener() directly. currently, using the above
workaround it's quite awkward, as for each form parameter one has to
specify the target page as well:

<form jwcid="@Form"
success="ognl:requestCycle.getPage('Foo').listeners.fooListener">
<input jwcid="myParam@TextField"
value="ognl:ognl:requestCycle.getPage('Foo').myParam"/>
<input jwcid="@Submit"/>
</form>


Of course, submitting back to oneself makes sense in a lot of cases, and
has the effect that both the source of invokation and all the processing
of the data is contained in the same page or component.

But, it is a bit contrary to the original concept of hyperlinking, where
one could actually link to an _other_ page, not onto himself. Let's
assume the following scenario:

- I have a page Foo, that displays something based on an input parameter.
- I have a huge amount of pages a that link to Foo, calling it with all
sorts of parameters. Let these be Bar001, Bar002, etc.

Traditionally this would be solved like the following:

- Foo.html would expect parameters either through HTTP GET or POST
- Bar00X.html could either link to it:

<a href="Foo.html?myParam=blahblah">link to Foo</a>

or have a form submit to it:

<form target="Foo.html">
<input type="text" name="myParam"/>
<input type="submit"/>
</from>


And this is all quite usual to do. Now, with Tapestry, all this is very
complicated using the encouraged pattern of having a listener on the
same page / component as the invocation source. Now the solution looks
like is:

- in the page Foo, a one would have a property name myParam
- in the page Bar00X, one would have:
  - a property named myParam
  - an injected property containing a reference to page Foo
  - a listener, doing most of the work:

something like this for _each_ Bar00X:

@InjectPage("FooPage")
public abstract IPage getFooPage();

public abstract Object getMyParam();

public IPage barListener(Object myParam) {
    FooPage fooPage = getFoo();
    fooPage.setMyParam(myParam);
    return fooPage;
}


and then the HTML code would look like:

<a jwcid="@DirectLink" listener="listener:barListener"
parameters="blahblah">link to Foo</a>



My problem with the above scenario is that one has to duplicate the
functionality of the listener for _every_ Bar00X page, including form
data validation, some amount of pre-processing. If we consider that
these might not share the same ancestor, that would enable re-use of
common functions, or that the caller might not even be a Tapestry page,
but say some foreign page, this solution is all too complex, encourages
code duplication, etc., when compared to the simple traditional solution
shown above.



Akos

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: target-page based listener semantics

Posted by Ákos Maróy <da...@tyrell.hu>.
Viktor Szathmary wrote:
> Actually, ExternalLink might be a good fit for your purposes:
> 
> http://tapestry.apache.org/tapestry4.1/components/ExternalLink.html

I see - looks interesting. Is there anything similar to use with forms?


Akos

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: target-page based listener semantics

Posted by Viktor Szathmary <ph...@gmail.com>.
Hi,

Actually, ExternalLink might be a good fit for your purposes:

http://tapestry.apache.org/tapestry4.1/components/ExternalLink.html

  Viktor


On 12/2/06, Ákos Maróy <da...@tyrell.hu> wrote:
> Hi,
>
> I have a question about listener semantics in Tapestry, about how one
> could achieve a simple usage semantics, when the target of a link or a
> form is a different page / component, than the invoker.
>
> The current typical usage for listeners in Tapestry is that the listener
> would be in the same page / component, as source of invocation (the
> form, link, etc.) This pattern in encouraged by the listener: binding as
> well.
>
> What I would like to have is a pattern where one could simply say that
> 'I will submit this to page Foo', instead of submitting to 'myself'. For
> example, currently a typical Tapestry invocation is something like:
>
> <a jwcid="@DirectLink" listener="listener:fooListener"
> parameters="blahblah">link back to myself</a>
>
> whereas if I'm at page Bar, but wold would still like to submit to
> fooListener at page Foo, it would be nice to have something like:
>
> <a jwcid="@DirectLink" target="Foo" listener="listener:fooListener"
> parameters="blahblah">link to page Foo</a>
>
> of course, one can achieve the above with some magic, specifically:
>
> <a jwcid="@DirectLink"
> listener="ognl:requestCycle.getPage('Foo').listeners.fooListener"
> parameters="blahblah">link to page Foo</a>
>
> but well, this is obviously a workaround :)
>
> similarly, it would be nice if the target page would define the scope of
> form parameter as well. let's take the example:
>
> <form jwcid="@Form" success="listener:fooListener">
> <input jwcid="myParam@TextField" value="ognl:myParam"/>
> <input jwcid="@Submit"/>
> </form>
>
> submits back to the same page. now one could have:
>
> <form jwcid="@Form" target="Foo" success="listener:fooListener">
> <input jwcid="myParam@TextField" value="ognl:myParam"/>
> <input jwcid="@Submit"/>
> </form>
>
> to submit to Foo.fooListener() directly. currently, using the above
> workaround it's quite awkward, as for each form parameter one has to
> specify the target page as well:
>
> <form jwcid="@Form"
> success="ognl:requestCycle.getPage('Foo').listeners.fooListener">
> <input jwcid="myParam@TextField"
> value="ognl:ognl:requestCycle.getPage('Foo').myParam"/>
> <input jwcid="@Submit"/>
> </form>
>
>
> Of course, submitting back to oneself makes sense in a lot of cases, and
> has the effect that both the source of invokation and all the processing
> of the data is contained in the same page or component.
>
> But, it is a bit contrary to the original concept of hyperlinking, where
> one could actually link to an _other_ page, not onto himself. Let's
> assume the following scenario:
>
> - I have a page Foo, that displays something based on an input parameter.
> - I have a huge amount of pages a that link to Foo, calling it with all
> sorts of parameters. Let these be Bar001, Bar002, etc.
>
> Traditionally this would be solved like the following:
>
> - Foo.html would expect parameters either through HTTP GET or POST
> - Bar00X.html could either link to it:
>
> <a href="Foo.html?myParam=blahblah">link to Foo</a>
>
> or have a form submit to it:
>
> <form target="Foo.html">
> <input type="text" name="myParam"/>
> <input type="submit"/>
> </from>
>
>
> And this is all quite usual to do. Now, with Tapestry, all this is very
> complicated using the encouraged pattern of having a listener on the
> same page / component as the invocation source. Now the solution looks
> like is:
>
> - in the page Foo, a one would have a property name myParam
> - in the page Bar00X, one would have:
>   - a property named myParam
>   - an injected property containing a reference to page Foo
>   - a listener, doing most of the work:
>
> something like this for _each_ Bar00X:
>
> @InjectPage("FooPage")
> public abstract IPage getFooPage();
>
> public abstract Object getMyParam();
>
> public IPage barListener(Object myParam) {
>     FooPage fooPage = getFoo();
>     fooPage.setMyParam(myParam);
>     return fooPage;
> }
>
>
> and then the HTML code would look like:
>
> <a jwcid="@DirectLink" listener="listener:barListener"
> parameters="blahblah">link to Foo</a>
>
>
>
> My problem with the above scenario is that one has to duplicate the
> functionality of the listener for _every_ Bar00X page, including form
> data validation, some amount of pre-processing. If we consider that
> these might not share the same ancestor, that would enable re-use of
> common functions, or that the caller might not even be a Tapestry page,
> but say some foreign page, this solution is all too complex, encourages
> code duplication, etc., when compared to the simple traditional solution
> shown above.
>
>
>
> Akos
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: target-page based listener semantics

Posted by Jonas Maurus <jo...@gmail.com>.
Hi Ákos,

Try to think of a listener not as the target of a link, but as the
"behavior behind" the link. Likewise a page's class is not a layer
between a template and a another view, think of it as part of the
template. The point I'm trying to make is that within Tapestry, the
typical semantics of a HTML link do not apply (well, they do, but only
"at and below" the level of a Tapestry-service).

How do you avoid duplication of behavior (like form validation) then?
You either develop a component that adds the desired behavior (by
combining existing components, if possible), or you create a
generalized abstraction and put it into the framework itself.
Validation, for example, can and should be handled by Tapestry's
validation framework as a "behavior" of the Form component.

Some additional observations:
1. Disclaimer: I could be totally wrong in how I understand this ;-)

2. the typical HTML semantics of a link are split up into the *Link
components. The only thing that's not available out-of-the-box is a
link-component that passes parameters to another page like you would
in a PHP-application using the query string. Using the POV described
above, passing the parameters is part of the behavior of the link.

putting it all together:
In my opinion, in typical Tapestry-fashion, you could easily develop a
component that uses a bit of introspection to pass a list of
parameters to a page and then activate a target page, thus abstracting
the behavior into a component (and getting the listener implementation
out of your page class).

I hope this helps...

best regards from Germany,
Jonas

PS: I find that the more we apply dependency injection and
component-based development, the less OOP-principles apply, because
there's not a real inheritance tree. Instead, behavior is "composed"
by assembling different components/services. The bad thing with
Tapestry 4.x is that it still depends a bit too much on using base
classes to add "framework behavior" to *your* code. As far as I know,
Howard is working on solving this with Tapestry 5.0. In principle, a
shared base class for your pages would be another possible way of
solving the problem, but it doesn't fit philosophically.

PSS: You might know that already, but implementation-wise you can
forgo the injection of the page and look it up using
IRequestCycle.getPage(...).

On 12/2/06, Ákos Maróy <da...@tyrell.hu> wrote:
> Hi,
>
> I have a question about listener semantics in Tapestry, about how one
> could achieve a simple usage semantics, when the target of a link or a
> form is a different page / component, than the invoker.
>
> The current typical usage for listeners in Tapestry is that the listener
> would be in the same page / component, as source of invocation (the
> form, link, etc.) This pattern in encouraged by the listener: binding as
> well.
[...]

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org