You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Leonardo Quijano Vincenzi <le...@dtqsoftware.com> on 2005/11/09 22:47:25 UTC

Creating a master / detail combobox pair (Ajax?)

Just as the title says, I'm trying to implement a re-usable way of 
binding two SELECTs together in a master / detail relationship. 
Basically what I need is to use one select (for example "Country") and 
bind a listener to the 'onchange' event. That'd listener would retrieve 
values for the associated detail combo (like "Province") and update the 
HTML.

Now, the tricky part is that I want to do this without form submissions. 
I'd rather use a parametrized listener, like this:

public void countryChanged(IRequestCycle cycle, Long provinceId) {
   ...
}

I guess I can also use a Ajax submit too, and update the combo box 
accordingly. But I definitively want to avoid page refreshing (that's 
why I'm using Ajax). What's the best way to accomplish this?? I've 
successfully associated an onchange() event to a listener without 
parameters (my preferred approach is to generate a javascript function 
for each listener, instead of using the @Refresh component), but I can't 
seem to find a way to do it in a parametrized way.

Any pointers?

-- 
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software




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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Leonardo Quijano Vincenzi <le...@dtqsoftware.com>.
hahaha... well go ahead and bring forth the components.

Then I'll owe you a beer, a virtual one at least... ;)

(i *really* have to get some sleep, anyway)

-- 
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software


Jesse Kuhnert wrote:
> Ughhh...That is a lot of code to read ;) It sounds like because your
> component "disappears" on the page the it is correctly being specified
> in your service as something that needs updating, but for one reason
> or another it never actually gets rendered, or doesn't get access to a
> writer that will send it's content back to the page...
>
> Hmmm hmmm...I had problems like this very early on but don't run
> across them anymore. My AjaxWebRequest has a hivemind logging
> interceptor that, when turned on, will display a log statement for
> every single component that does or doesn't get rendered.
> "isValidRequest" is if it's a valid ajax request, and
> "containsComponentId" shows the method call for each component and the
> id value it's checking. This has helped me in scnerios like this one.
>
> Ho humm ho humm..If you had only asked earlier I would have been able
> to save you so much trouble. I'm being told that "Lost" is going to be
> played on tivo now whether I like it or not by my wife so I have to go
> (until early tomorrow)....It will be a one hour change for me to do
> tomorrow morning, so that when you wake it will be to a new day filled
> with care free ajax form submissions and frivalous lattes served by
> buxom blonde aphrodite women...Or at least one filled with single form
> field form submissions that can easily be specified with onchange()
> attributes without the need to write special javascript or define
> components that don't get used.
>
> Make it so! Hah! Has enyone seen Extras lately? Piccard was there
> blathering on about his screenplay of him seeing women nude...It was
> very funny.
>
> j
>   



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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Jesse Kuhnert <jk...@gmail.com>.
Ughhh...That is a lot of code to read ;) It sounds like because your
component "disappears" on the page the it is correctly being specified
in your service as something that needs updating, but for one reason
or another it never actually gets rendered, or doesn't get access to a
writer that will send it's content back to the page...

Hmmm hmmm...I had problems like this very early on but don't run
across them anymore. My AjaxWebRequest has a hivemind logging
interceptor that, when turned on, will display a log statement for
every single component that does or doesn't get rendered.
"isValidRequest" is if it's a valid ajax request, and
"containsComponentId" shows the method call for each component and the
id value it's checking. This has helped me in scnerios like this one.

Ho humm ho humm..If you had only asked earlier I would have been able
to save you so much trouble. I'm being told that "Lost" is going to be
played on tivo now whether I like it or not by my wife so I have to go
(until early tomorrow)....It will be a one hour change for me to do
tomorrow morning, so that when you wake it will be to a new day filled
with care free ajax form submissions and frivalous lattes served by
buxom blonde aphrodite women...Or at least one filled with single form
field form submissions that can easily be specified with onchange()
attributes without the need to write special javascript or define
components that don't get used.

Make it so! Hah! Has enyone seen Extras lately? Piccard was there
blathering on about his screenplay of him seeing women nude...It was
very funny.

