Well, one of the points about our app, is that is maintains state and there
is always a log in to access it. So this use case makes it very different to
what you are doing as well. There is no question that one user would view
and edit the same data without using hibernate's persistance mechanisms to
roll back if necessary.
I'm not sure where stale links might come into that picture. We must have
tackled that somewhere.
The following is the sort of thing that you are looking at, serverside. It
is in its own component, not accomplished just with For.
The user has additional information to add and they are served an additional
text area into which the enter it.
This doesn't show a user button, but it shows how we ensure the text area is
rendered.

public abstract class FurtherInformation extends BaseComponent {

    //public abstract Set getNotes();

    public abstract OccupationalPensionSchemeReturn
getOccupationalPensionSchemeReturn();

    public abstract Note getNote();

    public abstract void setNote(Note note);


    /*
     * (non-Javadoc)
     *
     * @see org.apache.tapestry.AbstractComponent#prepareForRender(
org.apache.tapestry.IRequestCycle)
     */
    protected void prepareForRender(IRequestCycle cycle) {

        super.prepareForRender(cycle);

        Set noteSet = getOurObjectModelName().getAdditionalNotes();
        if (noteSet.isEmpty()) {
            noteSet.add(new Note());
        }
        setNote((Note) noteSet.iterator().next());
    }

}

<component-specification class="FurtherInformation"
    allow-body="yes"
    allow-informal-parameters="yes">

    <description>Further Information question </description>


     <!-- oReturn object -->
    <parameter name="oReturn" type="oReturn" direction="in" required="yes"/>

    <property-specification name="note" type="Note"/>

     <bean name="StringValidator" class="esValidator" lifecycle="page"/>

     <component id="question##3" type="QuestionBlock">
        <static-binding name="headingNumber">##3</static-binding>
        <static-binding name="headingTitle">Further
information</static-binding>
    </component>

    <component id="question##31" type="Question">
        <static-binding name="questionNumber">31</static-binding>
        <static-binding name="questionTitle">Please use this space to give
any further information you wish to tell us.</static-binding>
    </component>

 <!--  <component id="furtherInfoTextarea"
type="FurtherInformationTextarea">
        <binding name="notes" expression="Notes" />
    </component>-->

   <component id="furtherInfoTextarea" type="tTextArea">
       <static-binding name="displayName">Further
information</static-binding>
       <binding name="value" expression="note.note.value"/>
       <binding name="error" expression="note.note.error"/>
       <binding name="anotherErrorType" expression="
note.note.anotherErrorType"/>
       <binding name="validator" expression="beans.StringValidator"/>
       <binding name="rows" expression="10"/>
       <binding name="cols" expression="75"/>
       <binding name="maxchars" expression="4000"/>
    </component>





> </component-specification>
>

There is alos this which is closer, serverside, to your scenario of a suer
selected component.

