You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Geoff Callender <ge...@gmail.com> on 2013/01/01 09:19:33 UTC

Re: select blankOption

Agreed. To avoid thinking about the sequence of getters, set up the form in onPrepareForRender(). 

	Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/easyobject

As for the ValueEncoder do you need it?

	Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/id/1

On submit, there are many ways to get the chosen user, and each one has a cost. Off the top of my head, they include:

	* cache the list client-side as a t:hidden in the form. You'll need an encoder that can encode/decode the whole list to/from a String. 

	* cache the list client-side as a cookie. Probably not a good use of cookies.

	* cache the list server-side in the session. If you don't mind the overhead of a session then consider this:

		http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage

	* cache the list server-side in a conversation. See:

		http://jumpstart.doublenegative.com.au/jumpstart/examples/wizard/usingformfragments/START/wiz1

	* cache the list in the business tier. For how long? When should it be refreshed? memcache? ehcache?

	* no cache, get the user by id from the database. If the user has been updated by someone else you know immediately. 

Your encoder is doing the last one, and it seems like a pretty good way to start. IMHO, I'd avoid caching unless the use case demands it, or until the simpler solution is demonstrated to be causing a problem.

Geoff

On 31/12/2012, at 10:46 PM, Chris Poulsen wrote:

> Hi,
> 
> To me this looks like your problem is that you have a getters with
> side-effects? ( getUsers() for example ).
> 
> What if you set up your page-instance variables prior to rendering (not
> during - take a look at page lifecycle phases), then you could determine
> your blank option and retrieve/cache your user list without having "order"
> issues.
> 
> If I'm not mistaken about the ValueEncoder i think it is not used while you
> actually have the user list fetched (during initial page-render), but when
> you choose to act on some user object. Its id is passed along to the page
> and the encoder kicks in to provide you with a fully configured UserBean.
> So the UserBean fetches basically happens during two different requests,
> and probably only on the object(s) that you actually choose to do something
> with.
> - Of course you can cache the user list in session or similar and have it
> available for later use, but I do not really like that option.
> 
> -- 
> Chris
> 
> 
> On Mon, Dec 31, 2012 at 9:24 AM, John <jo...@quivinco.com> wrote:
> 
>> consider the following example
>> 
>> When the select component is rendered the call to getUOption precedes the
>> call to getUserSelectModel so the uOption state is not yet set.
>> 
>> <form t:type="form" t:id="form">
>>  <t:errors />
>>  <t:zone t:id="userZone" id="userZone">
>>   <t:select t:id="user" t:blankOption="prop:uOption"
>> t:model="userSelectModel"
>>    t:encoder="userEncoder" t:value="selectedUserId" t:zone="userZone" />
>> 
>>    public BlankOption getUOption() {
>> return uOption? BlankOption.ALWAYS:BlankOption.NEVER;
>>    }
>> 
>>    public SelectModel getUserSelectModel() {
>> return selectModelFactory.create(getUsers(), "username");
>>    }
>> 
>>    public List<UserBean> getUsers() {
>> try {
>>     List<UserBean> results =
>> usersDAO.getManagedUsers(stateBean.getUser().getUserId());
>>     if (results.size()) > 1) {
>>      uOption = true;
>>     } else {
>>      uOption = false;
>>     }
>>     return results;
>> } catch (Exception ex) {
>>     log.error("could not obtain users: " + ex.getMessage());
>> }
>> return null;
>>    }
>> 
>> 
>> Another dislike I have is with the ValueEncoder which has to invoke the
>> DAO again to obtain a matching user although these were already loaded up
>> when the model was created. It would be nice if the model cached the users
>> and simply referred back to them there.
>> 
>>    public UserBean toValue(String clientValue) {
>>      try {
>>          return usersDAO.getUser(Integer.parseInt(clientValue));
>>      } catch (Exception ex) {
>>          log.error("user encoder could not resolve clientValue to a
>> UserBean: "
>>           + ex.getMessage());
>>          return null;
>>      }
>>     }
>> 
>> So overall I see the need for some shared cache with the select component,
>> just loading a map up should do it?
>> 
>> John
>>  ----- Original Message -----
>>  From: Geoff Callender
>>  To: Tapestry users
>>  Sent: Sunday, December 30, 2012 3:00 PM
>>  Subject: Re: select blankOption
>> 
>> 
>>  Some code please.
>> 
>>  On 31/12/2012, at 1:42 AM, John wrote:
>> 
>>> I got that to work, it's great, I hadn't notice all the values can
>> have a prefix to change the scope.
>>> 
>>> The problem is that the method that returns the option values and sets
>> the blankOption value is invoked after the blankOption getter.
>>> 
>>> This is a problem I have hit before where a property value depends on
>> the result of a property value that is invoked later. I don't really want
>> to store results in the session and am wondering if I should use a cache
>> instead.
>>> 
>>> Is there any recomended approach in Tapestry? Maybe I should use
>> ehCache?
>>> 
>>> My typical example is a property that returns a collection from a DAO
>> to iterate over for a table or select list, and then some other property
>> whos state depends on that result in some way. So far I have been using
>> intermediate helper beans to bind the related state data.
>>> 
>>> John
>>> ----- Original Message -----
>>> From: Geoff Callender
>>> To: Tapestry users
>>> Sent: Sunday, December 30, 2012 1:37 PM
>>> Subject: Re: select blankOption
>>> 
>>> 
>>> Ah, so you were. Same applies.
>>> 
>>> On 31/12/2012, at 12:24 AM, John wrote:
>>> 
>>>> I was talking about blankOption, but I'll try with the prop prefix.
>>>> ----- Original Message -----
>>>> From: Geoff Callender
>>>> To: Tapestry users
>>>> Sent: Sunday, December 30, 2012 12:16 PM
>>>> Subject: Re: select blankOption
>>>> 
>>>> 
>>>> The default prefix is "literal". Use "prop", eg.
>> blankLabel="prop:yourProperty".
>>>> 
>>>> See Binding Expressions in
>> http://tapestry.apache.org/component-parameters.html .
>>>> 
>>>> Cheers,
>>>> 
>>>> Geoff
>>>> 
>>>> On 30/12/2012, at 11:07 PM, John wrote:
>>>> 
>>>>> I would like to choose dynamically whether to display the blank
>> option in my select component.
>>>>> 
>>>>> I am using it to display an ALL value and don't want to show this if
>> the select has only 1 option.
>>>>> 
>>>>> Unfortunately the docs say that the blankOption is a literal and not
>> a property. Is there any way to change that easily?
>>>>> 
>>>>> Maybe it's easiest just to add a new option with the name and value
>> I want?
>>>>> 
>>>>> John
>>>> 
>>>> 
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>> 
>>> 
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>> 
>> 
>>  ---------------------------------------------------------------------
>>  To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>  For additional commands, e-mail: users-help@tapestry.apache.org
>> 


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