j
On 11/9/05, Leonardo Quijano Vincenzi <le...@dtqsoftware.com> wrote:
> Ok, my head's gonna explode (yeah! I should've listened to Patrick
> Casey!!)... but I have a solution *almost* there... At least for the
> simpler case.
> I think the perfomance considerations can be solved later, if they're
> really needed. A simple "behind the scenes" form submit with some
> components being refreshed would be enough for this case (better keep it
> simple!)
>
> Right now, I have a kind of derivation of AjaxDirectLink, AjaxForm,
> Refresh, etc... components. I called "AjaxListenerInvoker" and it
> generates a JavaScript function I can later hook into my components to
> invoke a listener on "onchange()" events, or such.
>
> For example: This generates a unique Javascript function on the body of
> my document.
>
> <span jwcid="countryChangeInvoker@AjaxListenerInvoker"
>       listener="listener:changeCountry"
>       updateComponents="ognl:{components.stateField.id}"
>       />
>
> Then, my 'master' select:
>
> <div>
>     <label jwcid="@FieldLabel" field="component:country" />
>   <select jwcid="country@PropertySelection" displayName="Country"
>           validators="validators:required"
>           onchange="ognl:components.countryChangeInvoker.href"
>           model="ognl:referenceData.getCountriesModel(true)"
>           value="ognl:value.country" />
> </div>
>
> And my 'detail' select:
>
> <div jwcid="stateField@Any"
>        id="ognl:components.stateField.id">
>   <label jwcid="@FieldLabel" field="component:state" />
>   <select jwcid="state@PropertySelection" displayName="State"
>           validators="validators:required"
>           model="ognl:referenceData.getStatesModel(true, countryId)"
>           value="ognl:value.state" />
> </div>
>
> Now... right now a change in the master select would submit the form
> (using AjaxListenerInvoker) and invoke a listener on the page. The
> listener does nothing... just because right now the values are generated
> on form rendering, not on listener invocation. That could change (and
> does change on some components).
>
> The only problem I have right now is that updateComponents is failing to
> work. It seems that it invokes the listener fine, gets the appropiate
> response to the selected country, but the states field disappears from
> the page!! I'm hitting my head on the wall on this, maybe you can help out.
>
> I implemented the script component similar to what Tacos' does. Like this:
>
> AjaxListenerInvoker.script:
> *****************************************************************************************************
> <?xml version="1.0"?>
> <!DOCTYPE script PUBLIC
>     "-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
>     "http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
> <script>
>   <include-script resource-path="/net/sf/tacos/ajax/components/tacos.js" />
>   <include-script
> resource-path="/net/sf/tacos/ajax/components/prototype.js" />
>
>   <input-symbol key="hiddenId" class="java.lang.String" required="yes"/>
>   <input-symbol key="formId" required="yes" />
>   <input-symbol key="url" required="yes" />
>   <input-symbol key="evalScripts" required="yes" />
>   <input-symbol key="updateObject" required="yes" />
>   <input-symbol key="effects" />
>   <input-symbol key="statusElement" />
>
>   <let key="functionName" unique="yes">
>     ajaxListenerInvoker_${hiddenId}
>   </let>
>
>   <let key="href">
>     javascript:${functionName}();
>   </let>
>
>   <body>
>     <unique> <![CDATA[
>       dojo.require("dojo.fx.html");
>     ]]></unique>
>
>     <![CDATA[
>         function ${functionName}() {
>         Tapestry.find('${hiddenId}').value = "T";
>         tacos.formSubmit(
>         {
>            url: "${url}",
>            processScripts: "${evalScripts}",
>            effects: ${effects},
>            updateObject: ${updateObject},
>            formId: "${formId}",
>            changeUrl: false,
>            statusElement: "${statusElement}"
>         });
>       }
>     ]]>
>   </body>
>
> </script>
> *****************************************************************************************************
>
> AjaxListenerInvoker.jwc:
> *****************************************************************************************************
> <component-specification allow-body="no" allow-informal-parameters="no">
>     <parameter name="updateComponents" />
>     <parameter name="updateBlocks" />
>     <parameter name="processScripts" default-value="ognl:false" />
>     <parameter name="updateObject" />
>     <parameter name="listener" required="yes" />
>     <parameter name="parameters" />
>     <parameter name="direct" />
>     <parameter name="effects" />
>     <parameter name="statusElement" />
>     <parameter name="stateful" />
>     <parameter name="disabled" />
>     <parameter name="id" property="idParameter" default-value="id"/>
>     <parameter name="anchor"/>
>     <parameter name="renderer"/>
>
>     <inject property="listenerInvoker"
> object="infrastructure:listenerInvoker"/>
>     <inject property="ajaxEngineService" object="service:tacos.ajaxdirect"/>
>     <inject property="script" type="script"
> object="AjaxListenerInvoker.script"/>
>
>     <reserved-parameter name="href"/>
>     <reserved-parameter name="onclick"/>
>
> </component-specification>
> *****************************************************************************************************
>
> AjaxListenerInvoker.java (I stripped all comments... after all they are
> all in Spanish :P):
> *****************************************************************************************************
> import *;
>
> /** @author Leonardo Quijano Vincenzi  **/
> public abstract class AjaxListenerInvoker extends AbstractFormComponent {
>
>     @Override
>     protected boolean getCanTakeFocus() {
>         return false;
>     }
>
>     protected boolean isClicked(IRequestCycle cycle, String name) {
>         String value = cycle.getParameter(name);
>         return HiveMind.isNonBlank(value);
>     }
>
>     public static Object[] constructServiceParameters(Object
> parameterValue) {
>         Object result[] = null;
>
>         if(parameterValue != null) {
>             if(parameterValue instanceof Object[]) { result = (Object
> []) parameterValue;    }
>             else if(parameterValue instanceof List) {
>                 List list = (List) parameterValue;
>                 result = list.toArray();
>             }
>             else { return new Object[] { parameterValue };   }
>         }
>
>         return result;
>     }
>
>     /** @see AbstractFormComponent#renderFormComponent(IMarkupWriter,
> IRequestCycle)  */
>     @SuppressWarnings("unchecked")
>     @Override
>     protected void renderFormComponent(IMarkupWriter writer,
> IRequestCycle cycle) {
>         boolean disabled = isDisabled();
>
>         IForm form = getForm();
>         String name = getName();
>
>         String hiddenId =
> cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(getIdParameter()));
>
>         setClientId(hiddenId);
>         form.addHiddenValue(name, hiddenId, "");
>
>         if(!disabled) {
>             Object[] parameters =
> constructServiceParameters(getParameters());
>
>             String[] updateComponents = new String[0];
>             if (getUpdateComponents() != null) {
>                 updateComponents = (String[]) getUpdateComponents()
>                     .toArray(new String[getUpdateComponents().size()]);
>             }
>
>             //TODO: Add more html block logic hooks
>             String[] updateBlocks = new String[0];
>             if (getUpdateBlocks() != null) {
>                 updateBlocks = (String[])getUpdateBlocks()
>                     .toArray(new String[getUpdateBlocks().size()]);
>             }
>
>             AjaxDirectServiceParameter dsp = new AjaxDirectServiceParameter(
>                 (IDirect) form, parameters, updateComponents,
> updateBlocks, isDirect());
>             String reqUrl = getAjaxEngineService().getLink(
>                 cycle, false, dsp).getAbsoluteURL();
>
>             Map<String, Object> symbols = new HashMap<String, Object>();
>             symbols.put("hiddenId", hiddenId);
>             symbols.put("formId", form.getName());
>             symbols.put("url", reqUrl);
>             symbols.put("evalScripts", String.valueOf(isProcessScripts()));
>             if(getUpdateObject() != null) {
>                 symbols.put("updateObject", getUpdateObject());
>             }
>             else {
>                 symbols.put("updateObject", "null");
>             }
>             if (getEffects() != null) {
>                 symbols.put("effects", StringUtils.escapeJavaScriptHash(
>                     getEffects()));
>             }
>             else{
>                 symbols.put("effects", "{}");
>             }
>             if(getStatusElement() != null) {
>                 symbols.put("statusElement", getStatusElement());
>             }
>
>             PageRenderSupport pageRenderSupport =
>                 TapestryUtils.getPageRenderSupport(cycle, this);
>             getScript().execute(
>                 cycle, pageRenderSupport, symbols);
>
>             setHref((String) symbols.get("href"));
>             setFunctionName((String) symbols.get("functionName"));
>         }
>     }
>
>     /** @see AbstractFormComponent#rewindFormComponent(IMarkupWriter,
> IRequestCycle) */
>     @Override
>     protected void rewindFormComponent(IMarkupWriter writer,
> IRequestCycle cycle) {
>         if(isClicked(cycle, getName())) {
>             handleClick(cycle, getForm());
>         }
>     }
>
>     private void handleClick(final IRequestCycle cycle, IForm form) {
>         final IActionListener listener = getListener();
>         final IActionListener action = getAction();
>
>         if(listener == null && action == null) {
>             return;
>         }
>
>         final ListenerInvoker listenerInvoker = getListenerInvoker();
>
>         Object parameters = getParameters();
>         if (parameters != null) {
>             if (parameters instanceof Collection) {
>                 cycle.setListenerParameters(((Collection)
> parameters).toArray());
>             }
>             else {
>                 cycle.setListenerParameters(new Object[] { parameters });
>             }
>         }
>
>         if(listener != null) {
>             listenerInvoker.invokeListener(listener,
> AjaxListenerInvoker.this, cycle);
>         }
>
>         if(action != null) {
>             Runnable notify = new Runnable() {
>                 public void run() {
>                     listenerInvoker.invokeListener(
>                          action, AjaxListenerInvoker.this, cycle);
>                 }
>             };
>
>             form.addDeferredRunnable(notify);
>         }
>     }
>
>     public abstract IActionListener getListener();
>     public abstract IActionListener getAction();
>     public abstract Object getParameters();
>     public abstract String getHref();
>     public abstract void setHref(String href);
>     public abstract ListenerInvoker getListenerInvoker();
>     public abstract IScript getScript();
>     public abstract AjaxDirectService getAjaxEngineService();
>     public abstract Collection getUpdateComponents();
>     public abstract Collection getUpdateBlocks();
>     public abstract boolean isProcessScripts();
>     public abstract String getUpdateObject();
>     public abstract boolean isDirect();
>     public abstract String getEffects();
>     public abstract String getStatusElement();
>     public abstract boolean isDisabled();
> }
> *****************************************************************************************************
>
> --
> Ing. Leonardo Quijano Vincenzi
> Director T�cnico
> DTQ Software
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
>
>

