You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by George Christman <gc...@cardaddy.com> on 2011/10/28 18:25:50 UTC

Submit inside loop

Hello, I'm using the submit component inside of a loop in order to
dynamically generate different buttons / Actions. 

I'm using it as followed 

<t:Submit t:context="workflow.action.id" defer="false"/>

public Object onSelected(Integer action) {
}

I seem to be having a problem submitting the form data, everything appears
to be null.  Does anybody know what I might be doing wrong here?

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4946513.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
Hi Josh, it indeed has something to do with defer being false. When it's
false it runs through the methods in the following order. 

onPrepare
onSelect
onValidate

When I set it to true, it seemed to work perfectly. Shouldn't I be setting
defer to false when looping submit buttons with context?

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4946658.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
I figured out a way to handle the defer without having to defer the form. 
Seems to work perfectly.


<t:form>
   <loop id="actionBtns">
      <submit value="save"  context="id" defer="false"/>
      <submit value="submit"  context="id" defer="false"/>
   </loop>
<t:form>

private Integer action;

void onPrepare() {
}

void onValidate() {
}

void onSuccess() {
}

void onSelected(Integer action) {
this.action = action;
}

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4955823.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
Josh, I should probable refrain the question, when using defer inside of
onSelected, could I get it to run after onValidate runs?

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4955694.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by Josh Canfield <jo...@gmail.com>.
onValidate is fired by your form after all of fields have been
processed, your fields also trigger an validate event. You may want to
be more specific about which field/form your validate method is
intended to service.

Here is a test:

<t:form t:id="form">
    <t:loop source="1..3" index="index">
        <t:submit t:id="top" value="ok ${index}" defer="false"/>
    </t:loop>
    <t:textfield t:id="text1"/>
    <t:textfield t:id="text2"/>
    <t:textfield t:id="text3"/>
    <t:loop source="1..3" index="index">
        <t:submit t:id="ok" value="ok ${index}" defer="false"/>
    </t:loop>
</t:form>

    @Property
    private String text1;    @Property    private String text2;
@Property    private String text3;
    @Property    private int index;
    @Inject    private Logger log;

    @Inject
    private Environment environment;

    void onPrepare() {
        log.info("onPrepare()");
    }

    void onValidate() {
        log.info("onValidate()");
    }

    void onValidate(String value) {
        log.info("onValidate('{}')", value);
    }

    void onValidateFromText1(String value) {
        log.info("onValidateFromText1('{}')", value);
    }

    void onSelected() {
        log.info("onSelected()");
        FormSupport formSupport = environment.peek(FormSupport.class);
        formSupport.defer(new Runnable() {

            public void run() {
                log.info("onSelected() deferred");
            }
        });
    }

I typed a,b,c into the form fields, here is the output:

[INFO] pages.SubmitTests onPrepare()
[INFO] pages.SubmitTests onSelected()
[INFO] pages.SubmitTests onValidate('a')
[INFO] pages.SubmitTests onValidate()
[INFO] pages.SubmitTests onValidateFromText1('a')
[INFO] pages.SubmitTests onValidate('b')
[INFO] pages.SubmitTests onValidate()
[INFO] pages.SubmitTests onValidate('c')
[INFO] pages.SubmitTests onValidate()
[INFO] pages.SubmitTests onSelected() deferred
[INFO] pages.SubmitTests onValidate()


You can see that the onValidate is called for each field, in addition
to the single parameter onValidate, and the targeted
"onValidateFromText1".

Writing this example makes me think there should be a way to get the
component that is actually triggering an event. I don't think that's
currently possible...

Josh

On Tue, Nov 1, 2011 at 8:33 AM, George Christman
<gc...@cardaddy.com> wrote:
> Thanks Josh, I got it working. I do have one question though, I notice it
> runs things in the following order.
>
> onSelected
> onValidate
> formSupport.defer run code
> back to onValidate
> and back to onSelected
>
> Should I somehow be preventing it from rerunning the onValidate onSelected a
> second time?
>
> Thanks,
> George
>
> --
> View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4955519.html
> Sent from the Tapestry - User mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> 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: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
Thanks Josh, I got it working. I do have one question though, I notice it
runs things in the following order.