public abstract class CompanyForm extends ReadOnlyComponent {
>
>     public abstract OurObjectModelName getOurObjectModelName();
>
>     public abstract void setListEditMap(ListEditMap list);
>
>     public abstract ListEditMap getListEditMap();
>
>     public abstract int getMyIndex();
>
>     public abstract List getCompanyList();
>
>     public abstract void setCompanyList(List list);
>
>     // The selected company index for deletion.
>     public abstract int getDeleteOccurrence();
>
>     public void addCompany(IRequestCycle cycle) {
>         Company company = createCompany();
>         getCompanyList().add(company);
>     }
>
>     public Company getCompany() {
>         Company company = null;
>         List companies = getCompanyList();
>         if (getMyIndex() <= companies.size() - 1) {
>             company = (InsuranceCompany) companies.get(getMyIndex());
>         } else {
>             company = createCompany();
>             companies.add(company);
>         }
>         return company;
>     }
>
>     public void setCompany(Company company) {
>         if (getCompanyList().size() - 1 < getMyIndex()) {
>             getCompanyList().add(getMyIndex(), company);
>         } else {
>             getCompanyList().set(getMyIndex(), company);
>         }
>     }
>
>     protected void renderComponent(IMarkupWriter iMarkupWriter,
> IRequestCycle cycle) {
>
>         List companyList = getCompanyList();
>         if (cycle.isRewinding()) {
>             // clear companyList to receive new ones from the submitted
> form
>             getCompanyList().clear();
>         } else {
>             // Rendering mode
>             if (getCompanySet().isEmpty()) {
>                 getCompanySet().add(createCompany());
>             }
>
>             if (companyList.isEmpty()) {
>                 setUpCompanyList();
>             }
>         }
>
>         // Render : use listEdit to display each company form in
>         //          companyList.
>         // rewind : use listEdit to retreive each of the submitted
>         //             company form.
>         // The values in the map could be anything. They are not used.
>         ListEditMap map = new ListEditMap();
>         for (int i = 0; i < companyList.size(); i++) {
>             map.add(new Integer(i), "");
>         }
>
>         setListEditMap(map);
>
>         super.renderComponent(iMarkupWriter, cycle);
>
>         if (cycle.isRewinding()) {
>
>             if (getDeleteOccurrence() >= 0) {
>                 // A delete insurance button has been pressed.
>                 deleteInsuranceCompany();
>             }
>
>             // update the insurance company set with the insurance company
> form
>             // collected from the submitted form.
>             updateCompanySet();
>         }
>
>     }
>
>     /**
>      *  Remove an insurance company from the companyList and insurance
> company
>      *  set.
>      *
>      */
>     private void deleteInsuranceCompany() {
>         getCompanyList().remove(getDeleteOccurrence());
>         updateCompanySet();
>     }
>
>     private Set getCompanySet() {
>         Set companies = getOurObjectModelName().getDetails().getAT()
>                 .getCompanies();
>         return companies;
>     }
>
>     /**
>      *
>      * Clear insurance company set and populate it with elements fron
> companyList.
>      */
>     private void updateCompanySet() {
>         Set companySet = getCompanySet();
>         companySet.clear();
>         Iterator it = getCompanyList().iterator();
>         while (it.hasNext()) {
>             companySet.add(it.next());
>         }
>
>     }
>
>     private Company createCompany() {
>         Company company = new Company();
>         return company;
>     }
>
>     /**
>      * Populate  companyList with elements from
>      * insurance company set in occupationalPensionSchemeReturn.
>      */
>     private void setUpCompanyList() {
>
>         List companyList = getCompanyList();
>         companyList.clear();
>         Iterator it = getCompanySet().iterator();
>         while (it.hasNext()) {
>             companyList.add(it.next());
>         }
>
>     }
>
>     public int getCount() {
>         return getMyIndex() + 1;
>     }
>
>     public String getQuestionNo() {
>         return (getMyIndex() == 0 ? "12" : "B");
>     }
>
>     public String getQuestionP1No() {
>         return (getMyIndex() == 0 ? "12.1" : "B1");
>     }
>
>     public String getQuestionP2No() {
>         return (getMyIndex() == 0 ? "12.2" : "B2");
>     }
>
>     public String getQuestionP3No() {
>         return (getMyIndex() == 0 ? "12.3" : "B3");
>     }
>
>     public String getQuestionP4No() {
>         return (getMyIndex() == 0 ? "12.4" : "B4");
>     }
>
>     public boolean isShowDeleteButton() {
>         return (getMyIndex() == 0 ? false : true);
>     }
>
>     public String getHeadingTitle() {
>         String heading = "";
>         if (getMyIndex() == 0) {
>             heading = "Company's details";
>         } else {
>             heading = "Annex B: Company form (" + (getCount() - 1) + " of
> " + (getCompanyList().size() - 1)
>                     + ") " + " You must complete one of these company
> forms for every company.";
>         }
>         return heading;
>     }
>
>     public boolean isFirstOccurrence() {
>         return (getMyIndex() == 0 ? true : false);
>     }
> }
>

Note how the control works in the last few methods. These are all concerned
with the view, how the form is rendered.
For instance getQuestionP4No() is used below in the component spec and
corresponding HTML, which I don't show (its just a table with an jwcid)
<component id="questionTwelveFour" type="Question">
         <binding name="questionNumber"  expression="questionP4No"/>
        <static-binding name="questionTitle">Email address for contact at
firm</static-binding>
</component>

I think that the point is this shows how similar the techniques in Tapestry
are, even in a more complicated scenario and with sessional requirements.
HTH,
Adam