Re: Creating a master / detail combobox pair (Ajax?)

Posted by Leonardo Quijano Vincenzi <le...@dtqsoftware.com>.
Ok, my head's gonna explode (yeah! I should've listened to Patrick 
Casey!!)... but I have a solution *almost* there... At least for the 
simpler case.
I think the perfomance considerations can be solved later, if they're 
really needed. A simple "behind the scenes" form submit with some 
components being refreshed would be enough for this case (better keep it 
simple!)

Right now, I have a kind of derivation of AjaxDirectLink, AjaxForm, 
Refresh, etc... components. I called "AjaxListenerInvoker" and it 
generates a JavaScript function I can later hook into my components to 
invoke a listener on "onchange()" events, or such.

For example: This generates a unique Javascript function on the body of 
my document.

<span jwcid="countryChangeInvoker@AjaxListenerInvoker"
      listener="listener:changeCountry"
      updateComponents="ognl:{components.stateField.id}"
      />

Then, my 'master' select:

<div>
    <label jwcid="@FieldLabel" field="component:country" />
  <select jwcid="country@PropertySelection" displayName="Country"
          validators="validators:required"
          onchange="ognl:components.countryChangeInvoker.href"
          model="ognl:referenceData.getCountriesModel(true)"
          value="ognl:value.country" />
</div>

And my 'detail' select:

<div jwcid="stateField@Any"
       id="ognl:components.stateField.id">
  <label jwcid="@FieldLabel" field="component:state" />
  <select jwcid="state@PropertySelection" displayName="State"
          validators="validators:required"
          model="ognl:referenceData.getStatesModel(true, countryId)"
          value="ognl:value.state" />
</div>

Now... right now a change in the master select would submit the form 
(using AjaxListenerInvoker) and invoke a listener on the page. The 
listener does nothing... just because right now the values are generated 
on form rendering, not on listener invocation. That could change (and 
does change on some components).

The only problem I have right now is that updateComponents is failing to 
work. It seems that it invokes the listener fine, gets the appropiate 
response to the selected country, but the states field disappears from 
the page!! I'm hitting my head on the wall on this, maybe you can help out.

I implemented the script component similar to what Tacos' does. Like this:

AjaxListenerInvoker.script:
*****************************************************************************************************
<?xml version="1.0"?>
<!DOCTYPE script PUBLIC
    "-//Apache Software Foundation//Tapestry Script Specification 3.0//EN"
    "http://jakarta.apache.org/tapestry/dtd/Script_3_0.dtd">
<script>
  <include-script resource-path="/net/sf/tacos/ajax/components/tacos.js" />
  <include-script 