onSelected
onValidate
formSupport.defer run code
back to onValidate
and back to onSelected

Should I somehow be preventing it from rerunning the onValidate onSelected a
second time?

Thanks, 
George

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4955519.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by Josh Canfield <jo...@gmail.com>.
> Unless you know of a way to use context without defer, I could always use
> css positioning to handle this.

Use defer=false so that you can capture the context, then use the same
strategy that the submit component does and defer your processing code
until after the rest of the form has been processed.
Something like this never-compiled thus untested code:

@Inject
private FormSupport formSupport;

private Integer action;

// assuming <t:submit t:id="actionButton"/>
void onSelectedFromActionButton(Integer action) {
  this.action = action;

  formSupport.defer(new Runnable() {
            public void run(){ /* do stuff with form fields */}
      }
  );
}

Josh


On Mon, Oct 31, 2011 at 8:43 AM, George Christman
<gc...@cardaddy.com> wrote:
> Josh, thanks for taking the time to explain how the submit component works,
> makes a lot of sense now. I'm under the assumption when ever you use the
> submit component in a loop with context you'll need to use defer false. The
> loop will then need to be written at the end of the forum. Without using
> defer false, all submit buttons will get the last context id of the loop.
> This sound about right?
>
> The current form layout is as followed.
>
> <form>
>   <loop id="actionBtns">
>      <submit value="save"  context="id" defer="false"/>
>      <submit value="submit"  context="id" defer="false"/>
>   </loop>
>
>   form data goes here
>
>   <loop2 id="actionBtnsCopy">
>      <submit value="save"  context="id" defer="false"/>
>      <submit value="submit"  context="id" defer="false"/>
>   </loop2>
> </form>
>
> Unless you know of a way to use context without defer, I could always use
> css positioning to handle this.
>
> --
> View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4952731.html
> Sent from the Tapestry - User mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> 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: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
Josh, thanks for taking the time to explain how the submit component works,
makes a lot of sense now. I'm under the assumption when ever you use the
submit component in a loop with context you'll need to use defer false. The
loop will then need to be written at the end of the forum. Without using
defer false, all submit buttons will get the last context id of the loop.
This sound about right?

The current form layout is as followed. 

<form>
   <loop id="actionBtns">
      <submit value="save"  context="id" defer="false"/>
      <submit value="submit"  context="id" defer="false"/>
   </loop>

   form data goes here

   <loop2 id="actionBtnsCopy">
      <submit value="save"  context="id" defer="false"/>
      <submit value="submit"  context="id" defer="false"/>
   </loop2>
</form>

Unless you know of a way to use context without defer, I could always use
css positioning to handle this. 

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4952731.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by Josh Canfield <jo...@gmail.com>.
:) It's all about understanding the component model and how form
components interact during a post. I'd recommend reading the source of
the submit/textfield/form components it's not that long and could
provide some light as to what's going on. Using the debugger and
walking through is also very illuminating.

I've been through that process so let me lay out some of what I've learned here:

If you look in the submit code you'll see this in the beginRender method:
        formSupport.store(this, new ProcessSubmission(clientId, name));

This actually sticks a representation in the t:formdata to be
processed during a submit. These are put in to formdata in document
order, and processed in document order. All of the form fields do the
same thing, they stick an object in t:formdata that is processed when
the form is submitted.

If you put a submit button at the top of the form then it's processed
first, if you put it at the bottom of a form it's processed last. This
is where defer comes in. If you want your selected event to come after
all the rest of the form has been processed you need to set defer to
true.

During process submission:

        if (defer)
            formSupport.defer(sendNotification);
        else
            heartbeat.defer(sendNotification);

The heartbeat in this case is owned by the loop and is executed at the
end of each iteration in the loop (also stored in the t:formdata). If
you're not in a loop this isn't true, the heartbeat is actually
executed right after the stored actions are executed, and the deferred
action right after that:

            resources.triggerContextEvent(EventConstants.PREPARE,
context, eventCallback);

            if (eventCallback.isAborted())
                return true;

            executeStoredActions();

            heartbeat.end();

            formSupport.executeDeferred();