Re: select blankOption

Posted by Geoff Callender <ge...@gmail.com>.
Forgot to mention that Hidden fields need extra care because they get lost in the redirect that follows a server-side validation failure. This show how to work around the problem with FLASH persistence:

	* http://jumpstart.doublenegative.com.au/jumpstart/examples/input/totalcontroledit1/1

Geoff

On 01/01/2013, at 7:19 PM, Geoff Callender wrote:

> Agreed. To avoid thinking about the sequence of getters, set up the form in onPrepareForRender(). 
> 
> 	Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/easyobject
> 
> As for the ValueEncoder do you need it?
> 
> 	Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/id/1
> 
> On submit, there are many ways to get the chosen user, and each one has a cost. Off the top of my head, they include:
> 
> 	* cache the list client-side as a t:hidden in the form. You'll need an encoder that can encode/decode the whole list to/from a String. 
> 
> 	* cache the list client-side as a cookie. Probably not a good use of cookies.
> 
> 	* cache the list server-side in the session. If you don't mind the overhead of a session then consider this:
> 
> 		http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage
> 
> 	* cache the list server-side in a conversation. See:
> 
> 		http://jumpstart.doublenegative.com.au/jumpstart/examples/wizard/usingformfragments/START/wiz1
> 
> 	* cache the list in the business tier. For how long? When should it be refreshed? memcache? ehcache?
> 
> 	* no cache, get the user by id from the database. If the user has been updated by someone else you know immediately. 
> 
> Your encoder is doing the last one, and it seems like a pretty good way to start. IMHO, I'd avoid caching unless the use case demands it, or until the simpler solution is demonstrated to be causing a problem.
> 
> Geoff
> 
> On 31/12/2012, at 10:46 PM, Chris Poulsen wrote:
> 
>> Hi,
>> 
>> To me this looks like your problem is that you have a getters with
>> side-effects? ( getUsers() for example ).
>> 
>> What if you set up your page-instance variables prior to rendering (not
>> during - take a look at page lifecycle phases), then you could determine
>> your blank option and retrieve/cache your user list without having "order"
>> issues.
>> 
>> If I'm not mistaken about the ValueEncoder i think it is not used while you
>> actually have the user list fetched (during initial page-render), but when
>> you choose to act on some user object. Its id is passed along to the page
>> and the encoder kicks in to provide you with a fully configured UserBean.
>> So the UserBean fetches basically happens during two different requests,
>> and probably only on the object(s) that you actually choose to do something
>> with.
>> - Of course you can cache the user list in session or similar and have it
>> available for later use, but I do not really like that option.
>> 
>> -- 
>> Chris
>> 
>> 
>> On Mon, Dec 31, 2012 at 9:24 AM, John <jo...@quivinco.com> wrote:
>> 
>>> consider the following example
>>> 
>>> When the select component is rendered the call to getUOption precedes the
>>> call to getUserSelectModel so the uOption state is not yet set.
>>> 
>>> <form t:type="form" t:id="form">
>>> <t:errors />
>>> <t:zone t:id="userZone" id="userZone">
>>>  <t:select t:id="user" t:blankOption="prop:uOption"
>>> t:model="userSelectModel"
>>>   t:encoder="userEncoder" t:value="selectedUserId" t:zone="userZone" />
>>> 
>>>   public BlankOption getUOption() {
>>> return uOption? BlankOption.ALWAYS:BlankOption.NEVER;
>>>   }
>>> 
>>>   public SelectModel getUserSelectModel() {
>>> return selectModelFactory.create(getUsers(), "username");
>>>   }
>>> 
>>>   public List<UserBean> getUsers() {
>>> try {
>>>    List<UserBean> results =
>>> usersDAO.getManagedUsers(stateBean.getUser().getUserId());
>>>    if (results.size()) > 1) {
>>>     uOption = true;
>>>    } else {
>>>     uOption = false;
>>>    }
>>>    return results;
>>> } catch (Exception ex) {
>>>    log.error("could not obtain users: " + ex.getMessage());
>>> }
>>> return null;
>>>   }
>>> 
>>> 
>>> Another dislike I have is with the ValueEncoder which has to invoke the
>>> DAO again to obtain a matching user although these were already loaded up
>>> when the model was created. It would be nice if the model cached the users
>>> and simply referred back to them there.
>>> 
>>>   public UserBean toValue(String clientValue) {
>>>     try {
>>>         return usersDAO.getUser(Integer.parseInt(clientValue));
>>>     } catch (Exception ex) {
>>>         log.error("user encoder could not resolve clientValue to a
>>> UserBean: "
>>>          + ex.getMessage());
>>>         return null;
>>>     }
>>>    }
>>> 
>>> So overall I see the need for some shared cache with the select component,
>>> just loading a map up should do it?
>>> 
>>> John
>>> ----- Original Message -----
>>> From: Geoff Callender
>>> To: Tapestry users
>>> Sent: Sunday, December 30, 2012 3:00 PM
>>> Subject: Re: select blankOption
>>> 
>>> 
>>> Some code please.
>>> 
>>> On 31/12/2012, at 1:42 AM, John wrote:
>>> 
>>>> I got that to work, it's great, I hadn't notice all the values can
>>> have a prefix to change the scope.
>>>> 
>>>> The problem is that the method that returns the option values and sets
>>> the blankOption value is invoked after the blankOption getter.
>>>> 
>>>> This is a problem I have hit before where a property value depends on
>>> the result of a property value that is invoked later. I don't really want
>>> to store results in the session and am wondering if I should use a cache
>>> instead.
>>>> 
>>>> Is there any recomended approach in Tapestry? Maybe I should use
>>> ehCache?
>>>> 
>>>> My typical example is a property that returns a collection from a DAO
>>> to iterate over for a table or select list, and then some other property
>>> whos state depends on that result in some way. So far I have been using
>>> intermediate helper beans to bind the related state data.
>>>> 
>>>> John
>>>> ----- Original Message -----
>>>> From: Geoff Callender
>>>> To: Tapestry users
>>>> Sent: Sunday, December 30, 2012 1:37 PM
>>>> Subject: Re: select blankOption
>>>> 
>>>> 
>>>> Ah, so you were. Same applies.
>>>> 
>>>> On 31/12/2012, at 12:24 AM, John wrote:
>>>> 
>>>>> I was talking about blankOption, but I'll try with the prop prefix.
>>>>> ----- Original Message -----
>>>>> From: Geoff Callender
>>>>> To: Tapestry users
>>>>> Sent: Sunday, December 30, 2012 12:16 PM
>>>>> Subject: Re: select blankOption
>>>>> 
>>>>> 
>>>>> The default prefix is "literal". Use "prop", eg.
>>> blankLabel="prop:yourProperty".
>>>>> 
>>>>> See Binding Expressions in
>>> http://tapestry.apache.org/component-parameters.html .
>>>>> 
>>>>> Cheers,
>>>>> 
>>>>> Geoff
>>>>> 
>>>>> On 30/12/2012, at 11:07 PM, John wrote:
>>>>> 
>>>>>> I would like to choose dynamically whether to display the blank
>>> option in my select component.
>>>>>> 
>>>>>> I am using it to display an ALL value and don't want to show this if
>>> the select has only 1 option.
>>>>>> 
>>>>>> Unfortunately the docs say that the blankOption is a literal and not
>>> a property. Is there any way to change that easily?
>>>>>> 
>>>>>> Maybe it's easiest just to add a new option with the name and value
>>> I want?
>>>>>> 
>>>>>> John
>>>>> 
>>>>> 
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>> 
>>>> 
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>> 
>>> 
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>> 
> 


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