resource-path="/net/sf/tacos/ajax/components/prototype.js" />

  <input-symbol key="hiddenId" class="java.lang.String" required="yes"/>
  <input-symbol key="formId" required="yes" />
  <input-symbol key="url" required="yes" />
  <input-symbol key="evalScripts" required="yes" />
  <input-symbol key="updateObject" required="yes" />
  <input-symbol key="effects" />
  <input-symbol key="statusElement" />
   
  <let key="functionName" unique="yes">
    ajaxListenerInvoker_${hiddenId}
  </let>

  <let key="href">
    javascript:${functionName}();
  </let>

  <body>
    <unique> <![CDATA[
      dojo.require("dojo.fx.html");
    ]]></unique>

    <![CDATA[
        function ${functionName}() {
        Tapestry.find('${hiddenId}').value = "T";
        tacos.formSubmit(
        {
           url: "${url}",
           processScripts: "${evalScripts}",
           effects: ${effects},
           updateObject: ${updateObject},
           formId: "${formId}",
           changeUrl: false,
           statusElement: "${statusElement}"
        });
      }
    ]]>
  </body>
 
</script>
*****************************************************************************************************

AjaxListenerInvoker.jwc:
*****************************************************************************************************
<component-specification allow-body="no" allow-informal-parameters="no">
    <parameter name="updateComponents" />
    <parameter name="updateBlocks" />
    <parameter name="processScripts" default-value="ognl:false" />
    <parameter name="updateObject" />
    <parameter name="listener" required="yes" />
    <parameter name="parameters" />
    <parameter name="direct" />
    <parameter name="effects" />
    <parameter name="statusElement" />
    <parameter name="stateful" />
    <parameter name="disabled" />
    <parameter name="id" property="idParameter" default-value="id"/>
    <parameter name="anchor"/>
    <parameter name="renderer"/>

    <inject property="listenerInvoker" 
object="infrastructure:listenerInvoker"/>
    <inject property="ajaxEngineService" object="service:tacos.ajaxdirect"/>
    <inject property="script" type="script" 
object="AjaxListenerInvoker.script"/>
   
    <reserved-parameter name="href"/>
    <reserved-parameter name="onclick"/>
   
</component-specification>
*****************************************************************************************************

AjaxListenerInvoker.java (I stripped all comments... after all they are 
all in Spanish :P):
*****************************************************************************************************
import *;

/** @author Leonardo Quijano Vincenzi  **/
public abstract class AjaxListenerInvoker extends AbstractFormComponent {
   
    @Override
    protected boolean getCanTakeFocus() {
        return false;
    }
   
    protected boolean isClicked(IRequestCycle cycle, String name) {
        String value = cycle.getParameter(name);
        return HiveMind.isNonBlank(value);
    }
  
    public static Object[] constructServiceParameters(Object 
parameterValue) {
        Object result[] = null;
       
        if(parameterValue != null) {
            if(parameterValue instanceof Object[]) { result = (Object 
[]) parameterValue;    }
            else if(parameterValue instanceof List) {
                List list = (List) parameterValue;
                result = list.toArray();
            }
            else { return new Object[] { parameterValue };   }
        }
       
        return result;
    }
   
    /** @see AbstractFormComponent#renderFormComponent(IMarkupWriter, 
IRequestCycle)  */
    @SuppressWarnings("unchecked")
    @Override
    protected void renderFormComponent(IMarkupWriter writer, 
IRequestCycle cycle) {
        boolean disabled = isDisabled();

        IForm form = getForm();
        String name = getName();

        String hiddenId = 
cycle.getUniqueId(TapestryUtils.convertTapestryIdToNMToken(getIdParameter()));

        setClientId(hiddenId);
        form.addHiddenValue(name, hiddenId, "");

        if(!disabled) {
            Object[] parameters = 
constructServiceParameters(getParameters());
           
            String[] updateComponents = new String[0];
            if (getUpdateComponents() != null) {
                updateComponents = (String[]) getUpdateComponents()
                    .toArray(new String[getUpdateComponents().size()]);
            }
           
            //TODO: Add more html block logic hooks
            String[] updateBlocks = new String[0];
            if (getUpdateBlocks() != null) {
                updateBlocks = (String[])getUpdateBlocks()
                    .toArray(new String[getUpdateBlocks().size()]);
            }
           
            AjaxDirectServiceParameter dsp = new AjaxDirectServiceParameter(
                (IDirect) form, parameters, updateComponents, 
updateBlocks, isDirect());          
            String reqUrl = getAjaxEngineService().getLink(
                cycle, false, dsp).getAbsoluteURL();
           
            Map<String, Object> symbols = new HashMap<String, Object>();
            symbols.put("hiddenId", hiddenId);
            symbols.put("formId", form.getName());
            symbols.put("url", reqUrl);
            symbols.put("evalScripts", String.valueOf(isProcessScripts()));
            if(getUpdateObject() != null) {
                symbols.put("updateObject", getUpdateObject());
            }
            else {
                symbols.put("updateObject", "null");
            }           
            if (getEffects() != null) {
                symbols.put("effects", StringUtils.escapeJavaScriptHash(
                    getEffects()));
            }
            else{
                symbols.put("effects", "{}");
            }
            if(getStatusElement() != null) {
                symbols.put("statusElement", getStatusElement());
            }

            PageRenderSupport pageRenderSupport =
                TapestryUtils.getPageRenderSupport(cycle, this);
            getScript().execute(
                cycle, pageRenderSupport, symbols);
           
            setHref((String) symbols.get("href"));
            setFunctionName((String) symbols.get("functionName"));
        }
    }
   
    /** @see AbstractFormComponent#rewindFormComponent(IMarkupWriter, 
IRequestCycle) */
    @Override
    protected void rewindFormComponent(IMarkupWriter writer, 
IRequestCycle cycle) {
        if(isClicked(cycle, getName())) {
            handleClick(cycle, getForm());
        }
    }
   