On 08/12/05, karthik G <[EMAIL PROTECTED]> wrote:
>
> Ok ,
>
> I have handled the problem I was facing by figuring which button was
> clicked
> [Save] OR [Add Rows] and by tweaking here and there.
> I clean up the stale entries from the list when somebody (USER1) does a
> [Save]. Now even though USER2 might be adding new ones, tapestry anyways
> creates the new objects while deserializing , and i add it to the List if
> an
> entry is not found.
> I'm also synchronizing the deserialized list with the actual. and yes I'm
> using the For component.
>
> Now I have read about the stale link issue somewhere. I have never tried
> tapestry 3. Can we safely conclude that it will *never* occur when using
> For
> component in tapestry4? simply because For was created to address exactly
> that - it has its own copy of the list and hence does not rely on the
> underlying model being in sync when rewinding. Can someone please confirm
> this.
>
> thanks!
> karthik
>
>
> On 12/8/05, karthik G <[EMAIL PROTECTED]> wrote:
> >
> > Thanks, it will be great if you could suggest something really cool when
> > you find time.
> >
> > Meanwhile, I did try your suggestion (adding blank rows to the *model*,
> i
> > mean the list ) and it works like a charm. But I'm not sure if I'm doing
> it
> > the right way. If the user changes his mind later and does not commit,
> the
> > list is ending up with dummy entries that I dont want.
> > OR
> > when i open another browser and access the page, its displaying 3
> > additional rows now (the ones that i added earlier) as expected. User1
> has
> > not commited yet so User2 is seeing 3 blank rows even though User2
> did'nt
> > press [Add Rows] button.
> > So the model needs to be cleaned up in case the user just closes the
> page/
> > simply forgets to do anything. Could you please tell me a good way to
> handle
> > this.
> >
> > thanks
> > karthik
> >
> >
> > On 12/8/05, adasal <[EMAIL PROTECTED]> wrote:
> > >
> > > OK. I can't give you anything definitive.
> > > I will rumage our code base and give you an example in a while, but,
> so
> > > far
> > > as I know, we have done all of this server side.
> > > It is an interesting question, because we have a button that reveals
> > > additional fields in some circumstances and is a submit request in
> other
> > > circumstances, depends on the server.
> > > We have migrated from T3 to T4 (em, perhaps I should do a synch before
> I
> > > rumage at all:)) and in T3 we had the problem of ids available to use
> as
> > > handles in js. This has been resolved in T4, though I doubt we have
> > > migrated
> > > our scripts to reflect this.
> > > So my understanding is that you will get some very complex js if you
> > > attempt
> > > this from the client, but others should comment. Afterall there is
> much
> > > thought about AJAX and that is also about making such things more
> > > encapsulated and immiediate?
> > > In our case we had a couple of other reasons (aside from js
> complexity)
> > > not
> > > to use the client.
> > > 1). Legal. We couldn't be in the position where the client could
> > > (maliciously or inadvertantly) manipulate the code.
> > > 2). We are working up from our persistance model and it makes sense to
> > > create additional objects server side which we have a persistance
> > > mechanism
> > > for. As I see it, if you have a restricted number of types, e.g. you
> are
> > >
> > > just adding rows to a table represented by one type, client side might
> > > be
> > > OK, but if, as in our case, there is a wide variety of types, any one
> of
> > > which might have additional elements created and stored, it makes more
> > > sense
> > > to control all of this server side.
> > > 3). Our comprehensive error checking mechanism used js, the Tapestry
> > > framework and our own additions. It might be an error to create extra
> > > elements in some circumstances in our implementation, and this might
> > > only be
> > > available for checking in the business logic server side. So, if none
> of
> > > the
> > > other reasons were enough not to use client script this one definitely
> > > is :)
> > > I will go fishing for an example when I get a chance later today.
> > > Adam
> > >
> > > On 07/12/05, karthik G <[EMAIL PROTECTED]> wrote:
> > > >
> > > > Hi Adam,
> > > >
> > > > So do you want me to submit the form when user clicks [Add Rows] ? I
> > > can
> > > > try
> > > > that. But right now am adding new rows to the table (of users) on
> the
> > > > client
> > > > side using javascript. Now I dont know how to get For component to
> > > > recongnize the existance of new users during the final submit. I can
> > > see
> > > > tapestry generating hidden fields to store ids. But having that
> logic
> > > in
> > > > my
> > > > custom javascript method does'nt look neat.
> > > > Do you think i can stick to client side addition of new rows and
> still
> > >
> > > > handle things during final submit?.
> > > >
> > > > thanks
> > > > karthik
> > > >
> > > > On 12/6/05, adasal <[EMAIL PROTECTED]> wrote:
> > > > >
> > > > > In the listener have it check the status of Add Rows. If true have
> > > it
> > > > > create
> > > > > further elements in an iteration that are added into the ListMap
> as
> > > a
> > > > > series
> > > > > of empty strings.
> > > > > Adam
> > > > >
> > > > > On 06/12/05, karthik G < [EMAIL PROTECTED]> wrote:
> > > > > >
> > > > > > Hi All,
> > > > > >
> > > > > > I'm a tapestry beginner and I have a requirement similar to the
> > > > original
> > > > > > post.
> > > > > >
> > > > > > My requirement is almost similar too except that I plan to add
> > > blank
> > > > > rows
> > > > > > when user clicks the button [Add Rows]..say 5 rows by default.
> > > > Initially
> > > > > > get
> > > > > > all users and display them. Later allow user to add / edit.
> > > > > >
> > > > > >
> > > > > > (text)   (text Input) (PropertySelection)
> > > > > > UserId   UserName     Group
> > > > > >
> > > > > > xxx01    Peter        Admin
> > > > > > xxx02    Bob          Guess
> > > > > > xxx04    Mark         Beginner
> > > > > >
> > > > > > [Save] [Cancel] [Add Rows]
> > > > > >
> > > > > >
> > > > > >   (text)   (text Input) (PropertySelection)
> > > > > > UserId   UserName     Group
> > > > > >
> > > > > > xxx01    Peter        Admin
> > > > > > xxx02    Bob          Guess
> > > > > > xxx04    Mark         Beginner
> > > > > > --------     --------        --------
> > > > > > --------     --------        --------
> > > > > > --------     --------        --------
> > > > > > --------     --------        --------
> > > > > > --------     --------        --------
> > > > > > [Save] [Cancel] [Add Rows]
> > > > > >
> > > > > > Now if the use adds new entries, modifies some of the existing
> > > ones, i
> > > > > > need
> > > > > > to be able to add /update to the database on clicking [Save].
> How
> > > s' d
> > > > I
> > > > > > handle this?.
> > > > > >
> > > > > > Thanks ,
> > > > > > karthik
> > > > > >
> > > > > >
> > > > >
> > > >
> > > >
> > >
> >
> >
>
>

Reply via email to