so defer really has no effect.

Hopefully that's helpful.

Josh

On Fri, Oct 28, 2011 at 6:01 PM, George Christman
<gc...@cardaddy.com> wrote:
> Josh, I did some more experimenting and come up with the following outcome.
>
> I have two sets of buttons on the page, one set at the top of the page and
> once set at the bottom of the page. They are both identical other than the
> generated id's created by tapestry. The first set of buttons at the top of
> the page does not call the tapestry methods in the proper sequence. The
> second set at the bottom of the page seem to work perfectly fine. Is this
> possible a bug or something I'm doing wrong?
>
> Script generated
>
> Tapestry.init({
> 4 "linkSubmit" : [
> 5 {
> 6 "form" : "PR",
> 7 "validate" : true,
> 8 "clientId" : "linksubmit"
> 9 },
> 10 {
> 11 "form" : "PR",
> 12 "validate" : true,
> 13 "clientId" : "linksubmit_0"
> 14 },
> 15 {
> 16 "form" : "PR",
> 17 "validate" : true,
> 18 "clientId" : "linksubmit_1"
> 19 },
> 20 {
> 21 "form" : "PR",
> 22 "validate" : true,
> 23 "clientId" : "linksubmit_2"
> 24 }
> 25 ],
> 26 "formEventManager" : [
> 27 {
> 28 "formId" : "PR",
> 29 "validate" : {
> 30 "submit" : false,
> 31 "blur" : false
> 32 }
> 33 }
> 34 ]
>
> --
> View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4946808.html
> Sent from the Tapestry - User mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> 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: Submit inside loop

Posted by George Christman <gc...@cardaddy.com>.
Josh, I did some more experimenting and come up with the following outcome. 

I have two sets of buttons on the page, one set at the top of the page and
once set at the bottom of the page. They are both identical other than the
generated id's created by tapestry. The first set of buttons at the top of
the page does not call the tapestry methods in the proper sequence. The
second set at the bottom of the page seem to work perfectly fine. Is this
possible a bug or something I'm doing wrong?

Script generated

Tapestry.init({
4 "linkSubmit" : [
5 {
6 "form" : "PR",
7 "validate" : true,
8 "clientId" : "linksubmit"
9 },
10 {
11 "form" : "PR",
12 "validate" : true,
13 "clientId" : "linksubmit_0"
14 },
15 {
16 "form" : "PR",
17 "validate" : true,
18 "clientId" : "linksubmit_1"
19 },
20 {
21 "form" : "PR",
22 "validate" : true,
23 "clientId" : "linksubmit_2"
24 }
25 ],
26 "formEventManager" : [
27 {
28 "formId" : "PR",
29 "validate" : {
30 "submit" : false,
31 "blur" : false
32 }
33 }
34 ] 

--
View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4946808.html
Sent from the Tapestry - User mailing list archive at Nabble.com.

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


Re: Submit inside loop

Posted by Josh Canfield <jo...@gmail.com>.
When are you seeing the values as null? I can't see any reason that
what you are doing shouldn't work, unless you're trying to access the
values from the onSelected method with defer = false before the fields
have been processed.

You could set a breakpoint on the setter for your form properties and
see when they are getting called.

On Fri, Oct 28, 2011 at 4:25 PM, George Christman
<gc...@cardaddy.com> wrote:
> Hello, I'm using the submit component inside of a loop in order to
> dynamically generate different buttons / Actions.
>
> I'm using it as followed
>
> <t:Submit t:context="workflow.action.id" defer="false"/>
>
> public Object onSelected(Integer action) {
> }
>
> I seem to be having a problem submitting the form data, everything appears
> to be null.  Does anybody know what I might be doing wrong here?
>
> --
> View this message in context: http://tapestry.1045711.n5.nabble.com/Submit-inside-loop-tp4946513p4946513.html
> Sent from the Tapestry - User mailing list archive at Nabble.com.
>
> ---------------------------------------------------------------------
> 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