    private void handleClick(final IRequestCycle cycle, IForm form) {
        final IActionListener listener = getListener();
        final IActionListener action = getAction();

        if(listener == null && action == null) {
            return;
        }

        final ListenerInvoker listenerInvoker = getListenerInvoker();

        Object parameters = getParameters();
        if (parameters != null) {
            if (parameters instanceof Collection) {
                cycle.setListenerParameters(((Collection) 
parameters).toArray());
            }
            else {
                cycle.setListenerParameters(new Object[] { parameters });
            }
        }

        if(listener != null) {
            listenerInvoker.invokeListener(listener, 
AjaxListenerInvoker.this, cycle);
        }
       
        if(action != null) {
            Runnable notify = new Runnable() {
                public void run() {
                    listenerInvoker.invokeListener(
                         action, AjaxListenerInvoker.this, cycle);
                }
            };
   
            form.addDeferredRunnable(notify);
        }
    }

    public abstract IActionListener getListener();
    public abstract IActionListener getAction();
    public abstract Object getParameters();
    public abstract String getHref();
    public abstract void setHref(String href);
    public abstract ListenerInvoker getListenerInvoker();
    public abstract IScript getScript();
    public abstract AjaxDirectService getAjaxEngineService();
    public abstract Collection getUpdateComponents();
    public abstract Collection getUpdateBlocks();
    public abstract boolean isProcessScripts();
    public abstract String getUpdateObject();
    public abstract boolean isDirect();
    public abstract String getEffects();
    public abstract String getStatusElement();
    public abstract boolean isDisabled();
}
*****************************************************************************************************

-- 
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software




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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Jesse Kuhnert <jk...@gmail.com>.
I was thinking of something a little bit simpler perhaps.

I know with dojo at least, I can generate a base URL that would be
able to hit the form component of the specified field(s). I can then
decide to either do the default, which is pass in the formNode to
dojo, letting it embed the whole form...OR, I can pick and choose
which fields to send.

In your case, I would submit to the form, only sending the data of the
selected combo-box. Then, because your request would ask that nothing
else be done, the request would flow the block you had asked be
refreshed (the dependent box) and let it render, which should
hopefully satisfy the If statement because the first value had been
selected.

I like the above mentioned approach because it maintains state, and
because it's not a giant leap in learning for most people.

With that being said, you also highlight what more "advanced" users
seem to be wanting, which is more JSON semantic approaches to invoking
server methods, ie hivemind services...

I think ultimately there may be slight performance gains to the latter
approach, but it complicates the clients job considerably. Perhaps we
are finally getting into the area where special circumstances have to
start being accounted for for each specific component. Such as a
simple list which doesn't need any html to come with it. ...

I don't see why in your particular case you couldn't use the
direct=true approach, which would avoid touching any of those DB
dependent render areas. Unless of course you are in a loop, then
things become harder....

There has to be a middle ground somewhere. If I add something in I'd
like to at least ~think~ it will be generic enough to be useful in
other circumstances. God damn JSON is really biting at my heals
now...Bad JSON bad! (sorry...thinking cartman here)

Am I making sense? Ok...So I guess because I've started using JSON
with the grid, there are probably plenty of other components ripe for
this kind of specialised more efficient choice....I think I can
bravely sort of determine that a component is a ComboBox (or whatever
it's called in tapestry) and decide to render a JSON block as the
output instead of HTML.

Does this sound right to you?

I can see from your posting that you have a very clear picture of
what's going on overall, why don't you help add some of this into
tacos? Or split it up, or something. I won't ignore patches I assure
you. (unless they aren't umm..optimal, but I don't think yours
will..heh)

let me know so I don't kill myself on something not getting used..Not
that I won't use it myself, but first things first...

jesse
On 11/9/05, Leonardo Quijano Vincenzi <le...@dtqsoftware.com> wrote:
> Cool. I'm currently testing a Submit-based approach using
> tacos.formSubmit(). Now, there are some backLink / forwardLink params
> I'm removing on purpose, to avoid my page from changing after the
> submit. I hope I am right... hehe
>
> As for the functionality, I guess we can have 2 approaches:
>
> 1) The simpler, less performant option: Simply submit() the whole form
> but don't refresh the page. That's the one I'm currently implementing
> for my Friday milestone. It has, for me, 2 caveats:
>  a) The whole form is submitted just to update one value. That's
> sub-optimal.
>  b) We are changing server-side state for only a submit operation. Keep
> in mind that I'm taking the approach of having a lot of nesting levels
> on the component. My component could be embedded in a full 30+ component
> tree. Some of the other components could have nasty side-effects when
> submitted (the basic example is this refresh / submit / cancel dilemma:
> We certainly don't want to register a new 'Client' just to select his
> proper US State from a Country / State select combination).
>
> 2) The more complex option (IMO) of directly invoking listeners. Those
> listeners might not be in the same page. For example, I might implement
> a special "ReferenceService" which only generates data for the
> PropertySelection objects. That way I can go and do something like this:
>
> /reference.svc?name=states&country=US
>
> And it would get me only the states for United States. Then I'd avoid
> all other page generation (think of a large page with lots of
> mini-components that hit the database). That's one choice... the other
> one being a single listener:
>
> public void countryChanged(IRequestCycle cycle) {
>   ....
> }
>
> It would update a server-side collection of states with the new
> information. Then I update the states component.
> I guess, for the sake of simplicity, we can always get 1), see if it
> works, and see if 2) is really needed.
>
>
> Jesse Kuhnert wrote:
> > I think there have been enough postings about the submit functionality
> > that I feel comforatable addressing it head-on. I think I will create
> > a wiki page listing the requirements and assumptions I have and let
> > other people add-on/change what I start.
> >
> > But, to answer your question sort of immediately, the options are
> > fairly limitless. The only caveat I would place is that whatever
> > functionality is used, the page should more or less look the same,
> > code-wise, as it would without the ajax, just so it stays more
> > intuitive...Ie: you will probably have something like:
> >
> > <select id="master" ></select>
> >
> > <span jwcid="@If" condition="ognl:master != null (you know what i mean)">
> > <select id="optionBasedOnMaster"></select>
> > </span>
> >
> > It's pretty incredibly easy to add arbitrary parameters of data to a
> > dojo io request. The AjaxDirectService that you hinted at already
> > takes parameters, not caring if they come from a form submission or
> > link request. It's all the same :) (AjaxDirectServiceParameter).
> >
> > Not knowing exactly what you are expecting to happen on the client is
> > why I think I'm just going to have to go ahead and provide the full
> > suite of functionality in some sort of AjaxSubmit button. There are
> > too many possibilities...
> > -) Only updating the selected value, and then only refreshing the
> > select list depending on it. This also brings up whether or not you
> > want validation to occurr? If so in this scenerio it probably doesn't
> > make sense to validate the whole form? Maybe for some it does...
> >
> > -) Choosing which type of listener...Ie refresh/cancel/submit...etc..
> >
> > -) Disabling validation optionally on all or some parts of the forms
> > validation delegate...
> >
> > -) Optionally submitting only partial parts of a form. Ie only some
> > values change and not others.
> >
> > This is fairly easy to do already, but I don't want to hack anything
> > in...To be fair to you though here is a quick snippet of javascript
> > that you might use to do it..:
> >
> > <select id="master" onchange="javascript:return myFunction(this.value)">
> > <option value="1" >Numba 1</option>
> > <option value="2">Numba 2</option>
> > </select>
> >
> > <script>
> > function myFunction(value) {
> >    dojo.io.bind( {
> >
> >        });
> > }
> > </script>
> >
> > You can use the tacos methods, but you'll have to find a way to get a
> > base url string. Refresh might do it...or simply declaring an
> > ajaxdirectlink  component, but instead of rendering it doing an
> > ognl:component:yourlink.getLink().getAbsoluteURL() to get it.
> >
> > The relevant dojo page is here:
> > http://dojotoolkit.org/docs/intro_to_dojo_io.html
> >
> > Otherwise, I'll probably try to put the component together later
> > tonight, which will mostl likely spill into tomorrow, and even extend
> > until friday. :) I'm sure there will be enough pieces in place to get
> > the functionality you want by tomorrow though...
> >
> > jesse
> > On 11/9/05, Leonardo Quijano Vincenzi <le...@dtqsoftware.com> wrote:
> >
> --
> Ing. Leonardo Quijano Vincenzi
> Director Técnico
> DTQ Software
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
>
>

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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Leonardo Quijano Vincenzi <le...@dtqsoftware.com>.
Cool. I'm currently testing a Submit-based approach using 
tacos.formSubmit(). Now, there are some backLink / forwardLink params 
I'm removing on purpose, to avoid my page from changing after the 
submit. I hope I am right... hehe