Re: select blankOption

Posted by John <jo...@quivinco.com>.
Thanks for these ideas Geoff, very informative.

I decided to go for the @Cached idea Lance suggested on the thread "implementing a Tapestry IoC annotation to cache method results". It was very quick and effective to implement this solution and using native Tapestry techniques. I'm not clear where the data is cached though. For now this and stale data are not a big matter.

I still feel I have a preference for components that use lists from databases to be a bit more efficient in terms of their use of data and encapsulation. Perhaps I should just regard the Tapestry components as something I need to extend/modify for my own uses? But I am also reluctant to go down the route of making my own stuff and decreasing the familiarity of my code as well as introducing new stuff to go wrong.

John
  ----- Original Message ----- 
  From: Geoff Callender 
  To: Tapestry users 
  Sent: Tuesday, January 01, 2013 8:19 AM
  Subject: Re: select blankOption


  Agreed. To avoid thinking about the sequence of getters, set up the form in onPrepareForRender(). 

  Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/easyobject

  As for the ValueEncoder do you need it?

  Eg. http://jumpstart.doublenegative.com.au/jumpstart/examples/select/id/1

  On submit, there are many ways to get the chosen user, and each one has a cost. Off the top of my head, they include:

  * cache the list client-side as a t:hidden in the form. You'll need an encoder that can encode/decode the whole list to/from a String. 

  * cache the list client-side as a cookie. Probably not a good use of cookies.

  * cache the list server-side in the session. If you don't mind the overhead of a session then consider this:

  http://jumpstart.doublenegative.com.au/jumpstart/examples/state/storingdatainapage

  * cache the list server-side in a conversation. See:

  http://jumpstart.doublenegative.com.au/jumpstart/examples/wizard/usingformfragments/START/wiz1

  * cache the list in the business tier. For how long? When should it be refreshed? memcache? ehcache?

  * no cache, get the user by id from the database. If the user has been updated by someone else you know immediately. 

  Your encoder is doing the last one, and it seems like a pretty good way to start. IMHO, I'd avoid caching unless the use case demands it, or until the simpler solution is demonstrated to be causing a problem.

  Geoff

  On 31/12/2012, at 10:46 PM, Chris Poulsen wrote:

  > Hi,
  > 
  > To me this looks like your problem is that you have a getters with
  > side-effects? ( getUsers() for example ).
  > 
  > What if you set up your page-instance variables prior to rendering (not
  > during - take a look at page lifecycle phases), then you could determine
  > your blank option and retrieve/cache your user list without having "order"
  > issues.
  > 
  > If I'm not mistaken about the ValueEncoder i think it is not used while you
  > actually have the user list fetched (during initial page-render), but when
  > you choose to act on some user object. Its id is passed along to the page
  > and the encoder kicks in to provide you with a fully configured UserBean.
  > So the UserBean fetches basically happens during two different requests,
  > and probably only on the object(s) that you actually choose to do something
  > with.
  > - Of course you can cache the user list in session or similar and have it
  > available for later use, but I do not really like that option.
  > 
  > -- 
  > Chris
  > 
  > 
  > On Mon, Dec 31, 2012 at 9:24 AM, John <jo...@quivinco.com> wrote:
  > 
  >> consider the following example
  >> 
  >> When the select component is rendered the call to getUOption precedes the
  >> call to getUserSelectModel so the uOption state is not yet set.
  >> 
  >> <form t:type="form" t:id="form">
  >>  <t:errors />
  >>  <t:zone t:id="userZone" id="userZone">
  >>   <t:select t:id="user" t:blankOption="prop:uOption"
  >> t:model="userSelectModel"
  >>    t:encoder="userEncoder" t:value="selectedUserId" t:zone="userZone" />
  >> 
  >>    public BlankOption getUOption() {
  >> return uOption? BlankOption.ALWAYS:BlankOption.NEVER;
  >>    }
  >> 
  >>    public SelectModel getUserSelectModel() {
  >> return selectModelFactory.create(getUsers(), "username");
  >>    }
  >> 
  >>    public List<UserBean> getUsers() {
  >> try {
  >>     List<UserBean> results =
  >> usersDAO.getManagedUsers(stateBean.getUser().getUserId());
  >>     if (results.size()) > 1) {
  >>      uOption = true;
  >>     } else {
  >>      uOption = false;
  >>     }
  >>     return results;
  >> } catch (Exception ex) {
  >>     log.error("could not obtain users: " + ex.getMessage());
  >> }
  >> return null;
  >>    }
  >> 
  >> 
  >> Another dislike I have is with the ValueEncoder which has to invoke the
  >> DAO again to obtain a matching user although these were already loaded up
  >> when the model was created. It would be nice if the model cached the users
  >> and simply referred back to them there.
  >> 
  >>    public UserBean toValue(String clientValue) {
  >>      try {
  >>          return usersDAO.getUser(Integer.parseInt(clientValue));
  >>      } catch (Exception ex) {
  >>          log.error("user encoder could not resolve clientValue to a
  >> UserBean: "
  >>           + ex.getMessage());
  >>          return null;
  >>      }
  >>     }
  >> 
  >> So overall I see the need for some shared cache with the select component,
  >> just loading a map up should do it?
  >> 
  >> John
  >>  ----- Original Message -----
  >>  From: Geoff Callender
  >>  To: Tapestry users
  >>  Sent: Sunday, December 30, 2012 3:00 PM
  >>  Subject: Re: select blankOption
  >> 
  >> 
  >>  Some code please.
  >> 
  >>  On 31/12/2012, at 1:42 AM, John wrote:
  >> 
  >>> I got that to work, it's great, I hadn't notice all the values can
  >> have a prefix to change the scope.
  >>> 
  >>> The problem is that the method that returns the option values and sets
  >> the blankOption value is invoked after the blankOption getter.
  >>> 
  >>> This is a problem I have hit before where a property value depends on
  >> the result of a property value that is invoked later. I don't really want
  >> to store results in the session and am wondering if I should use a cache
  >> instead.
  >>> 
  >>> Is there any recomended approach in Tapestry? Maybe I should use
  >> ehCache?
  >>> 
  >>> My typical example is a property that returns a collection from a DAO
  >> to iterate over for a table or select list, and then some other property
  >> whos state depends on that result in some way. So far I have been using
  >> intermediate helper beans to bind the related state data.
  >>> 
  >>> John
  >>> ----- Original Message -----
  >>> From: Geoff Callender
  >>> To: Tapestry users
  >>> Sent: Sunday, December 30, 2012 1:37 PM
  >>> Subject: Re: select blankOption
  >>> 
  >>> 
  >>> Ah, so you were. Same applies.
  >>> 
  >>> On 31/12/2012, at 12:24 AM, John wrote:
  >>> 
  >>>> I was talking about blankOption, but I'll try with the prop prefix.
  >>>> ----- Original Message -----
  >>>> From: Geoff Callender
  >>>> To: Tapestry users
  >>>> Sent: Sunday, December 30, 2012 12:16 PM
  >>>> Subject: Re: select blankOption
  >>>> 
  >>>> 
  >>>> The default prefix is "literal". Use "prop", eg.
  >> blankLabel="prop:yourProperty".
  >>>> 
  >>>> See Binding Expressions in
  >> http://tapestry.apache.org/component-parameters.html .
  >>>> 
  >>>> Cheers,
  >>>> 
  >>>> Geoff
  >>>> 
  >>>> On 30/12/2012, at 11:07 PM, John wrote:
  >>>> 
  >>>>> I would like to choose dynamically whether to display the blank
  >> option in my select component.
  >>>>> 
  >>>>> I am using it to display an ALL value and don't want to show this if
  >> the select has only 1 option.
  >>>>> 
  >>>>> Unfortunately the docs say that the blankOption is a literal and not
  >> a property. Is there any way to change that easily?
  >>>>> 
  >>>>> Maybe it's easiest just to add a new option with the name and value
  >> I want?
  >>>>> 
  >>>>> John
  >>>> 
  >>>> 
  >>>> ---------------------------------------------------------------------
  >>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
  >>>> For additional commands, e-mail: users-help@tapestry.apache.org
  >>> 
  >>> 
  >>> ---------------------------------------------------------------------
  >>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
  >>> For additional commands, e-mail: users-help@tapestry.apache.org
  >> 
  >> 
  >>  ---------------------------------------------------------------------
  >>  To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
  >>  For additional commands, e-mail: users-help@tapestry.apache.org
  >> 


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