1 2 Previous Next 17 Replies Latest reply on Dec 24, 2007 7:22 AM by pmuir

    Two browser tabs : basic question

    dkane

      My dataTable is built on DataModel and DataModelSelection.

      One colunm contains "delete record" link that takes injected DataModelSelection item and removes it from both List and database. After that view is being rerendered.
      Component is session-scoped.

      Let's say table contains records A,B,C,D .
      I open table in two browser tabs (tab1, tab2). Both displays A,B,C,D .
      I delete record C in tab1. Not it shows A,B,D .
      tab2 still shows A,B,C,D. I click "delete row" on C in tab2. After that it shows A,B.
      Looks like DataModelSelection only sends row number but not primary key, etc. Row number is 3 in both actions, but after first delete from tab1 3rd row is already D, not C.

      What I want in multi-tab interface : intercepting the attempt to delete row that does not exist , and saying "Record C is already deleted" .
      I beleive this is basic and well-discussed question. Please let me know the best practices.

      Thank you.

        • 1. Re: Two browser tabs : basic question
          damianharvey

          I tried to recreate your issue but if I attempt this, Hibernate throws a

          org.hibernate.StaleStateException : Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

          I'm glad you raised this though as I'll make sure I handle this error. Not sure why it's not generating this for you though? How do you do your delete? Something like this?
          myList.remove(selectedObject);
          entityManager.remove(selectedObject)


          Cheers,

          Damian.

          • 2. Re: Two browser tabs : basic question
            dkane

             

            "damianharvey" wrote:
            How do you do your delete? Something like this?
            myList.remove(selectedObject);
            entityManager.remove(selectedObject)



            Not quite. First operator myList.remove(selectedObject) is the same.
            What comes to second : my list of records is actually a collection of entity beans referred by @OneToMany annotation in another entity bean (with CascadeType.PERSIST).
            So in order to remove entity from database, I remove it from collection and Hibernate takes care of underlying deletion.

            I don't consider that as a key difference though.

            • 3. Re: Two browser tabs : basic question
              dkane

              No one have "delete" controls in data tables :) ?

              • 4. Re: Two browser tabs : basic question
                pmuir

                This is a problem with using the @DataModelSelection approach. If you used page parameters you wouldn't have this problem as you would be deleting rows by id rather than position in a list. So I would use page parameters ;)

                • 5. Re: Two browser tabs : basic question
                  damianharvey

                  Oh dear. So all my hard work removing IDs from my application is wasted? Exposing IDs in my app is a bit of an issue.

                  Do you know if it is just @DataModelSelection that is the problem or @DataModel in general? Ie. if I use this:

                  <s:link action="#{myBean.delete(myObject)}"...
                  will I still face this issue? (I assume so).

                  Cheers,

                  Damian.

                  • 6. Re: Two browser tabs : basic question
                    dkane

                     

                    "pete.muir@jboss.org" wrote:
                    If you used page parameters you wouldn't have this problem

                    Thank you!
                    Does any Seam examples project illustrate this recommended approach ?

                    • 7. Re: Two browser tabs : basic question
                      pmuir

                       

                      "damianharvey" wrote:
                      Oh dear. So all my hard work removing IDs from my application is wasted? Exposing IDs in my app is a bit of an issue.


                      Sure, there are some apps where page parameters aren't appropriate.

                      We need to dig into exactly what works and doesn't with passing parameters to EL action methods inside iterative components

                      http://jira.jboss.com/jira/browse/JBSEAM-2391

                      If you were able to use some synthetic id instead of the entities primary key would that help? Like the way the entity converter uses a numeric sequence - i.e. is this something which it would be good to have support in Seam for?

                      Does any Seam examples project illustrate this recommended approach ?


                      The seamdiscs example uses page parameters to select etc. rows - but not explicitly to delete.

                      • 8. Re: Two browser tabs : basic question
                        damianharvey

                        I originally removed the IDs as I didn't want the user hacking around trying to find someone else's data. It's the usual use-case I suppose and certainly something that I do whenever I see IDs in the query string. I can check the authorisation of the user in the find() or similar but the @DataModel approach means I don't have to bother with that.

                        Sounds like from http://jira.jboss.com/jira/browse/JBSEAM-1734 that you're looking at removing the @DataModel entirely?

                        JBSEAM-2391 just needs the DataTable populated from a list annotated with @DataModel or am I missing something?

                        Cheers,

                        Damian.

                        • 9. Re: Two browser tabs : basic question
                          pmuir

                          Not removing @DataModel, just removing the ability to pass objects inside of iterations using extended EL. The ability to pass an ID would still be there of course, but that doesn't meet your obfuscation requirements (which I agree with on a conceptual level). However, there is both Gavin's conceptual point, and the problem that we get more questions on the forum about why this doesn't work than any other. No conclusion has been reached for now.

                          JBSEAM-2391 - we need to document clearly what works and doesn't as everyone is just confused atm.

                          • 10. Re: Two browser tabs : basic question
                            dkane

                             

                            "pete.muir@jboss.org" wrote:
                            If you used page parameters you wouldn't have this problem as you would be deleting rows by id rather than position in a list. So I would use page parameters ;)


                            I have solved problem partially with page parameters, but it does not work for ajax request (used exactly for "delete" operation in table).

                            I define page parameter in pages.xml and obtain it via @RequestParameter in code. For normal request it works, for ajax request parameter is null.

                            Any idea ?

                            • 11. Re: Two browser tabs : basic question
                              pmuir

                              You don't need to use @RequestParamater with page parameters. Take a look at seamdiscs to see how they should be used.

                              • 12. Re: Two browser tabs : basic question
                                dkane

                                 

                                "pete.muir@jboss.org" wrote:
                                You don't need to use @RequestParamater with page parameters. Take a look at seamdiscs to see how they should be used.


                                I see no "param" tags in pages.xml of seamDISKS example (Seam 2.0) .

                                In seamSPACE , I see the following :

                                pages.xml :

                                <page view-id="/comment.xhtml" login-required="true">
                                 <restrict/>
                                
                                 <param name="name" value="#{selectedMember.memberName}"/>
                                 <param name="blogId" value="#{selectedBlog.blogId}"/>
                                
                                 <navigation from-action="#{blog.saveComment}">
                                 <redirect view-id="/blogentry.xhtml"/>
                                 </navigation>
                                </page>



                                The target page, blogentry.xhtml , uses BlogAction component.
                                In BlogAction.java :

                                @RequestParameter
                                private Integer blogId;
                                ......
                                
                                @Factory("selectedBlog")
                                @Begin(join=true)
                                public void getBlog()
                                {
                                 try
                                 {
                                 selectedBlog = (MemberBlog) entityManager.createQuery(
                                 "from MemberBlog b where b.blogId = :blogId and b.member.memberName = :memberName")
                                 .setParameter("blogId",blogId)
                                 .setParameter("memberName", name)
                                 .getSingleResult();
                                 }
                                ....
                                }
                                


                                I.e. blogId is passed as @RequestParameter and then used to retreive current blog. Exactly the same approach I am trying to implement. Anything wrong ?





                                • 13. Re: Two browser tabs : basic question
                                  damianharvey

                                  I don't think that there's anything *wrong* with @RequestParameter, but it isn't optional so your Bean containing it must always be passed it.

                                  Using just page params is just as easy. You would do something like this:
                                  test.xhtml:

                                  <a4j:outputPanel id="testPanel">
                                   <h:dataTable
                                   id="testTable"
                                   var="row"
                                   value="#{testBean.widgets}">
                                   <h:column>
                                   #{row.id}
                                   </h:column>
                                   <h:column>
                                   <s:link
                                   action="#{testBean.deleteWidget}">
                                   Normal Delete
                                   <f:param name="id" value="#{row.id}"/>
                                   </s:link>
                                   <a4j:commandLink
                                   action="#{testBean.deleteWidget}"
                                   ajaxSingle="true"
                                   reRender="testPanel">
                                   AJAX Delete
                                   <f:param name="id" value="#{row.id}"/>
                                   </a4j:commandLink>
                                   </h:column>
                                   </h:dataTable>
                                  </a4j:outputPanel>
                                  

                                  test.page.xml:
                                  <param name="id" value="#{testBean.id}"/>
                                  

                                  TestBean.java
                                  @Name("testBean")
                                  public class TestBean {
                                   private long id;
                                   private List<Widget> widgets = new ArrayList<Widget>
                                  
                                   public long getId() {
                                   return this.id;
                                   }
                                  
                                   public void setId(long id) {
                                   this.id = id;
                                   }
                                  
                                   public void deleteWidget() {
                                   Widget widget = entityManager.find(Widget.class, this.id);
                                   entityManager.remove(widget);
                                   widgets.remove(widget);
                                   }
                                  
                                   public List<Widget> getWidgets() {
                                   if(this.widgets == null || this.widgets.size == 0) {
                                   loadWidgets();
                                   }
                                   return this.widgets;
                                   }
                                   private void loadWidgets() {
                                   this.widgets = entityManager.createQuery("select w from Widget w").getResultList();
                                   }
                                  

                                  Hope this helps.

                                  Cheers,

                                  Damian.



                                  • 14. Re: Two browser tabs : basic question
                                    dkane

                                    damianharvey,

                                    thank you for the example.

                                    This radically differs from the model I have had in mind about page parameters. I thought that tag

                                    <param name="id" value="#{testBean.id}"/>

                                    takes value FROM #{testBean.id} and makes it available via @RequestParam.

                                    In your example, value is being taken from row.id using f:param (that is understandable) , and then it is being ASSIGNED TO #{testBean.id}.

                                    I thought "param" in pages.xml exposes value to context for further usage, i.e. "value=" attribute is getter, not setter.

                                    How can it work in both directions ? At least it works in the way I expected..

                                    1 2 Previous Next