As for the functionality, I guess we can have 2 approaches:

1) The simpler, less performant option: Simply submit() the whole form 
but don't refresh the page. That's the one I'm currently implementing 
for my Friday milestone. It has, for me, 2 caveats:
 a) The whole form is submitted just to update one value. That's 
sub-optimal.
 b) We are changing server-side state for only a submit operation. Keep 
in mind that I'm taking the approach of having a lot of nesting levels 
on the component. My component could be embedded in a full 30+ component 
tree. Some of the other components could have nasty side-effects when 
submitted (the basic example is this refresh / submit / cancel dilemma: 
We certainly don't want to register a new 'Client' just to select his 
proper US State from a Country / State select combination).

2) The more complex option (IMO) of directly invoking listeners. Those 
listeners might not be in the same page. For example, I might implement 
a special "ReferenceService" which only generates data for the 
PropertySelection objects. That way I can go and do something like this:

/reference.svc?name=states&country=US

And it would get me only the states for United States. Then I'd avoid 
all other page generation (think of a large page with lots of 
mini-components that hit the database). That's one choice... the other 
one being a single listener:

public void countryChanged(IRequestCycle cycle) {
  ....
}

It would update a server-side collection of states with the new 
information. Then I update the states component.
I guess, for the sake of simplicity, we can always get 1), see if it 
works, and see if 2) is really needed.


Jesse Kuhnert wrote:
> I think there have been enough postings about the submit functionality
> that I feel comforatable addressing it head-on. I think I will create
> a wiki page listing the requirements and assumptions I have and let
> other people add-on/change what I start.
>
> But, to answer your question sort of immediately, the options are
> fairly limitless. The only caveat I would place is that whatever
> functionality is used, the page should more or less look the same,
> code-wise, as it would without the ajax, just so it stays more
> intuitive...Ie: you will probably have something like:
>
> <select id="master" ></select>
>
> <span jwcid="@If" condition="ognl:master != null (you know what i mean)">
> <select id="optionBasedOnMaster"></select>
> </span>
>
> It's pretty incredibly easy to add arbitrary parameters of data to a
> dojo io request. The AjaxDirectService that you hinted at already
> takes parameters, not caring if they come from a form submission or
> link request. It's all the same :) (AjaxDirectServiceParameter).
>
> Not knowing exactly what you are expecting to happen on the client is
> why I think I'm just going to have to go ahead and provide the full
> suite of functionality in some sort of AjaxSubmit button. There are
> too many possibilities...
> -) Only updating the selected value, and then only refreshing the
> select list depending on it. This also brings up whether or not you
> want validation to occurr? If so in this scenerio it probably doesn't
> make sense to validate the whole form? Maybe for some it does...
>
> -) Choosing which type of listener...Ie refresh/cancel/submit...etc..
>
> -) Disabling validation optionally on all or some parts of the forms
> validation delegate...
>
> -) Optionally submitting only partial parts of a form. Ie only some
> values change and not others.
>
> This is fairly easy to do already, but I don't want to hack anything
> in...To be fair to you though here is a quick snippet of javascript
> that you might use to do it..:
>
> <select id="master" onchange="javascript:return myFunction(this.value)">
> <option value="1" >Numba 1</option>
> <option value="2">Numba 2</option>
> </select>
>
> <script>
> function myFunction(value) {
>    dojo.io.bind( {
>
>        });
> }
> </script>
>
> You can use the tacos methods, but you'll have to find a way to get a
> base url string. Refresh might do it...or simply declaring an
> ajaxdirectlink  component, but instead of rendering it doing an
> ognl:component:yourlink.getLink().getAbsoluteURL() to get it.
>
> The relevant dojo page is here:
> http://dojotoolkit.org/docs/intro_to_dojo_io.html
>
> Otherwise, I'll probably try to put the component together later
> tonight, which will mostl likely spill into tomorrow, and even extend
> until friday. :) I'm sure there will be enough pieces in place to get
> the functionality you want by tomorrow though...
>
> jesse
> On 11/9/05, Leonardo Quijano Vincenzi <le...@dtqsoftware.com> wrote:
>   
-- 
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software




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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Jesse Kuhnert <jk...@gmail.com>.
I think there have been enough postings about the submit functionality
that I feel comforatable addressing it head-on. I think I will create
a wiki page listing the requirements and assumptions I have and let
other people add-on/change what I start.

But, to answer your question sort of immediately, the options are
fairly limitless. The only caveat I would place is that whatever
functionality is used, the page should more or less look the same,
code-wise, as it would without the ajax, just so it stays more
intuitive...Ie: you will probably have something like:

<select id="master" ></select>

<span jwcid="@If" condition="ognl:master != null (you know what i mean)">
<select id="optionBasedOnMaster"></select>
</span>

It's pretty incredibly easy to add arbitrary parameters of data to a
dojo io request. The AjaxDirectService that you hinted at already
takes parameters, not caring if they come from a form submission or
link request. It's all the same :) (AjaxDirectServiceParameter).

Not knowing exactly what you are expecting to happen on the client is
why I think I'm just going to have to go ahead and provide the full
suite of functionality in some sort of AjaxSubmit button. There are
too many possibilities...
-) Only updating the selected value, and then only refreshing the
select list depending on it. This also brings up whether or not you
want validation to occurr? If so in this scenerio it probably doesn't
make sense to validate the whole form? Maybe for some it does...

-) Choosing which type of listener...Ie refresh/cancel/submit...etc..

-) Disabling validation optionally on all or some parts of the forms
validation delegate...

-) Optionally submitting only partial parts of a form. Ie only some
values change and not others.

This is fairly easy to do already, but I don't want to hack anything
in...To be fair to you though here is a quick snippet of javascript
that you might use to do it..:

<select id="master" onchange="javascript:return myFunction(this.value)">
<option value="1" >Numba 1</option>
<option value="2">Numba 2</option>
</select>

<script>
function myFunction(value) {
   dojo.io.bind( {

       });
}
</script>

You can use the tacos methods, but you'll have to find a way to get a
base url string. Refresh might do it...or simply declaring an
ajaxdirectlink  component, but instead of rendering it doing an
ognl:component:yourlink.getLink().getAbsoluteURL() to get it.

The relevant dojo page is here:
http://dojotoolkit.org/docs/intro_to_dojo_io.html

Otherwise, I'll probably try to put the component together later
tonight, which will mostl likely spill into tomorrow, and even extend
until friday. :) I'm sure there will be enough pieces in place to get
the functionality you want by tomorrow though...

jesse
On 11/9/05, Leonardo Quijano Vincenzi <le...@dtqsoftware.com> wrote:
> Patrick Casey wrote:
> >       I don't think there's an oob master->child combo box implementation
> > out there for tapestry so you'd have to write your own. If you've never
> > written AJAX stuff before, and/or aren't really comfortable with javascript
> > and raw servlet processing, I'd honestly consider going another way (like
> > biting the bullet and submitting).
> >
> That's what I was doing. But then our client for this project has
> already repeated several times that he doesn't like the 'page refresh',
> and worse, I agree with him. The other option I have is to put all the
> data on the page and do a more traditional Javascript master / detail
> combos using arrays and such. But then, when the logic starts to get
> more complex, AJAX is a much better solution.
> >       Doing AJAX from the ground up involves a large number of poorly
> > documented, loosely coupled, moving parts, most of which have poor error
> > message and limited debugging capabilities. If you've never done it before,
> > expect to age a few years as you get over the hump.
> >
> I already created two component using Javascript/Ajax (taking ideas from
> Tacos's components, if you don't mind), that effectively submits or
> makes an Ajax request to a listener. It's just the parameters thing I
> haven't sort out.
>
> Now, I'm only asking for some pointers. Maybe you have better ideas or
> know the Ajax infrastructure better than I do (but don't underestimate
> me, hehe). There are 2 paths I could explore, and I'd appreciate some
> help on them:
>
> 1) Extending AjaxDirectService to allow for parameters. I have the
> feeling it should be fairly simple, and that I'm just not getting it.
> Does any of the Dojo's or Prototype interfaces allow for custom
> parameters on the request?
>
> 2) Submitting the form without page refreshing (kind of a background
> page submission). This isn't the best approach, IMO, because maybe I
> really don't want to change the server state. But, if it's easy, I can
> implement it as a workaround.
>
> In any case, I'd be glad to share any good implementations I can work
> out. I think this use case is fairly common.
>
> (and don't worry, I'm fairly comfortable with Javascript as long as I
> can get the 'big picture' of how Ajax/Tacos works).
>
> --
> Ing. Leonardo Quijano Vincenzi
> Director Técnico
> DTQ Software
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org
>
>

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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Ron Piterman <rp...@gmx.net>.
I have a java script variant - without ajax - so if the "tree" is 
limited and it is acceptable that all data will be rendered as 
javascript in the page you can use it...
drop me a mail, and I will send it to you...
Cheers,
Ron


ציטוט Leonardo Quijano Vincenzi:
> Patrick Casey wrote:
> 
>>     I don't think there's an oob master->child combo box implementation
>> out there for tapestry so you'd have to write your own. If you've never
>> written AJAX stuff before, and/or aren't really comfortable with 
>> javascript
>> and raw servlet processing, I'd honestly consider going another way (like
>> biting the bullet and submitting).
>>   
> 
> That's what I was doing. But then our client for this project has 
> already repeated several times that he doesn't like the 'page refresh', 
> and worse, I agree with him. The other option I have is to put all the 
> data on the page and do a more traditional Javascript master / detail 
> combos using arrays and such. But then, when the logic starts to get 
> more complex, AJAX is a much better solution.
> 
>>     Doing AJAX from the ground up involves a large number of poorly
>> documented, loosely coupled, moving parts, most of which have poor error
>> message and limited debugging capabilities. If you've never done it 
>> before,
>> expect to age a few years as you get over the hump.
>>   
> 
> I already created two component using Javascript/Ajax (taking ideas from 
> Tacos's components, if you don't mind), that effectively submits or 
> makes an Ajax request to a listener. It's just the parameters thing I 
> haven't sort out.
> 
> Now, I'm only asking for some pointers. Maybe you have better ideas or 
> know the Ajax infrastructure better than I do (but don't underestimate 
> me, hehe). There are 2 paths I could explore, and I'd appreciate some 
> help on them:
> 
> 1) Extending AjaxDirectService to allow for parameters. I have the 
> feeling it should be fairly simple, and that I'm just not getting it. 
> Does any of the Dojo's or Prototype interfaces allow for custom 
> parameters on the request?
> 
> 2) Submitting the form without page refreshing (kind of a background 
> page submission). This isn't the best approach, IMO, because maybe I 
> really don't want to change the server state. But, if it's easy, I can 
> implement it as a workaround.
> 
> In any case, I'd be glad to share any good implementations I can work 
> out. I think this use case is fairly common.
> 
> (and don't worry, I'm fairly comfortable with Javascript as long as I 
> can get the 'big picture' of how Ajax/Tacos works).
> 


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


Re: Creating a master / detail combobox pair (Ajax?)

Posted by Leonardo Quijano Vincenzi <le...@dtqsoftware.com>.
Patrick Casey wrote:
> 	I don't think there's an oob master->child combo box implementation
> out there for tapestry so you'd have to write your own. If you've never
> written AJAX stuff before, and/or aren't really comfortable with javascript
> and raw servlet processing, I'd honestly consider going another way (like
> biting the bullet and submitting).
>   
That's what I was doing. But then our client for this project has 
already repeated several times that he doesn't like the 'page refresh', 
and worse, I agree with him. The other option I have is to put all the 
data on the page and do a more traditional Javascript master / detail 
combos using arrays and such. But then, when the logic starts to get 
more complex, AJAX is a much better solution.
> 	Doing AJAX from the ground up involves a large number of poorly
> documented, loosely coupled, moving parts, most of which have poor error
> message and limited debugging capabilities. If you've never done it before,
> expect to age a few years as you get over the hump.
>   
I already created two component using Javascript/Ajax (taking ideas from 
Tacos's components, if you don't mind), that effectively submits or 
makes an Ajax request to a listener. It's just the parameters thing I 
haven't sort out.

Now, I'm only asking for some pointers. Maybe you have better ideas or 
know the Ajax infrastructure better than I do (but don't underestimate 
me, hehe). There are 2 paths I could explore, and I'd appreciate some 
help on them:

1) Extending AjaxDirectService to allow for parameters. I have the 
feeling it should be fairly simple, and that I'm just not getting it. 
Does any of the Dojo's or Prototype interfaces allow for custom 
parameters on the request?

2) Submitting the form without page refreshing (kind of a background 
page submission). This isn't the best approach, IMO, because maybe I 
really don't want to change the server state. But, if it's easy, I can 
implement it as a workaround.

In any case, I'd be glad to share any good implementations I can work 
out. I think this use case is fairly common.

(and don't worry, I'm fairly comfortable with Javascript as long as I 
can get the 'big picture' of how Ajax/Tacos works).

-- 
Ing. Leonardo Quijano Vincenzi
Director Técnico
DTQ Software



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


RE: Creating a master / detail combobox pair (Ajax?)

Posted by Patrick Casey <pa...@adelphia.net>.
	I don't think there's an oob master->child combo box implementation
out there for tapestry so you'd have to write your own. If you've never
written AJAX stuff before, and/or aren't really comfortable with javascript
and raw servlet processing, I'd honestly consider going another way (like
biting the bullet and submitting).

	Doing AJAX from the ground up involves a large number of poorly
documented, loosely coupled, moving parts, most of which have poor error
message and limited debugging capabilities. If you've never done it before,
expect to age a few years as you get over the hump.

	--- Pat

> -----Original Message-----
> From: Leonardo Quijano Vincenzi [mailto:leonardo@dtqsoftware.com]
> Sent: Wednesday, November 09, 2005 1:47 PM
> To: Tapestry users
> Subject: Creating a master / detail combobox pair (Ajax?)
> 
> Just as the title says, I'm trying to implement a re-usable way of
> binding two SELECTs together in a master / detail relationship.
> Basically what I need is to use one select (for example "Country") and
> bind a listener to the 'onchange' event. That'd listener would retrieve
> values for the associated detail combo (like "Province") and update the
> HTML.
> 
> Now, the tricky part is that I want to do this without form submissions.
> I'd rather use a parametrized listener, like this:
> 
> public void countryChanged(IRequestCycle cycle, Long provinceId) {
>    ...
> }
> 
> I guess I can also use a Ajax submit too, and update the combo box
> accordingly. But I definitively want to avoid page refreshing (that's
> why I'm using Ajax). What's the best way to accomplish this?? I've
> successfully associated an onchange() event to a listener without
> parameters (my preferred approach is to generate a javascript function
> for each listener, instead of using the @Refresh component), but I can't
> seem to find a way to do it in a parametrized way.
> 
> Any pointers?
> 
> --
> Ing. Leonardo Quijano Vincenzi
> Director Técnico
> DTQ Software
> 
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tapestry-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tapestry-user-help@jakarta.apache.org




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