You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@wicket.apache.org by Philip Wilkinson <wi...@gmail.com> on 2012/01/08 21:22:50 UTC

AjaxLink causes list model to be detached before rendering!

Hi,

Can anyone tell me why when I refresh a wicket page which has on it an
AjaxLink, my list model is being detached just

before its contents are rendered by a list view?

I would expect detach only to be called after everything has been rendered.

Detaching just before rendering would call a potentially 2nd
unnecessary expensive database operation to load the

model.

I have an Ajax‌Link call back which seems to be creating this
behaviour. Without it the models are detached afer

rendering as expected.

E.g. for the attached simple test page, pressing refresh in the
browser, when there is no AjaxLink I get the following

diagnostic output from Testpage.java...

LOAD LIST MODEL 79e14338
populateItem 79e14338
populateItem 79e14338
populateItem 79e14338
DETACH MODELS 79e14338
DETACH LIST MODEL 79e14338
DETACH MODELS 79e14338
DETACH LIST MODEL 79e14338

When I surround the label with an ajax link, pressing refresh in the
browser, I get..

LOAD LIST MODEL 62b512e7
DETACH LIST MODEL 62b512e7 //1st ????
DETACH LIST MODEL 62b512e7 //2nd ????
DETACH LIST MODEL 62b512e7 //3rd ????
populateItem 62b512e7 // render "Aaaaa"
populateItem 62b512e7 // render "Bbbbb"
populateItem 62b512e7 // render "Ccccc"
LOAD LIST MODEL 62b512e7
DETACH MODELS 62b512e7
DETACH LIST MODEL 62b512e7 //4th
DETACH LIST MODEL 62b512e7 //5th
DETACH LIST MODEL 62b512e7 //6th
DETACH LIST MODEL 62b512e7 //7th
DETACH MODELS 62b512e7
DETACH LIST MODEL 62b512e7 //8th
DETACH LIST MODEL 62b512e7 //9th
DETACH LIST MODEL 62b512e7 //10th
DETACH LIST MODEL 62b512e7 //11th
WRITE OBJECT 62b512e7

.. 11 calls to .detach() on the list model!
between the 3rd and 4th detach list model the list is actually
rendered. Had this been for real this would have ment

that the list model would have been loaded again at this point.
Here's the detail stack traces of where these 11 detach list models
are being called from when refresh on my test page with
the AjaxLink...

*** 1st DETACH LIST MODEL ***
TestPage$2.detach() line: 39
TestPage$3$1(Component).detachModel() line: 3536
TestPage$3$1(Component).detachModels() line: 1203
TestPage$3$1(Component).detach() line: 1154
ListItem<T>(MarkupContainer).detachChildren() line: 1710
ListItem<T>(Component).detach() line: 1161
TestPage$3(MarkupContainer).removeAll() line: 672
TestPage$3(ListView<T>).onPopulate() line: 498
TestPage$3(AbstractRepeater).onBeforeRender() line: 119
TestPage$3(Component).internalBeforeRender() line: 981
TestPage$3(Component).beforeRender() line: 1015
TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
TestPage(Component).onBeforeRender() line: 3774
TestPage(Page).onBeforeRender() line: 823
TestPage(Component).internalBeforeRender() line: 981
TestPage(Component).beforeRender() line: 1015
TestPage(Component).internalPrepareForRender(boolean) line: 2184
TestPage(Page).internalPrepareForRender(boolean) line: 280
TestPage(Component).render() line: 2271
TestPage(Page).renderPage() line: 1035
WebPageRenderer.renderPage(Url, RequestCycle) line: 105
WebPageRenderer.respond(RequestCycle) line: 182
RenderPageRequestHandler.respond(IRequestCycle) line: 167

*** 2nd DETACH LIST MODEL ***
Same as before.

*** 3rd DETACH LIST MODEL ***
Same as before.

*** Now populateItem is called 3 times on the list view to render the
3 list elements...

*** 4th DETACH LIST MODEL ***
TestPage$2.detach() line: 39
TestPage$3(Component).detachModel() line: 3536
TestPage$3(Component).detachModels() line: 1203
TestPage$3(Component).detach() line: 1154
TestPage(MarkupContainer).detachChildren() line: 1710
TestPage(Component).detach() line: 1161
PageProvider.detach() line: 318
RenderPageRequestHandler.detach(IRequestCycle) line: 148
RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
RequestCycle.onDetach() line: 565
RequestCycle.detach() line: 508
RequestCycle.processRequestAndDetach() line: 284
WicketFilter.processRequest(ServletRequest, ServletResponse,
FilterChain) line: 162

*** 5th DETACH LIST MODEL ***
TestPage$2.detach() line: 39
TestPage$3$1(Component).detachModel() line: 3536
TestPage$3$1(Component).detachModels() line: 1203
TestPage$3$1(Component).detach() line: 1154
ListItem<T>(MarkupContainer).detachChildren() line: 1710
ListItem<T>(Component).detach() line: 1161
TestPage$3(MarkupContainer).detachChildren() line: 1710
TestPage$3(Component).detach() line: 1161
TestPage(MarkupContainer).detachChildren() line: 1710
TestPage(Component).detach() line: 1161
PageProvider.detach() line: 318
RenderPageRequestHandler.detach(IRequestCycle) line: 148
RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
RequestCycle.onDetach() line: 565
RequestCycle.detach() line: 508
RequestCycle.processRequestAndDetach() line: 284
WicketFilter.processRequest(ServletRequest, ServletResponse,
FilterChain) line: 162

*** 6th DETACH LIST MODEL ***
Same as before

*** 7th DETACH LIST MODEL ***
Same as before

*** 8th DETACH LIST MODEL ***
TestPage$2.detach() line: 39
TestPage$3(Component).detachModel() line: 3536
TestPage$3(Component).detachModels() line: 1203
TestPage$3(Component).detach() line: 1154
TestPage(MarkupContainer).detachChildren() line: 1710
TestPage(Component).detach() line: 1161
PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
line: 156
PageStoreManager(AbstractPageManager).commitRequest() line: 94
PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
PageAccessSynchronizer$2.commitRequest() line: 281
Application$2.onDetach(RequestCycle) line: 1588
RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
RequestCycleListenerCollection$3.notify(Object) line: 97
ListenerCollection$1.notify(T) line: 119
RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
line: 143
RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
line: 113
RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
RequestCycle.onDetach() line: 569
RequestCycle.detach() line: 508
RequestCycle.processRequestAndDetach() line: 284
WicketFilter.processRequest(ServletRequest, ServletResponse,
FilterChain) line: 162

*** 9th DETACH LIST MODEL ***
Same as before

*** 10th DETACH LIST MODEL ***
Same as before

*** 11th DETACH LIST MODEL ***
Same as before

To show this behaviour I've created the following test page.
TestPage.java

public class TestPage extends WebPage
{
   private static List<String> testList = new ArrayList<String>() {{
add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};

   public TestPage()
   {
       final IModel<List<String>> model = new
LoadableDetachableModel<List<String>>()
               {
                   @Override
                   protected List<String> load()
                   {
                       System.out.println("LOAD LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                       return testList; // would normally get list
from db, in this case just retun static list.
                   }

                   @Override
                   public void detach()
                   {
                       System.out.println("DETACH LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                       super.detach(); // detach model. "heavyweight"
model no longer needed.
                   }
               };

       final ListView<String> rows = new ListView<String>("rows", model)
       {
           @Override
           protected void populateItem(ListItem<String> item)
           {
               System.out.println("populateItem " +
Integer.toHexString(TestPage.this.hashCode()));

               // with ajax link.
               AjaxLink<List<String>> link = new
AjaxLink<List<String>>("link", model)
                   {
                       @Override
                       public void onClick(AjaxRequestTarget target)
                       {
                           // Do nothing.
                       }
                   };
               link.add(new Label("label", item.getModel()));
               item.add(link);

//                item.add(new Label("label", item.getModel())); // no
ajax link.

           }
       };

       add(rows);
   }

   private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException
   {
       System.out.println("READ OBJECT " +
Integer.toHexString(TestPage.this.hashCode()));
       in.defaultReadObject();
   }

   private void writeObject(ObjectOutputStream out) throws
IOException, ClassNotFoundException
   {
       System.out.println("WRITE OBJECT " +
Integer.toHexString(TestPage.this.hashCode()));
       out.defaultWriteObject();
   }

   @Override
   public void detachModels()
   {
       System.out.println("DETACH MODELS " +
Integer.toHexString(TestPage.this.hashCode()));
       super.detachModels();
   }
}

with the TestPage.html

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:wicket="http://wicket.apache.org/"
   xml:lang="en" lang="en">
<head>
</head>
<body>
  <div wicket:id="rows">
        <a wicket:id="link"><label wicket:id="label"></label></a>
      <!--  <label wicket:id="label"></label> -->
  </div>
</body>
</html>

I understand that I could use a ListModel, and not worry about this
detaching behaviour, as the list would be a serializable, but I'd like
the list model to always be up to date from the database on page
refresh, which wouldn't be the case for a ListModel. (it would always
have the same values as it did on the initial page render)

I am using wicket 1.5.3 and firefox 8.0.

Thanks.

Phil.

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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Philip Wilkinson <wi...@gmail.com>.
Hi Sven,

OK thanks, that's good, with this..

        final IModel<List<String>> model = new
LoadableDetachableModel<List<String>>()
                {
                    @Override
                    protected List<String> load()
                    {
                        System.out.println("LOAD LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                        return testList;
                    }

                    @Override
                    public void detach()
                    {
                        System.out.println("DETACH LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                        super.detach();
                    }
                };

        final ListView<String> rows = new ListView<String>("rows", model)
        {
            @Override
            protected void populateItem(final ListItem<String> item)
            {
                System.out.println("populateItem " +
Integer.toHexString(TestPage.this.hashCode()));

                // with ajax link to remove entry.
                AjaxLink<Void> link = new AjaxLink<Void>("link")
                    {
                        @Override
                        public void onClick(AjaxRequestTarget target)
                        {
                            model.getObject().remove(item.getIndex()-1);

                            target.add(TestPage.this);
                        }
                    };
                link.add(new Label("label", item.getModel()));
                item.add(link);
            }
        };

I get the following diagnostic output..

LOAD LIST MODEL 12d9a936
populateItem 12d9a936
populateItem 12d9a936
populateItem 12d9a936
DETACH MODELS 12d9a936
DETACH LIST MODEL 12d9a936
DETACH MODELS 12d9a936
DETACH LIST MODEL 12d9a936

which is perfect!

So now I (and everyone else) just has to remember not to pass the List
Views model into the constructor of a component in a List Views Item!


On Mon, Jan 9, 2012 at 9:13 PM, Sven Meier <sv...@meiers.net> wrote:
> Ah, yes, stupid compiler ;).
>
> You can keep the model final and use it on your onClick(), but *not* passing
> it to the AjaxLink constructor:
>
>      final IModel<List<String>>    model = ... // final again !!
>
>
>      final ListView<String>    rows = new ListView<String>("rows", model)
>      {
>          @Override
>          protected void populateItem(final ListItem<String>    item)
>          {
>              AjaxLink<Void>    link = new AjaxLink<Void>("link") // no model
>
>                  {
>                      @Override
>                      public void onClick(AjaxRequestTarget target)
>                      {
>                          model.remove(item.getIndex()-1);
>                          target.add(TestPage.this);
>                       }
>                  };
>               ...
>           }
>      };
>
>
>
>
>
> On 01/09/2012 10:05 PM, Philip Wilkinson wrote:
>>
>> Thanks Sven, interesting idea however, rows is not accessible to
>> onClick() method as compilation error, "rows" not accessible as may
>> not be initialised. I guess Id have to getParent() loop until I found
>> a ListView component :(
>>
>> Cheers, Phil.
>>
>>
>> On Mon, Jan 9, 2012 at 6:31 PM, Sven Meier<sv...@meiers.net>  wrote:
>>>>
>>>> you can ... give each item an isolated model
>>>
>>> Actually ListItemModel doesn't detach the owning list's model, so it's
>>> 'isolated' already.
>>>
>>> For your AjaxLink you can access the list view instead:
>>>
>>>       IModel<List<String>>    model = ... // not final !!
>>>
>>>
>>>       final ListView<String>    rows = new ListView<String>("rows",
>>> model)
>>>       {
>>>           @Override
>>>           protected void populateItem(final ListItem<String>    item)
>>>           {
>>>               AjaxLink<Void>    link = new AjaxLink<Void>("link")
>>>                   {
>>>                       @Override
>>>                       public void onClick(AjaxRequestTarget target)
>>>                       {
>>>
>>> rows.getModelObject().remove(item.getIndex()-1);
>>>                           target.add(TestPage.this);
>>>
>>>                        }
>>>                   };
>>>               link.add(new Label("label", item.getModel()));
>>>               item.add(link);
>>>           }
>>>       };
>>>
>>> Sven
>>>
>>>
>>> On 01/09/2012 05:13 PM, Igor Vaynberg wrote:
>>>>
>>>> the problem here is that the listitem's model is tied to the
>>>> listview's model. so calling detach() on the listview item causes the
>>>> detachment of the listview's model.
>>>>
>>>> you can either override getlistitemmodel() on the listview and give
>>>> each item an isolated model, or you can use a dataview or another
>>>> non-listview repeater.
>>>>
>>>> as far as the many calls to detach(), they are nearly impossible to
>>>> avoid without making the framework much more complicated. in practice
>>>> the logic in detach() is very simple so having multiple calls to it is
>>>> not a problem.
>>>>
>>>> -igor
>>>>
>>>> On Mon, Jan 9, 2012 at 5:06 AM, Philip Wilkinson
>>>> <wi...@gmail.com>    wrote:
>>>>>
>>>>> Sven,
>>>>>
>>>>> Thanks for your speedy reply.
>>>>>
>>>>> Indeed you are correct, removing the model from the AjaxLink stops the
>>>>> model from becoming detached immediately before rendering.
>>>>>
>>>>> I now realise that my cut-down example was too cut-down! The AjaxLink
>>>>> is actually there to remove the entry from the list, requiring the
>>>>> list model to be accessible to the link. So the implementation would
>>>>> be something like:-
>>>>>
>>>>> public class TestPage extends WebPage
>>>>> {
>>>>>    private List<String>    testList = new ArrayList<String>() {{
>>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>>
>>>>>    public TestPage()
>>>>>    {
>>>>>        setOutputMarkupId(true);
>>>>>        final IModel<List<String>>    model = new
>>>>> LoadableDetachableModel<List<String>>()
>>>>>                {
>>>>>                    @Override
>>>>>                    protected List<String>    load()
>>>>>                    {
>>>>>                        System.out.println("LOAD LIST MODEL " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>                        return testList;
>>>>>                    }
>>>>>
>>>>>                    @Override
>>>>>                    public void detach()
>>>>>                    {
>>>>>                        System.out.println("DETACH LIST MODEL " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>                        super.detach();
>>>>>                    }
>>>>>                };
>>>>>
>>>>>        final ListView<String>    rows = new ListView<String>("rows",
>>>>> model)
>>>>>        {
>>>>>            @Override
>>>>>            protected void populateItem(final ListItem<String>    item)
>>>>>            {
>>>>>                System.out.println("populateItem " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>
>>>>>                // with ajax link to remove entry.
>>>>>                AjaxLink<List<String>>    link = new
>>>>> AjaxLink<List<String>>("link", model)
>>>>>                    {
>>>>>                        @Override
>>>>>                        public void onClick(AjaxRequestTarget target)
>>>>>                        {
>>>>>                            getModelObject().remove(item.getIndex()-1);
>>>>>                            target.add(TestPage.this); // normally the
>>>>> target would be a containing panel, but for this example, the page
>>>>> will do!
>>>>>                        }
>>>>>                    };
>>>>>                link.add(new Label("label", item.getModel()));
>>>>>                item.add(link);
>>>>>            }
>>>>>        };
>>>>>
>>>>>        add(rows);
>>>>>    }
>>>>>
>>>>>    @Override
>>>>>    public void detachModels()
>>>>>    {
>>>>>        System.out.println("DETACH MODELS " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>        super.detachModels();
>>>>>    }
>>>>> }
>>>>>
>>>>> Possibly I could get around this by not setting the model on the
>>>>> AjaxLink, so wicket doesn't try to detach it, and passing the list
>>>>> model in separately. But that feels like I'm not following the "wicket
>>>>> way" if I do something like that :(. I think that knowing what the
>>>>> AjaxLink is doing, removing an entry from the list, the most
>>>>> appropriate model for the AjaxLink is the list model!
>>>>>
>>>>> Do you think there's a case here for the wicket framework being a
>>>>> little smarter about when it detaches its models?
>>>>> I think that only the relevant models need to be detached only once
>>>>> after every response has been rendered. So that they can be lazy
>>>>> loaded again when required during the next response \ render cycle.
>>>>>
>>>>> Regards,
>>>>>
>>>>> Phil.
>>>>>
>>>>>
>>>>> On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier<sv...@meiers.net>    wrote:
>>>>>>
>>>>>> Hi Philip,
>>>>>>
>>>>>> ListView throws away all its children before rendering. When a
>>>>>> component
>>>>>> is
>>>>>> removed, its model gets detached.
>>>>>> Your AjaxLink has your LoadableDetachableModel as model, thus it
>>>>>> forces
>>>>>> it
>>>>>> to be detached too.
>>>>>>
>>>>>> As a possible solution, don't keep a reference to the model as your
>>>>>> AjaxLink's model:
>>>>>>                AjaxLink<Void>    link = new AjaxLink<Void>("link")
>>>>>>
>>>>>> Hope this helps
>>>>>> Sven
>>>>>>
>>>>>>
>>>>>> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> Can anyone tell me why when I refresh a wicket page which has on it
>>>>>>> an
>>>>>>> AjaxLink, my list model is being detached just
>>>>>>>
>>>>>>> before its contents are rendered by a list view?
>>>>>>>
>>>>>>> I would expect detach only to be called after everything has been
>>>>>>> rendered.
>>>>>>>
>>>>>>> Detaching just before rendering would call a potentially 2nd
>>>>>>> unnecessary expensive database operation to load the
>>>>>>>
>>>>>>> model.
>>>>>>>
>>>>>>> I have an Ajax‌Link call back which seems to be creating this
>>>>>>> behaviour. Without it the models are detached afer
>>>>>>>
>>>>>>> rendering as expected.
>>>>>>>
>>>>>>> E.g. for the attached simple test page, pressing refresh in the
>>>>>>> browser, when there is no AjaxLink I get the following
>>>>>>>
>>>>>>> diagnostic output from Testpage.java...
>>>>>>>
>>>>>>> LOAD LIST MODEL 79e14338
>>>>>>> populateItem 79e14338
>>>>>>> populateItem 79e14338
>>>>>>> populateItem 79e14338
>>>>>>> DETACH MODELS 79e14338
>>>>>>> DETACH LIST MODEL 79e14338
>>>>>>> DETACH MODELS 79e14338
>>>>>>> DETACH LIST MODEL 79e14338
>>>>>>>
>>>>>>> When I surround the label with an ajax link, pressing refresh in the
>>>>>>> browser, I get..
>>>>>>>
>>>>>>> LOAD LIST MODEL 62b512e7
>>>>>>> DETACH LIST MODEL 62b512e7 //1st ????
>>>>>>> DETACH LIST MODEL 62b512e7 //2nd ????
>>>>>>> DETACH LIST MODEL 62b512e7 //3rd ????
>>>>>>> populateItem 62b512e7 // render "Aaaaa"
>>>>>>> populateItem 62b512e7 // render "Bbbbb"
>>>>>>> populateItem 62b512e7 // render "Ccccc"
>>>>>>> LOAD LIST MODEL 62b512e7
>>>>>>> DETACH MODELS 62b512e7
>>>>>>> DETACH LIST MODEL 62b512e7 //4th
>>>>>>> DETACH LIST MODEL 62b512e7 //5th
>>>>>>> DETACH LIST MODEL 62b512e7 //6th
>>>>>>> DETACH LIST MODEL 62b512e7 //7th
>>>>>>> DETACH MODELS 62b512e7
>>>>>>> DETACH LIST MODEL 62b512e7 //8th
>>>>>>> DETACH LIST MODEL 62b512e7 //9th
>>>>>>> DETACH LIST MODEL 62b512e7 //10th
>>>>>>> DETACH LIST MODEL 62b512e7 //11th
>>>>>>> WRITE OBJECT 62b512e7
>>>>>>>
>>>>>>> .. 11 calls to .detach() on the list model!
>>>>>>> between the 3rd and 4th detach list model the list is actually
>>>>>>> rendered. Had this been for real this would have ment
>>>>>>>
>>>>>>> that the list model would have been loaded again at this point.
>>>>>>> Here's the detail stack traces of where these 11 detach list models
>>>>>>> are being called from when refresh on my test page with
>>>>>>> the AjaxLink...
>>>>>>>
>>>>>>> *** 1st DETACH LIST MODEL ***
>>>>>>> TestPage$2.detach() line: 39
>>>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>>>> ListItem<T>(Component).detach() line: 1161
>>>>>>> TestPage$3(MarkupContainer).removeAll() line: 672
>>>>>>> TestPage$3(ListView<T>).onPopulate() line: 498
>>>>>>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>>>>>>> TestPage$3(Component).internalBeforeRender() line: 981
>>>>>>> TestPage$3(Component).beforeRender() line: 1015
>>>>>>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>>>>>>> TestPage(Component).onBeforeRender() line: 3774
>>>>>>> TestPage(Page).onBeforeRender() line: 823
>>>>>>> TestPage(Component).internalBeforeRender() line: 981
>>>>>>> TestPage(Component).beforeRender() line: 1015
>>>>>>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>>>>>>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>>>>>>> TestPage(Component).render() line: 2271
>>>>>>> TestPage(Page).renderPage() line: 1035
>>>>>>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>>>>>>> WebPageRenderer.respond(RequestCycle) line: 182
>>>>>>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>>>>>>
>>>>>>> *** 2nd DETACH LIST MODEL ***
>>>>>>> Same as before.
>>>>>>>
>>>>>>> *** 3rd DETACH LIST MODEL ***
>>>>>>> Same as before.
>>>>>>>
>>>>>>> *** Now populateItem is called 3 times on the list view to render the
>>>>>>> 3 list elements...
>>>>>>>
>>>>>>> *** 4th DETACH LIST MODEL ***
>>>>>>> TestPage$2.detach() line: 39
>>>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>>>> TestPage$3(Component).detach() line: 1154
>>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>>> TestPage(Component).detach() line: 1161
>>>>>>> PageProvider.detach() line: 318
>>>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>>>> RequestCycle.onDetach() line: 565
>>>>>>> RequestCycle.detach() line: 508
>>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>>> FilterChain) line: 162
>>>>>>>
>>>>>>> *** 5th DETACH LIST MODEL ***
>>>>>>> TestPage$2.detach() line: 39
>>>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>>>> ListItem<T>(Component).detach() line: 1161
>>>>>>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>>>>>>> TestPage$3(Component).detach() line: 1161
>>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>>> TestPage(Component).detach() line: 1161
>>>>>>> PageProvider.detach() line: 318
>>>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>>>> RequestCycle.onDetach() line: 565
>>>>>>> RequestCycle.detach() line: 508
>>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>>> FilterChain) line: 162
>>>>>>>
>>>>>>> *** 6th DETACH LIST MODEL ***
>>>>>>> Same as before
>>>>>>>
>>>>>>> *** 7th DETACH LIST MODEL ***
>>>>>>> Same as before
>>>>>>>
>>>>>>> *** 8th DETACH LIST MODEL ***
>>>>>>> TestPage$2.detach() line: 39
>>>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>>>> TestPage$3(Component).detach() line: 1154
>>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>>> TestPage(Component).detach() line: 1161
>>>>>>>
>>>>>>>
>>>>>>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>>>>>>> line: 156
>>>>>>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>>>>>>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line:
>>>>>>> 68
>>>>>>> PageAccessSynchronizer$2.commitRequest() line: 281
>>>>>>> Application$2.onDetach(RequestCycle) line: 1588
>>>>>>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line:
>>>>>>> 99
>>>>>>> RequestCycleListenerCollection$3.notify(Object) line: 97
>>>>>>> ListenerCollection$1.notify(T) line: 119
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>>>>>>> line: 143
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>>>>>>> line: 113
>>>>>>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>>>>>>> RequestCycle.onDetach() line: 569
>>>>>>> RequestCycle.detach() line: 508
>>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>>> FilterChain) line: 162
>>>>>>>
>>>>>>> *** 9th DETACH LIST MODEL ***
>>>>>>> Same as before
>>>>>>>
>>>>>>> *** 10th DETACH LIST MODEL ***
>>>>>>> Same as before
>>>>>>>
>>>>>>> *** 11th DETACH LIST MODEL ***
>>>>>>> Same as before
>>>>>>>
>>>>>>> To show this behaviour I've created the following test page.
>>>>>>> TestPage.java
>>>>>>>
>>>>>>> public class TestPage extends WebPage
>>>>>>> {
>>>>>>>    private static List<String>      testList = new
>>>>>>> ArrayList<String>() {{
>>>>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>>>>
>>>>>>>    public TestPage()
>>>>>>>    {
>>>>>>>        final IModel<List<String>>      model = new
>>>>>>> LoadableDetachableModel<List<String>>()
>>>>>>>                {
>>>>>>>                    @Override
>>>>>>>                    protected List<String>      load()
>>>>>>>                    {
>>>>>>>                        System.out.println("LOAD LIST MODEL " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>                        return testList; // would normally get list
>>>>>>> from db, in this case just retun static list.
>>>>>>>                    }
>>>>>>>
>>>>>>>                    @Override
>>>>>>>                    public void detach()
>>>>>>>                    {
>>>>>>>                        System.out.println("DETACH LIST MODEL " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>                        super.detach(); // detach model. "heavyweight"
>>>>>>> model no longer needed.
>>>>>>>                    }
>>>>>>>                };
>>>>>>>
>>>>>>>        final ListView<String>      rows = new
>>>>>>> ListView<String>("rows",
>>>>>>> model)
>>>>>>>        {
>>>>>>>            @Override
>>>>>>>            protected void populateItem(ListItem<String>      item)
>>>>>>>            {
>>>>>>>                System.out.println("populateItem " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>
>>>>>>>                // with ajax link.
>>>>>>>                AjaxLink<List<String>>      link = new
>>>>>>> AjaxLink<List<String>>("link", model)
>>>>>>>                    {
>>>>>>>                        @Override
>>>>>>>                        public void onClick(AjaxRequestTarget target)
>>>>>>>                        {
>>>>>>>                            // Do nothing.
>>>>>>>                        }
>>>>>>>                    };
>>>>>>>                link.add(new Label("label", item.getModel()));
>>>>>>>                item.add(link);
>>>>>>>
>>>>>>> //                item.add(new Label("label", item.getModel())); //
>>>>>>> no
>>>>>>> ajax link.
>>>>>>>
>>>>>>>            }
>>>>>>>        };
>>>>>>>
>>>>>>>        add(rows);
>>>>>>>    }
>>>>>>>
>>>>>>>    private void readObject(ObjectInputStream in) throws IOException,
>>>>>>> ClassNotFoundException
>>>>>>>    {
>>>>>>>        System.out.println("READ OBJECT " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>        in.defaultReadObject();
>>>>>>>    }
>>>>>>>
>>>>>>>    private void writeObject(ObjectOutputStream out) throws
>>>>>>> IOException, ClassNotFoundException
>>>>>>>    {
>>>>>>>        System.out.println("WRITE OBJECT " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>        out.defaultWriteObject();
>>>>>>>    }
>>>>>>>
>>>>>>>    @Override
>>>>>>>    public void detachModels()
>>>>>>>    {
>>>>>>>        System.out.println("DETACH MODELS " +
>>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>>        super.detachModels();
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> with the TestPage.html
>>>>>>>
>>>>>>> <?xml version="1.0" encoding="UTF-8" ?>
>>>>>>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>>>>>>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>>>>>>> <html xmlns="http://www.w3.org/1999/xhtml"
>>>>>>> xmlns:wicket="http://wicket.apache.org/"
>>>>>>>    xml:lang="en" lang="en">
>>>>>>> <head>
>>>>>>> </head>
>>>>>>> <body>
>>>>>>>   <div wicket:id="rows">
>>>>>>>         <a wicket:id="link"><label wicket:id="label"></label></a>
>>>>>>>       <!--<label wicket:id="label"></label>      -->
>>>>>>>   </div>
>>>>>>> </body>
>>>>>>> </html>
>>>>>>>
>>>>>>> I understand that I could use a ListModel, and not worry about this
>>>>>>> detaching behaviour, as the list would be a serializable, but I'd
>>>>>>> like
>>>>>>> the list model to always be up to date from the database on page
>>>>>>> refresh, which wouldn't be the case for a ListModel. (it would always
>>>>>>> have the same values as it did on the initial page render)
>>>>>>>
>>>>>>> I am using wicket 1.5.3 and firefox 8.0.
>>>>>>>
>>>>>>> Thanks.
>>>>>>>
>>>>>>> Phil.
>>>>>>>
>>>>>>> ---------------------------------------------------------------------
>>>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>>>
>>>>>> ---------------------------------------------------------------------
>>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>

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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Sven Meier <sv...@meiers.net>.
Ah, yes, stupid compiler ;).

You can keep the model final and use it on your onClick(), but *not* 
passing it to the AjaxLink constructor:

       final IModel<List<String>>    model = ... // final again !!

       final ListView<String>    rows = new ListView<String>("rows", model)
       {
           @Override
           protected void populateItem(final ListItem<String>    item)
           {
               AjaxLink<Void>    link = new AjaxLink<Void>("link") // no model
                   {
                       @Override
                       public void onClick(AjaxRequestTarget target)
                       {
                           model.remove(item.getIndex()-1);
                           target.add(TestPage.this);
                        }
                   };
                ...
            }
       };




On 01/09/2012 10:05 PM, Philip Wilkinson wrote:
> Thanks Sven, interesting idea however, rows is not accessible to
> onClick() method as compilation error, "rows" not accessible as may
> not be initialised. I guess Id have to getParent() loop until I found
> a ListView component :(
>
> Cheers, Phil.
>
>
> On Mon, Jan 9, 2012 at 6:31 PM, Sven Meier<sv...@meiers.net>  wrote:
>>> you can ... give each item an isolated model
>> Actually ListItemModel doesn't detach the owning list's model, so it's
>> 'isolated' already.
>>
>> For your AjaxLink you can access the list view instead:
>>
>>        IModel<List<String>>    model = ... // not final !!
>>
>>
>>        final ListView<String>    rows = new ListView<String>("rows", model)
>>        {
>>            @Override
>>            protected void populateItem(final ListItem<String>    item)
>>            {
>>                AjaxLink<Void>    link = new AjaxLink<Void>("link")
>>                    {
>>                        @Override
>>                        public void onClick(AjaxRequestTarget target)
>>                        {
>>                            rows.getModelObject().remove(item.getIndex()-1);
>>                            target.add(TestPage.this);
>>
>>                         }
>>                    };
>>                link.add(new Label("label", item.getModel()));
>>                item.add(link);
>>            }
>>        };
>>
>> Sven
>>
>>
>> On 01/09/2012 05:13 PM, Igor Vaynberg wrote:
>>> the problem here is that the listitem's model is tied to the
>>> listview's model. so calling detach() on the listview item causes the
>>> detachment of the listview's model.
>>>
>>> you can either override getlistitemmodel() on the listview and give
>>> each item an isolated model, or you can use a dataview or another
>>> non-listview repeater.
>>>
>>> as far as the many calls to detach(), they are nearly impossible to
>>> avoid without making the framework much more complicated. in practice
>>> the logic in detach() is very simple so having multiple calls to it is
>>> not a problem.
>>>
>>> -igor
>>>
>>> On Mon, Jan 9, 2012 at 5:06 AM, Philip Wilkinson
>>> <wi...@gmail.com>    wrote:
>>>> Sven,
>>>>
>>>> Thanks for your speedy reply.
>>>>
>>>> Indeed you are correct, removing the model from the AjaxLink stops the
>>>> model from becoming detached immediately before rendering.
>>>>
>>>> I now realise that my cut-down example was too cut-down! The AjaxLink
>>>> is actually there to remove the entry from the list, requiring the
>>>> list model to be accessible to the link. So the implementation would
>>>> be something like:-
>>>>
>>>> public class TestPage extends WebPage
>>>> {
>>>>     private List<String>    testList = new ArrayList<String>() {{
>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>
>>>>     public TestPage()
>>>>     {
>>>>         setOutputMarkupId(true);
>>>>         final IModel<List<String>>    model = new
>>>> LoadableDetachableModel<List<String>>()
>>>>                 {
>>>>                     @Override
>>>>                     protected List<String>    load()
>>>>                     {
>>>>                         System.out.println("LOAD LIST MODEL " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>                         return testList;
>>>>                     }
>>>>
>>>>                     @Override
>>>>                     public void detach()
>>>>                     {
>>>>                         System.out.println("DETACH LIST MODEL " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>                         super.detach();
>>>>                     }
>>>>                 };
>>>>
>>>>         final ListView<String>    rows = new ListView<String>("rows", model)
>>>>         {
>>>>             @Override
>>>>             protected void populateItem(final ListItem<String>    item)
>>>>             {
>>>>                 System.out.println("populateItem " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>
>>>>                 // with ajax link to remove entry.
>>>>                 AjaxLink<List<String>>    link = new
>>>> AjaxLink<List<String>>("link", model)
>>>>                     {
>>>>                         @Override
>>>>                         public void onClick(AjaxRequestTarget target)
>>>>                         {
>>>>                             getModelObject().remove(item.getIndex()-1);
>>>>                             target.add(TestPage.this); // normally the
>>>> target would be a containing panel, but for this example, the page
>>>> will do!
>>>>                         }
>>>>                     };
>>>>                 link.add(new Label("label", item.getModel()));
>>>>                 item.add(link);
>>>>             }
>>>>         };
>>>>
>>>>         add(rows);
>>>>     }
>>>>
>>>>     @Override
>>>>     public void detachModels()
>>>>     {
>>>>         System.out.println("DETACH MODELS " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>         super.detachModels();
>>>>     }
>>>> }
>>>>
>>>> Possibly I could get around this by not setting the model on the
>>>> AjaxLink, so wicket doesn't try to detach it, and passing the list
>>>> model in separately. But that feels like I'm not following the "wicket
>>>> way" if I do something like that :(. I think that knowing what the
>>>> AjaxLink is doing, removing an entry from the list, the most
>>>> appropriate model for the AjaxLink is the list model!
>>>>
>>>> Do you think there's a case here for the wicket framework being a
>>>> little smarter about when it detaches its models?
>>>> I think that only the relevant models need to be detached only once
>>>> after every response has been rendered. So that they can be lazy
>>>> loaded again when required during the next response \ render cycle.
>>>>
>>>> Regards,
>>>>
>>>> Phil.
>>>>
>>>>
>>>> On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier<sv...@meiers.net>    wrote:
>>>>> Hi Philip,
>>>>>
>>>>> ListView throws away all its children before rendering. When a component
>>>>> is
>>>>> removed, its model gets detached.
>>>>> Your AjaxLink has your LoadableDetachableModel as model, thus it forces
>>>>> it
>>>>> to be detached too.
>>>>>
>>>>> As a possible solution, don't keep a reference to the model as your
>>>>> AjaxLink's model:
>>>>>                 AjaxLink<Void>    link = new AjaxLink<Void>("link")
>>>>>
>>>>> Hope this helps
>>>>> Sven
>>>>>
>>>>>
>>>>> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>>>>> Hi,
>>>>>>
>>>>>> Can anyone tell me why when I refresh a wicket page which has on it an
>>>>>> AjaxLink, my list model is being detached just
>>>>>>
>>>>>> before its contents are rendered by a list view?
>>>>>>
>>>>>> I would expect detach only to be called after everything has been
>>>>>> rendered.
>>>>>>
>>>>>> Detaching just before rendering would call a potentially 2nd
>>>>>> unnecessary expensive database operation to load the
>>>>>>
>>>>>> model.
>>>>>>
>>>>>> I have an Ajax‌Link call back which seems to be creating this
>>>>>> behaviour. Without it the models are detached afer
>>>>>>
>>>>>> rendering as expected.
>>>>>>
>>>>>> E.g. for the attached simple test page, pressing refresh in the
>>>>>> browser, when there is no AjaxLink I get the following
>>>>>>
>>>>>> diagnostic output from Testpage.java...
>>>>>>
>>>>>> LOAD LIST MODEL 79e14338
>>>>>> populateItem 79e14338
>>>>>> populateItem 79e14338
>>>>>> populateItem 79e14338
>>>>>> DETACH MODELS 79e14338
>>>>>> DETACH LIST MODEL 79e14338
>>>>>> DETACH MODELS 79e14338
>>>>>> DETACH LIST MODEL 79e14338
>>>>>>
>>>>>> When I surround the label with an ajax link, pressing refresh in the
>>>>>> browser, I get..
>>>>>>
>>>>>> LOAD LIST MODEL 62b512e7
>>>>>> DETACH LIST MODEL 62b512e7 //1st ????
>>>>>> DETACH LIST MODEL 62b512e7 //2nd ????
>>>>>> DETACH LIST MODEL 62b512e7 //3rd ????
>>>>>> populateItem 62b512e7 // render "Aaaaa"
>>>>>> populateItem 62b512e7 // render "Bbbbb"
>>>>>> populateItem 62b512e7 // render "Ccccc"
>>>>>> LOAD LIST MODEL 62b512e7
>>>>>> DETACH MODELS 62b512e7
>>>>>> DETACH LIST MODEL 62b512e7 //4th
>>>>>> DETACH LIST MODEL 62b512e7 //5th
>>>>>> DETACH LIST MODEL 62b512e7 //6th
>>>>>> DETACH LIST MODEL 62b512e7 //7th
>>>>>> DETACH MODELS 62b512e7
>>>>>> DETACH LIST MODEL 62b512e7 //8th
>>>>>> DETACH LIST MODEL 62b512e7 //9th
>>>>>> DETACH LIST MODEL 62b512e7 //10th
>>>>>> DETACH LIST MODEL 62b512e7 //11th
>>>>>> WRITE OBJECT 62b512e7
>>>>>>
>>>>>> .. 11 calls to .detach() on the list model!
>>>>>> between the 3rd and 4th detach list model the list is actually
>>>>>> rendered. Had this been for real this would have ment
>>>>>>
>>>>>> that the list model would have been loaded again at this point.
>>>>>> Here's the detail stack traces of where these 11 detach list models
>>>>>> are being called from when refresh on my test page with
>>>>>> the AjaxLink...
>>>>>>
>>>>>> *** 1st DETACH LIST MODEL ***
>>>>>> TestPage$2.detach() line: 39
>>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>>> ListItem<T>(Component).detach() line: 1161
>>>>>> TestPage$3(MarkupContainer).removeAll() line: 672
>>>>>> TestPage$3(ListView<T>).onPopulate() line: 498
>>>>>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>>>>>> TestPage$3(Component).internalBeforeRender() line: 981
>>>>>> TestPage$3(Component).beforeRender() line: 1015
>>>>>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>>>>>> TestPage(Component).onBeforeRender() line: 3774
>>>>>> TestPage(Page).onBeforeRender() line: 823
>>>>>> TestPage(Component).internalBeforeRender() line: 981
>>>>>> TestPage(Component).beforeRender() line: 1015
>>>>>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>>>>>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>>>>>> TestPage(Component).render() line: 2271
>>>>>> TestPage(Page).renderPage() line: 1035
>>>>>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>>>>>> WebPageRenderer.respond(RequestCycle) line: 182
>>>>>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>>>>>
>>>>>> *** 2nd DETACH LIST MODEL ***
>>>>>> Same as before.
>>>>>>
>>>>>> *** 3rd DETACH LIST MODEL ***
>>>>>> Same as before.
>>>>>>
>>>>>> *** Now populateItem is called 3 times on the list view to render the
>>>>>> 3 list elements...
>>>>>>
>>>>>> *** 4th DETACH LIST MODEL ***
>>>>>> TestPage$2.detach() line: 39
>>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>>> TestPage$3(Component).detach() line: 1154
>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>> TestPage(Component).detach() line: 1161
>>>>>> PageProvider.detach() line: 318
>>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>>> RequestCycle.onDetach() line: 565
>>>>>> RequestCycle.detach() line: 508
>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>> FilterChain) line: 162
>>>>>>
>>>>>> *** 5th DETACH LIST MODEL ***
>>>>>> TestPage$2.detach() line: 39
>>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>>> ListItem<T>(Component).detach() line: 1161
>>>>>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>>>>>> TestPage$3(Component).detach() line: 1161
>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>> TestPage(Component).detach() line: 1161
>>>>>> PageProvider.detach() line: 318
>>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>>> RequestCycle.onDetach() line: 565
>>>>>> RequestCycle.detach() line: 508
>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>> FilterChain) line: 162
>>>>>>
>>>>>> *** 6th DETACH LIST MODEL ***
>>>>>> Same as before
>>>>>>
>>>>>> *** 7th DETACH LIST MODEL ***
>>>>>> Same as before
>>>>>>
>>>>>> *** 8th DETACH LIST MODEL ***
>>>>>> TestPage$2.detach() line: 39
>>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>>> TestPage$3(Component).detach() line: 1154
>>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>>> TestPage(Component).detach() line: 1161
>>>>>>
>>>>>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>>>>>> line: 156
>>>>>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>>>>>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
>>>>>> PageAccessSynchronizer$2.commitRequest() line: 281
>>>>>> Application$2.onDetach(RequestCycle) line: 1588
>>>>>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
>>>>>> RequestCycleListenerCollection$3.notify(Object) line: 97
>>>>>> ListenerCollection$1.notify(T) line: 119
>>>>>>
>>>>>>
>>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>>>>>> line: 143
>>>>>>
>>>>>>
>>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>>>>>> line: 113
>>>>>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>>>>>> RequestCycle.onDetach() line: 569
>>>>>> RequestCycle.detach() line: 508
>>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>>> FilterChain) line: 162
>>>>>>
>>>>>> *** 9th DETACH LIST MODEL ***
>>>>>> Same as before
>>>>>>
>>>>>> *** 10th DETACH LIST MODEL ***
>>>>>> Same as before
>>>>>>
>>>>>> *** 11th DETACH LIST MODEL ***
>>>>>> Same as before
>>>>>>
>>>>>> To show this behaviour I've created the following test page.
>>>>>> TestPage.java
>>>>>>
>>>>>> public class TestPage extends WebPage
>>>>>> {
>>>>>>     private static List<String>      testList = new ArrayList<String>() {{
>>>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>>>
>>>>>>     public TestPage()
>>>>>>     {
>>>>>>         final IModel<List<String>>      model = new
>>>>>> LoadableDetachableModel<List<String>>()
>>>>>>                 {
>>>>>>                     @Override
>>>>>>                     protected List<String>      load()
>>>>>>                     {
>>>>>>                         System.out.println("LOAD LIST MODEL " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>                         return testList; // would normally get list
>>>>>> from db, in this case just retun static list.
>>>>>>                     }
>>>>>>
>>>>>>                     @Override
>>>>>>                     public void detach()
>>>>>>                     {
>>>>>>                         System.out.println("DETACH LIST MODEL " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>                         super.detach(); // detach model. "heavyweight"
>>>>>> model no longer needed.
>>>>>>                     }
>>>>>>                 };
>>>>>>
>>>>>>         final ListView<String>      rows = new ListView<String>("rows",
>>>>>> model)
>>>>>>         {
>>>>>>             @Override
>>>>>>             protected void populateItem(ListItem<String>      item)
>>>>>>             {
>>>>>>                 System.out.println("populateItem " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>
>>>>>>                 // with ajax link.
>>>>>>                 AjaxLink<List<String>>      link = new
>>>>>> AjaxLink<List<String>>("link", model)
>>>>>>                     {
>>>>>>                         @Override
>>>>>>                         public void onClick(AjaxRequestTarget target)
>>>>>>                         {
>>>>>>                             // Do nothing.
>>>>>>                         }
>>>>>>                     };
>>>>>>                 link.add(new Label("label", item.getModel()));
>>>>>>                 item.add(link);
>>>>>>
>>>>>> //                item.add(new Label("label", item.getModel())); // no
>>>>>> ajax link.
>>>>>>
>>>>>>             }
>>>>>>         };
>>>>>>
>>>>>>         add(rows);
>>>>>>     }
>>>>>>
>>>>>>     private void readObject(ObjectInputStream in) throws IOException,
>>>>>> ClassNotFoundException
>>>>>>     {
>>>>>>         System.out.println("READ OBJECT " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>         in.defaultReadObject();
>>>>>>     }
>>>>>>
>>>>>>     private void writeObject(ObjectOutputStream out) throws
>>>>>> IOException, ClassNotFoundException
>>>>>>     {
>>>>>>         System.out.println("WRITE OBJECT " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>         out.defaultWriteObject();
>>>>>>     }
>>>>>>
>>>>>>     @Override
>>>>>>     public void detachModels()
>>>>>>     {
>>>>>>         System.out.println("DETACH MODELS " +
>>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>>         super.detachModels();
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>> with the TestPage.html
>>>>>>
>>>>>> <?xml version="1.0" encoding="UTF-8" ?>
>>>>>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>>>>>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>>>>>> <html xmlns="http://www.w3.org/1999/xhtml"
>>>>>> xmlns:wicket="http://wicket.apache.org/"
>>>>>>     xml:lang="en" lang="en">
>>>>>> <head>
>>>>>> </head>
>>>>>> <body>
>>>>>>    <div wicket:id="rows">
>>>>>>          <a wicket:id="link"><label wicket:id="label"></label></a>
>>>>>>        <!--<label wicket:id="label"></label>      -->
>>>>>>    </div>
>>>>>> </body>
>>>>>> </html>
>>>>>>
>>>>>> I understand that I could use a ListModel, and not worry about this
>>>>>> detaching behaviour, as the list would be a serializable, but I'd like
>>>>>> the list model to always be up to date from the database on page
>>>>>> refresh, which wouldn't be the case for a ListModel. (it would always
>>>>>> have the same values as it did on the initial page render)
>>>>>>
>>>>>> I am using wicket 1.5.3 and firefox 8.0.
>>>>>>
>>>>>> Thanks.
>>>>>>
>>>>>> Phil.
>>>>>>
>>>>>> ---------------------------------------------------------------------
>>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>


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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Philip Wilkinson <wi...@gmail.com>.
Thanks Sven, interesting idea however, rows is not accessible to
onClick() method as compilation error, "rows" not accessible as may
not be initialised. I guess Id have to getParent() loop until I found
a ListView component :(

Cheers, Phil.


On Mon, Jan 9, 2012 at 6:31 PM, Sven Meier <sv...@meiers.net> wrote:
>>you can ... give each item an isolated model
>
> Actually ListItemModel doesn't detach the owning list's model, so it's
> 'isolated' already.
>
> For your AjaxLink you can access the list view instead:
>
>       IModel<List<String>>  model = ... // not final !!
>
>
>       final ListView<String>  rows = new ListView<String>("rows", model)
>       {
>           @Override
>           protected void populateItem(final ListItem<String>  item)
>           {
>               AjaxLink<Void>  link = new AjaxLink<Void>("link")
>                   {
>                       @Override
>                       public void onClick(AjaxRequestTarget target)
>                       {
>                           rows.getModelObject().remove(item.getIndex()-1);
>                           target.add(TestPage.this);
>
>                        }
>                   };
>               link.add(new Label("label", item.getModel()));
>               item.add(link);
>           }
>       };
>
> Sven
>
>
> On 01/09/2012 05:13 PM, Igor Vaynberg wrote:
>>
>> the problem here is that the listitem's model is tied to the
>> listview's model. so calling detach() on the listview item causes the
>> detachment of the listview's model.
>>
>> you can either override getlistitemmodel() on the listview and give
>> each item an isolated model, or you can use a dataview or another
>> non-listview repeater.
>>
>> as far as the many calls to detach(), they are nearly impossible to
>> avoid without making the framework much more complicated. in practice
>> the logic in detach() is very simple so having multiple calls to it is
>> not a problem.
>>
>> -igor
>>
>> On Mon, Jan 9, 2012 at 5:06 AM, Philip Wilkinson
>> <wi...@gmail.com>  wrote:
>>>
>>> Sven,
>>>
>>> Thanks for your speedy reply.
>>>
>>> Indeed you are correct, removing the model from the AjaxLink stops the
>>> model from becoming detached immediately before rendering.
>>>
>>> I now realise that my cut-down example was too cut-down! The AjaxLink
>>> is actually there to remove the entry from the list, requiring the
>>> list model to be accessible to the link. So the implementation would
>>> be something like:-
>>>
>>> public class TestPage extends WebPage
>>> {
>>>    private List<String>  testList = new ArrayList<String>() {{
>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>
>>>    public TestPage()
>>>    {
>>>        setOutputMarkupId(true);
>>>        final IModel<List<String>>  model = new
>>> LoadableDetachableModel<List<String>>()
>>>                {
>>>                    @Override
>>>                    protected List<String>  load()
>>>                    {
>>>                        System.out.println("LOAD LIST MODEL " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>                        return testList;
>>>                    }
>>>
>>>                    @Override
>>>                    public void detach()
>>>                    {
>>>                        System.out.println("DETACH LIST MODEL " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>                        super.detach();
>>>                    }
>>>                };
>>>
>>>        final ListView<String>  rows = new ListView<String>("rows", model)
>>>        {
>>>            @Override
>>>            protected void populateItem(final ListItem<String>  item)
>>>            {
>>>                System.out.println("populateItem " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>
>>>                // with ajax link to remove entry.
>>>                AjaxLink<List<String>>  link = new
>>> AjaxLink<List<String>>("link", model)
>>>                    {
>>>                        @Override
>>>                        public void onClick(AjaxRequestTarget target)
>>>                        {
>>>                            getModelObject().remove(item.getIndex()-1);
>>>                            target.add(TestPage.this); // normally the
>>> target would be a containing panel, but for this example, the page
>>> will do!
>>>                        }
>>>                    };
>>>                link.add(new Label("label", item.getModel()));
>>>                item.add(link);
>>>            }
>>>        };
>>>
>>>        add(rows);
>>>    }
>>>
>>>    @Override
>>>    public void detachModels()
>>>    {
>>>        System.out.println("DETACH MODELS " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>        super.detachModels();
>>>    }
>>> }
>>>
>>> Possibly I could get around this by not setting the model on the
>>> AjaxLink, so wicket doesn't try to detach it, and passing the list
>>> model in separately. But that feels like I'm not following the "wicket
>>> way" if I do something like that :(. I think that knowing what the
>>> AjaxLink is doing, removing an entry from the list, the most
>>> appropriate model for the AjaxLink is the list model!
>>>
>>> Do you think there's a case here for the wicket framework being a
>>> little smarter about when it detaches its models?
>>> I think that only the relevant models need to be detached only once
>>> after every response has been rendered. So that they can be lazy
>>> loaded again when required during the next response \ render cycle.
>>>
>>> Regards,
>>>
>>> Phil.
>>>
>>>
>>> On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier<sv...@meiers.net>  wrote:
>>>>
>>>> Hi Philip,
>>>>
>>>> ListView throws away all its children before rendering. When a component
>>>> is
>>>> removed, its model gets detached.
>>>> Your AjaxLink has your LoadableDetachableModel as model, thus it forces
>>>> it
>>>> to be detached too.
>>>>
>>>> As a possible solution, don't keep a reference to the model as your
>>>> AjaxLink's model:
>>>>                AjaxLink<Void>  link = new AjaxLink<Void>("link")
>>>>
>>>> Hope this helps
>>>> Sven
>>>>
>>>>
>>>> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> Can anyone tell me why when I refresh a wicket page which has on it an
>>>>> AjaxLink, my list model is being detached just
>>>>>
>>>>> before its contents are rendered by a list view?
>>>>>
>>>>> I would expect detach only to be called after everything has been
>>>>> rendered.
>>>>>
>>>>> Detaching just before rendering would call a potentially 2nd
>>>>> unnecessary expensive database operation to load the
>>>>>
>>>>> model.
>>>>>
>>>>> I have an Ajax‌Link call back which seems to be creating this
>>>>> behaviour. Without it the models are detached afer
>>>>>
>>>>> rendering as expected.
>>>>>
>>>>> E.g. for the attached simple test page, pressing refresh in the
>>>>> browser, when there is no AjaxLink I get the following
>>>>>
>>>>> diagnostic output from Testpage.java...
>>>>>
>>>>> LOAD LIST MODEL 79e14338
>>>>> populateItem 79e14338
>>>>> populateItem 79e14338
>>>>> populateItem 79e14338
>>>>> DETACH MODELS 79e14338
>>>>> DETACH LIST MODEL 79e14338
>>>>> DETACH MODELS 79e14338
>>>>> DETACH LIST MODEL 79e14338
>>>>>
>>>>> When I surround the label with an ajax link, pressing refresh in the
>>>>> browser, I get..
>>>>>
>>>>> LOAD LIST MODEL 62b512e7
>>>>> DETACH LIST MODEL 62b512e7 //1st ????
>>>>> DETACH LIST MODEL 62b512e7 //2nd ????
>>>>> DETACH LIST MODEL 62b512e7 //3rd ????
>>>>> populateItem 62b512e7 // render "Aaaaa"
>>>>> populateItem 62b512e7 // render "Bbbbb"
>>>>> populateItem 62b512e7 // render "Ccccc"
>>>>> LOAD LIST MODEL 62b512e7
>>>>> DETACH MODELS 62b512e7
>>>>> DETACH LIST MODEL 62b512e7 //4th
>>>>> DETACH LIST MODEL 62b512e7 //5th
>>>>> DETACH LIST MODEL 62b512e7 //6th
>>>>> DETACH LIST MODEL 62b512e7 //7th
>>>>> DETACH MODELS 62b512e7
>>>>> DETACH LIST MODEL 62b512e7 //8th
>>>>> DETACH LIST MODEL 62b512e7 //9th
>>>>> DETACH LIST MODEL 62b512e7 //10th
>>>>> DETACH LIST MODEL 62b512e7 //11th
>>>>> WRITE OBJECT 62b512e7
>>>>>
>>>>> .. 11 calls to .detach() on the list model!
>>>>> between the 3rd and 4th detach list model the list is actually
>>>>> rendered. Had this been for real this would have ment
>>>>>
>>>>> that the list model would have been loaded again at this point.
>>>>> Here's the detail stack traces of where these 11 detach list models
>>>>> are being called from when refresh on my test page with
>>>>> the AjaxLink...
>>>>>
>>>>> *** 1st DETACH LIST MODEL ***
>>>>> TestPage$2.detach() line: 39
>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>> ListItem<T>(Component).detach() line: 1161
>>>>> TestPage$3(MarkupContainer).removeAll() line: 672
>>>>> TestPage$3(ListView<T>).onPopulate() line: 498
>>>>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>>>>> TestPage$3(Component).internalBeforeRender() line: 981
>>>>> TestPage$3(Component).beforeRender() line: 1015
>>>>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>>>>> TestPage(Component).onBeforeRender() line: 3774
>>>>> TestPage(Page).onBeforeRender() line: 823
>>>>> TestPage(Component).internalBeforeRender() line: 981
>>>>> TestPage(Component).beforeRender() line: 1015
>>>>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>>>>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>>>>> TestPage(Component).render() line: 2271
>>>>> TestPage(Page).renderPage() line: 1035
>>>>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>>>>> WebPageRenderer.respond(RequestCycle) line: 182
>>>>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>>>>
>>>>> *** 2nd DETACH LIST MODEL ***
>>>>> Same as before.
>>>>>
>>>>> *** 3rd DETACH LIST MODEL ***
>>>>> Same as before.
>>>>>
>>>>> *** Now populateItem is called 3 times on the list view to render the
>>>>> 3 list elements...
>>>>>
>>>>> *** 4th DETACH LIST MODEL ***
>>>>> TestPage$2.detach() line: 39
>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>> TestPage$3(Component).detach() line: 1154
>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>> TestPage(Component).detach() line: 1161
>>>>> PageProvider.detach() line: 318
>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>> RequestCycle.onDetach() line: 565
>>>>> RequestCycle.detach() line: 508
>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>> FilterChain) line: 162
>>>>>
>>>>> *** 5th DETACH LIST MODEL ***
>>>>> TestPage$2.detach() line: 39
>>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>>> TestPage$3$1(Component).detach() line: 1154
>>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>>> ListItem<T>(Component).detach() line: 1161
>>>>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>>>>> TestPage$3(Component).detach() line: 1161
>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>> TestPage(Component).detach() line: 1161
>>>>> PageProvider.detach() line: 318
>>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>>> RequestCycle.onDetach() line: 565
>>>>> RequestCycle.detach() line: 508
>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>> FilterChain) line: 162
>>>>>
>>>>> *** 6th DETACH LIST MODEL ***
>>>>> Same as before
>>>>>
>>>>> *** 7th DETACH LIST MODEL ***
>>>>> Same as before
>>>>>
>>>>> *** 8th DETACH LIST MODEL ***
>>>>> TestPage$2.detach() line: 39
>>>>> TestPage$3(Component).detachModel() line: 3536
>>>>> TestPage$3(Component).detachModels() line: 1203
>>>>> TestPage$3(Component).detach() line: 1154
>>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>>> TestPage(Component).detach() line: 1161
>>>>>
>>>>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>>>>> line: 156
>>>>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>>>>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
>>>>> PageAccessSynchronizer$2.commitRequest() line: 281
>>>>> Application$2.onDetach(RequestCycle) line: 1588
>>>>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
>>>>> RequestCycleListenerCollection$3.notify(Object) line: 97
>>>>> ListenerCollection$1.notify(T) line: 119
>>>>>
>>>>>
>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>>>>> line: 143
>>>>>
>>>>>
>>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>>>>> line: 113
>>>>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>>>>> RequestCycle.onDetach() line: 569
>>>>> RequestCycle.detach() line: 508
>>>>> RequestCycle.processRequestAndDetach() line: 284
>>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>>> FilterChain) line: 162
>>>>>
>>>>> *** 9th DETACH LIST MODEL ***
>>>>> Same as before
>>>>>
>>>>> *** 10th DETACH LIST MODEL ***
>>>>> Same as before
>>>>>
>>>>> *** 11th DETACH LIST MODEL ***
>>>>> Same as before
>>>>>
>>>>> To show this behaviour I've created the following test page.
>>>>> TestPage.java
>>>>>
>>>>> public class TestPage extends WebPage
>>>>> {
>>>>>    private static List<String>    testList = new ArrayList<String>() {{
>>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>>
>>>>>    public TestPage()
>>>>>    {
>>>>>        final IModel<List<String>>    model = new
>>>>> LoadableDetachableModel<List<String>>()
>>>>>                {
>>>>>                    @Override
>>>>>                    protected List<String>    load()
>>>>>                    {
>>>>>                        System.out.println("LOAD LIST MODEL " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>                        return testList; // would normally get list
>>>>> from db, in this case just retun static list.
>>>>>                    }
>>>>>
>>>>>                    @Override
>>>>>                    public void detach()
>>>>>                    {
>>>>>                        System.out.println("DETACH LIST MODEL " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>                        super.detach(); // detach model. "heavyweight"
>>>>> model no longer needed.
>>>>>                    }
>>>>>                };
>>>>>
>>>>>        final ListView<String>    rows = new ListView<String>("rows",
>>>>> model)
>>>>>        {
>>>>>            @Override
>>>>>            protected void populateItem(ListItem<String>    item)
>>>>>            {
>>>>>                System.out.println("populateItem " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>
>>>>>                // with ajax link.
>>>>>                AjaxLink<List<String>>    link = new
>>>>> AjaxLink<List<String>>("link", model)
>>>>>                    {
>>>>>                        @Override
>>>>>                        public void onClick(AjaxRequestTarget target)
>>>>>                        {
>>>>>                            // Do nothing.
>>>>>                        }
>>>>>                    };
>>>>>                link.add(new Label("label", item.getModel()));
>>>>>                item.add(link);
>>>>>
>>>>> //                item.add(new Label("label", item.getModel())); // no
>>>>> ajax link.
>>>>>
>>>>>            }
>>>>>        };
>>>>>
>>>>>        add(rows);
>>>>>    }
>>>>>
>>>>>    private void readObject(ObjectInputStream in) throws IOException,
>>>>> ClassNotFoundException
>>>>>    {
>>>>>        System.out.println("READ OBJECT " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>        in.defaultReadObject();
>>>>>    }
>>>>>
>>>>>    private void writeObject(ObjectOutputStream out) throws
>>>>> IOException, ClassNotFoundException
>>>>>    {
>>>>>        System.out.println("WRITE OBJECT " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>        out.defaultWriteObject();
>>>>>    }
>>>>>
>>>>>    @Override
>>>>>    public void detachModels()
>>>>>    {
>>>>>        System.out.println("DETACH MODELS " +
>>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>>        super.detachModels();
>>>>>    }
>>>>> }
>>>>>
>>>>> with the TestPage.html
>>>>>
>>>>> <?xml version="1.0" encoding="UTF-8" ?>
>>>>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>>>>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>>>>> <html xmlns="http://www.w3.org/1999/xhtml"
>>>>> xmlns:wicket="http://wicket.apache.org/"
>>>>>    xml:lang="en" lang="en">
>>>>> <head>
>>>>> </head>
>>>>> <body>
>>>>>   <div wicket:id="rows">
>>>>>         <a wicket:id="link"><label wicket:id="label"></label></a>
>>>>>       <!--<label wicket:id="label"></label>    -->
>>>>>   </div>
>>>>> </body>
>>>>> </html>
>>>>>
>>>>> I understand that I could use a ListModel, and not worry about this
>>>>> detaching behaviour, as the list would be a serializable, but I'd like
>>>>> the list model to always be up to date from the database on page
>>>>> refresh, which wouldn't be the case for a ListModel. (it would always
>>>>> have the same values as it did on the initial page render)
>>>>>
>>>>> I am using wicket 1.5.3 and firefox 8.0.
>>>>>
>>>>> Thanks.
>>>>>
>>>>> Phil.
>>>>>
>>>>> ---------------------------------------------------------------------
>>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>>
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>

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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Sven Meier <sv...@meiers.net>.
 >you can ... give each item an isolated model

Actually ListItemModel doesn't detach the owning list's model, so it's 
'isolated' already.

For your AjaxLink you can access the list view instead:

        IModel<List<String>>  model = ... // not final !!

        final ListView<String>  rows = new ListView<String>("rows", model)
        {
            @Override
            protected void populateItem(final ListItem<String>  item)
            {
                AjaxLink<Void>  link = new AjaxLink<Void>("link")
                    {
                        @Override
                        public void onClick(AjaxRequestTarget target)
                        {
                            rows.getModelObject().remove(item.getIndex()-1);
                            target.add(TestPage.this);
                         }
                    };
                link.add(new Label("label", item.getModel()));
                item.add(link);
            }
        };

Sven

On 01/09/2012 05:13 PM, Igor Vaynberg wrote:
> the problem here is that the listitem's model is tied to the
> listview's model. so calling detach() on the listview item causes the
> detachment of the listview's model.
>
> you can either override getlistitemmodel() on the listview and give
> each item an isolated model, or you can use a dataview or another
> non-listview repeater.
>
> as far as the many calls to detach(), they are nearly impossible to
> avoid without making the framework much more complicated. in practice
> the logic in detach() is very simple so having multiple calls to it is
> not a problem.
>
> -igor
>
> On Mon, Jan 9, 2012 at 5:06 AM, Philip Wilkinson
> <wi...@gmail.com>  wrote:
>> Sven,
>>
>> Thanks for your speedy reply.
>>
>> Indeed you are correct, removing the model from the AjaxLink stops the
>> model from becoming detached immediately before rendering.
>>
>> I now realise that my cut-down example was too cut-down! The AjaxLink
>> is actually there to remove the entry from the list, requiring the
>> list model to be accessible to the link. So the implementation would
>> be something like:-
>>
>> public class TestPage extends WebPage
>> {
>>     private List<String>  testList = new ArrayList<String>() {{
>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>
>>     public TestPage()
>>     {
>>         setOutputMarkupId(true);
>>         final IModel<List<String>>  model = new
>> LoadableDetachableModel<List<String>>()
>>                 {
>>                     @Override
>>                     protected List<String>  load()
>>                     {
>>                         System.out.println("LOAD LIST MODEL " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>                         return testList;
>>                     }
>>
>>                     @Override
>>                     public void detach()
>>                     {
>>                         System.out.println("DETACH LIST MODEL " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>                         super.detach();
>>                     }
>>                 };
>>
>>         final ListView<String>  rows = new ListView<String>("rows", model)
>>         {
>>             @Override
>>             protected void populateItem(final ListItem<String>  item)
>>             {
>>                 System.out.println("populateItem " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>
>>                 // with ajax link to remove entry.
>>                 AjaxLink<List<String>>  link = new
>> AjaxLink<List<String>>("link", model)
>>                     {
>>                         @Override
>>                         public void onClick(AjaxRequestTarget target)
>>                         {
>>                             getModelObject().remove(item.getIndex()-1);
>>                             target.add(TestPage.this); // normally the
>> target would be a containing panel, but for this example, the page
>> will do!
>>                         }
>>                     };
>>                 link.add(new Label("label", item.getModel()));
>>                 item.add(link);
>>             }
>>         };
>>
>>         add(rows);
>>     }
>>
>>     @Override
>>     public void detachModels()
>>     {
>>         System.out.println("DETACH MODELS " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>         super.detachModels();
>>     }
>> }
>>
>> Possibly I could get around this by not setting the model on the
>> AjaxLink, so wicket doesn't try to detach it, and passing the list
>> model in separately. But that feels like I'm not following the "wicket
>> way" if I do something like that :(. I think that knowing what the
>> AjaxLink is doing, removing an entry from the list, the most
>> appropriate model for the AjaxLink is the list model!
>>
>> Do you think there's a case here for the wicket framework being a
>> little smarter about when it detaches its models?
>> I think that only the relevant models need to be detached only once
>> after every response has been rendered. So that they can be lazy
>> loaded again when required during the next response \ render cycle.
>>
>> Regards,
>>
>> Phil.
>>
>>
>> On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier<sv...@meiers.net>  wrote:
>>> Hi Philip,
>>>
>>> ListView throws away all its children before rendering. When a component is
>>> removed, its model gets detached.
>>> Your AjaxLink has your LoadableDetachableModel as model, thus it forces it
>>> to be detached too.
>>>
>>> As a possible solution, don't keep a reference to the model as your
>>> AjaxLink's model:
>>>                 AjaxLink<Void>  link = new AjaxLink<Void>("link")
>>>
>>> Hope this helps
>>> Sven
>>>
>>>
>>> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>>> Hi,
>>>>
>>>> Can anyone tell me why when I refresh a wicket page which has on it an
>>>> AjaxLink, my list model is being detached just
>>>>
>>>> before its contents are rendered by a list view?
>>>>
>>>> I would expect detach only to be called after everything has been
>>>> rendered.
>>>>
>>>> Detaching just before rendering would call a potentially 2nd
>>>> unnecessary expensive database operation to load the
>>>>
>>>> model.
>>>>
>>>> I have an Ajax‌Link call back which seems to be creating this
>>>> behaviour. Without it the models are detached afer
>>>>
>>>> rendering as expected.
>>>>
>>>> E.g. for the attached simple test page, pressing refresh in the
>>>> browser, when there is no AjaxLink I get the following
>>>>
>>>> diagnostic output from Testpage.java...
>>>>
>>>> LOAD LIST MODEL 79e14338
>>>> populateItem 79e14338
>>>> populateItem 79e14338
>>>> populateItem 79e14338
>>>> DETACH MODELS 79e14338
>>>> DETACH LIST MODEL 79e14338
>>>> DETACH MODELS 79e14338
>>>> DETACH LIST MODEL 79e14338
>>>>
>>>> When I surround the label with an ajax link, pressing refresh in the
>>>> browser, I get..
>>>>
>>>> LOAD LIST MODEL 62b512e7
>>>> DETACH LIST MODEL 62b512e7 //1st ????
>>>> DETACH LIST MODEL 62b512e7 //2nd ????
>>>> DETACH LIST MODEL 62b512e7 //3rd ????
>>>> populateItem 62b512e7 // render "Aaaaa"
>>>> populateItem 62b512e7 // render "Bbbbb"
>>>> populateItem 62b512e7 // render "Ccccc"
>>>> LOAD LIST MODEL 62b512e7
>>>> DETACH MODELS 62b512e7
>>>> DETACH LIST MODEL 62b512e7 //4th
>>>> DETACH LIST MODEL 62b512e7 //5th
>>>> DETACH LIST MODEL 62b512e7 //6th
>>>> DETACH LIST MODEL 62b512e7 //7th
>>>> DETACH MODELS 62b512e7
>>>> DETACH LIST MODEL 62b512e7 //8th
>>>> DETACH LIST MODEL 62b512e7 //9th
>>>> DETACH LIST MODEL 62b512e7 //10th
>>>> DETACH LIST MODEL 62b512e7 //11th
>>>> WRITE OBJECT 62b512e7
>>>>
>>>> .. 11 calls to .detach() on the list model!
>>>> between the 3rd and 4th detach list model the list is actually
>>>> rendered. Had this been for real this would have ment
>>>>
>>>> that the list model would have been loaded again at this point.
>>>> Here's the detail stack traces of where these 11 detach list models
>>>> are being called from when refresh on my test page with
>>>> the AjaxLink...
>>>>
>>>> *** 1st DETACH LIST MODEL ***
>>>> TestPage$2.detach() line: 39
>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>> TestPage$3$1(Component).detach() line: 1154
>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>> ListItem<T>(Component).detach() line: 1161
>>>> TestPage$3(MarkupContainer).removeAll() line: 672
>>>> TestPage$3(ListView<T>).onPopulate() line: 498
>>>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>>>> TestPage$3(Component).internalBeforeRender() line: 981
>>>> TestPage$3(Component).beforeRender() line: 1015
>>>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>>>> TestPage(Component).onBeforeRender() line: 3774
>>>> TestPage(Page).onBeforeRender() line: 823
>>>> TestPage(Component).internalBeforeRender() line: 981
>>>> TestPage(Component).beforeRender() line: 1015
>>>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>>>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>>>> TestPage(Component).render() line: 2271
>>>> TestPage(Page).renderPage() line: 1035
>>>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>>>> WebPageRenderer.respond(RequestCycle) line: 182
>>>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>>>
>>>> *** 2nd DETACH LIST MODEL ***
>>>> Same as before.
>>>>
>>>> *** 3rd DETACH LIST MODEL ***
>>>> Same as before.
>>>>
>>>> *** Now populateItem is called 3 times on the list view to render the
>>>> 3 list elements...
>>>>
>>>> *** 4th DETACH LIST MODEL ***
>>>> TestPage$2.detach() line: 39
>>>> TestPage$3(Component).detachModel() line: 3536
>>>> TestPage$3(Component).detachModels() line: 1203
>>>> TestPage$3(Component).detach() line: 1154
>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>> TestPage(Component).detach() line: 1161
>>>> PageProvider.detach() line: 318
>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>> RequestCycle.onDetach() line: 565
>>>> RequestCycle.detach() line: 508
>>>> RequestCycle.processRequestAndDetach() line: 284
>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>> FilterChain) line: 162
>>>>
>>>> *** 5th DETACH LIST MODEL ***
>>>> TestPage$2.detach() line: 39
>>>> TestPage$3$1(Component).detachModel() line: 3536
>>>> TestPage$3$1(Component).detachModels() line: 1203
>>>> TestPage$3$1(Component).detach() line: 1154
>>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>>> ListItem<T>(Component).detach() line: 1161
>>>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>>>> TestPage$3(Component).detach() line: 1161
>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>> TestPage(Component).detach() line: 1161
>>>> PageProvider.detach() line: 318
>>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>>> RequestCycle.onDetach() line: 565
>>>> RequestCycle.detach() line: 508
>>>> RequestCycle.processRequestAndDetach() line: 284
>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>> FilterChain) line: 162
>>>>
>>>> *** 6th DETACH LIST MODEL ***
>>>> Same as before
>>>>
>>>> *** 7th DETACH LIST MODEL ***
>>>> Same as before
>>>>
>>>> *** 8th DETACH LIST MODEL ***
>>>> TestPage$2.detach() line: 39
>>>> TestPage$3(Component).detachModel() line: 3536
>>>> TestPage$3(Component).detachModels() line: 1203
>>>> TestPage$3(Component).detach() line: 1154
>>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>>> TestPage(Component).detach() line: 1161
>>>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>>>> line: 156
>>>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>>>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
>>>> PageAccessSynchronizer$2.commitRequest() line: 281
>>>> Application$2.onDetach(RequestCycle) line: 1588
>>>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
>>>> RequestCycleListenerCollection$3.notify(Object) line: 97
>>>> ListenerCollection$1.notify(T) line: 119
>>>>
>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>>>> line: 143
>>>>
>>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>>>> line: 113
>>>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>>>> RequestCycle.onDetach() line: 569
>>>> RequestCycle.detach() line: 508
>>>> RequestCycle.processRequestAndDetach() line: 284
>>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>>> FilterChain) line: 162
>>>>
>>>> *** 9th DETACH LIST MODEL ***
>>>> Same as before
>>>>
>>>> *** 10th DETACH LIST MODEL ***
>>>> Same as before
>>>>
>>>> *** 11th DETACH LIST MODEL ***
>>>> Same as before
>>>>
>>>> To show this behaviour I've created the following test page.
>>>> TestPage.java
>>>>
>>>> public class TestPage extends WebPage
>>>> {
>>>>     private static List<String>    testList = new ArrayList<String>() {{
>>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>>
>>>>     public TestPage()
>>>>     {
>>>>         final IModel<List<String>>    model = new
>>>> LoadableDetachableModel<List<String>>()
>>>>                 {
>>>>                     @Override
>>>>                     protected List<String>    load()
>>>>                     {
>>>>                         System.out.println("LOAD LIST MODEL " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>                         return testList; // would normally get list
>>>> from db, in this case just retun static list.
>>>>                     }
>>>>
>>>>                     @Override
>>>>                     public void detach()
>>>>                     {
>>>>                         System.out.println("DETACH LIST MODEL " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>                         super.detach(); // detach model. "heavyweight"
>>>> model no longer needed.
>>>>                     }
>>>>                 };
>>>>
>>>>         final ListView<String>    rows = new ListView<String>("rows", model)
>>>>         {
>>>>             @Override
>>>>             protected void populateItem(ListItem<String>    item)
>>>>             {
>>>>                 System.out.println("populateItem " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>
>>>>                 // with ajax link.
>>>>                 AjaxLink<List<String>>    link = new
>>>> AjaxLink<List<String>>("link", model)
>>>>                     {
>>>>                         @Override
>>>>                         public void onClick(AjaxRequestTarget target)
>>>>                         {
>>>>                             // Do nothing.
>>>>                         }
>>>>                     };
>>>>                 link.add(new Label("label", item.getModel()));
>>>>                 item.add(link);
>>>>
>>>> //                item.add(new Label("label", item.getModel())); // no
>>>> ajax link.
>>>>
>>>>             }
>>>>         };
>>>>
>>>>         add(rows);
>>>>     }
>>>>
>>>>     private void readObject(ObjectInputStream in) throws IOException,
>>>> ClassNotFoundException
>>>>     {
>>>>         System.out.println("READ OBJECT " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>         in.defaultReadObject();
>>>>     }
>>>>
>>>>     private void writeObject(ObjectOutputStream out) throws
>>>> IOException, ClassNotFoundException
>>>>     {
>>>>         System.out.println("WRITE OBJECT " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>         out.defaultWriteObject();
>>>>     }
>>>>
>>>>     @Override
>>>>     public void detachModels()
>>>>     {
>>>>         System.out.println("DETACH MODELS " +
>>>> Integer.toHexString(TestPage.this.hashCode()));
>>>>         super.detachModels();
>>>>     }
>>>> }
>>>>
>>>> with the TestPage.html
>>>>
>>>> <?xml version="1.0" encoding="UTF-8" ?>
>>>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>>>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>>>> <html xmlns="http://www.w3.org/1999/xhtml"
>>>> xmlns:wicket="http://wicket.apache.org/"
>>>>     xml:lang="en" lang="en">
>>>> <head>
>>>> </head>
>>>> <body>
>>>>    <div wicket:id="rows">
>>>>          <a wicket:id="link"><label wicket:id="label"></label></a>
>>>>        <!--<label wicket:id="label"></label>    -->
>>>>    </div>
>>>> </body>
>>>> </html>
>>>>
>>>> I understand that I could use a ListModel, and not worry about this
>>>> detaching behaviour, as the list would be a serializable, but I'd like
>>>> the list model to always be up to date from the database on page
>>>> refresh, which wouldn't be the case for a ListModel. (it would always
>>>> have the same values as it did on the initial page render)
>>>>
>>>> I am using wicket 1.5.3 and firefox 8.0.
>>>>
>>>> Thanks.
>>>>
>>>> Phil.
>>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>


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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Igor Vaynberg <ig...@gmail.com>.
the problem here is that the listitem's model is tied to the
listview's model. so calling detach() on the listview item causes the
detachment of the listview's model.

you can either override getlistitemmodel() on the listview and give
each item an isolated model, or you can use a dataview or another
non-listview repeater.

as far as the many calls to detach(), they are nearly impossible to
avoid without making the framework much more complicated. in practice
the logic in detach() is very simple so having multiple calls to it is
not a problem.

-igor

On Mon, Jan 9, 2012 at 5:06 AM, Philip Wilkinson
<wi...@gmail.com> wrote:
> Sven,
>
> Thanks for your speedy reply.
>
> Indeed you are correct, removing the model from the AjaxLink stops the
> model from becoming detached immediately before rendering.
>
> I now realise that my cut-down example was too cut-down! The AjaxLink
> is actually there to remove the entry from the list, requiring the
> list model to be accessible to the link. So the implementation would
> be something like:-
>
> public class TestPage extends WebPage
> {
>    private List<String> testList = new ArrayList<String>() {{
> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>
>    public TestPage()
>    {
>        setOutputMarkupId(true);
>        final IModel<List<String>> model = new
> LoadableDetachableModel<List<String>>()
>                {
>                    @Override
>                    protected List<String> load()
>                    {
>                        System.out.println("LOAD LIST MODEL " +
> Integer.toHexString(TestPage.this.hashCode()));
>                        return testList;
>                    }
>
>                    @Override
>                    public void detach()
>                    {
>                        System.out.println("DETACH LIST MODEL " +
> Integer.toHexString(TestPage.this.hashCode()));
>                        super.detach();
>                    }
>                };
>
>        final ListView<String> rows = new ListView<String>("rows", model)
>        {
>            @Override
>            protected void populateItem(final ListItem<String> item)
>            {
>                System.out.println("populateItem " +
> Integer.toHexString(TestPage.this.hashCode()));
>
>                // with ajax link to remove entry.
>                AjaxLink<List<String>> link = new
> AjaxLink<List<String>>("link", model)
>                    {
>                        @Override
>                        public void onClick(AjaxRequestTarget target)
>                        {
>                            getModelObject().remove(item.getIndex()-1);
>                            target.add(TestPage.this); // normally the
> target would be a containing panel, but for this example, the page
> will do!
>                        }
>                    };
>                link.add(new Label("label", item.getModel()));
>                item.add(link);
>            }
>        };
>
>        add(rows);
>    }
>
>    @Override
>    public void detachModels()
>    {
>        System.out.println("DETACH MODELS " +
> Integer.toHexString(TestPage.this.hashCode()));
>        super.detachModels();
>    }
> }
>
> Possibly I could get around this by not setting the model on the
> AjaxLink, so wicket doesn't try to detach it, and passing the list
> model in separately. But that feels like I'm not following the "wicket
> way" if I do something like that :(. I think that knowing what the
> AjaxLink is doing, removing an entry from the list, the most
> appropriate model for the AjaxLink is the list model!
>
> Do you think there's a case here for the wicket framework being a
> little smarter about when it detaches its models?
> I think that only the relevant models need to be detached only once
> after every response has been rendered. So that they can be lazy
> loaded again when required during the next response \ render cycle.
>
> Regards,
>
> Phil.
>
>
> On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier <sv...@meiers.net> wrote:
>> Hi Philip,
>>
>> ListView throws away all its children before rendering. When a component is
>> removed, its model gets detached.
>> Your AjaxLink has your LoadableDetachableModel as model, thus it forces it
>> to be detached too.
>>
>> As a possible solution, don't keep a reference to the model as your
>> AjaxLink's model:
>>                AjaxLink<Void> link = new AjaxLink<Void>("link")
>>
>> Hope this helps
>> Sven
>>
>>
>> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>>
>>> Hi,
>>>
>>> Can anyone tell me why when I refresh a wicket page which has on it an
>>> AjaxLink, my list model is being detached just
>>>
>>> before its contents are rendered by a list view?
>>>
>>> I would expect detach only to be called after everything has been
>>> rendered.
>>>
>>> Detaching just before rendering would call a potentially 2nd
>>> unnecessary expensive database operation to load the
>>>
>>> model.
>>>
>>> I have an Ajax‌Link call back which seems to be creating this
>>> behaviour. Without it the models are detached afer
>>>
>>> rendering as expected.
>>>
>>> E.g. for the attached simple test page, pressing refresh in the
>>> browser, when there is no AjaxLink I get the following
>>>
>>> diagnostic output from Testpage.java...
>>>
>>> LOAD LIST MODEL 79e14338
>>> populateItem 79e14338
>>> populateItem 79e14338
>>> populateItem 79e14338
>>> DETACH MODELS 79e14338
>>> DETACH LIST MODEL 79e14338
>>> DETACH MODELS 79e14338
>>> DETACH LIST MODEL 79e14338
>>>
>>> When I surround the label with an ajax link, pressing refresh in the
>>> browser, I get..
>>>
>>> LOAD LIST MODEL 62b512e7
>>> DETACH LIST MODEL 62b512e7 //1st ????
>>> DETACH LIST MODEL 62b512e7 //2nd ????
>>> DETACH LIST MODEL 62b512e7 //3rd ????
>>> populateItem 62b512e7 // render "Aaaaa"
>>> populateItem 62b512e7 // render "Bbbbb"
>>> populateItem 62b512e7 // render "Ccccc"
>>> LOAD LIST MODEL 62b512e7
>>> DETACH MODELS 62b512e7
>>> DETACH LIST MODEL 62b512e7 //4th
>>> DETACH LIST MODEL 62b512e7 //5th
>>> DETACH LIST MODEL 62b512e7 //6th
>>> DETACH LIST MODEL 62b512e7 //7th
>>> DETACH MODELS 62b512e7
>>> DETACH LIST MODEL 62b512e7 //8th
>>> DETACH LIST MODEL 62b512e7 //9th
>>> DETACH LIST MODEL 62b512e7 //10th
>>> DETACH LIST MODEL 62b512e7 //11th
>>> WRITE OBJECT 62b512e7
>>>
>>> .. 11 calls to .detach() on the list model!
>>> between the 3rd and 4th detach list model the list is actually
>>> rendered. Had this been for real this would have ment
>>>
>>> that the list model would have been loaded again at this point.
>>> Here's the detail stack traces of where these 11 detach list models
>>> are being called from when refresh on my test page with
>>> the AjaxLink...
>>>
>>> *** 1st DETACH LIST MODEL ***
>>> TestPage$2.detach() line: 39
>>> TestPage$3$1(Component).detachModel() line: 3536
>>> TestPage$3$1(Component).detachModels() line: 1203
>>> TestPage$3$1(Component).detach() line: 1154
>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>> ListItem<T>(Component).detach() line: 1161
>>> TestPage$3(MarkupContainer).removeAll() line: 672
>>> TestPage$3(ListView<T>).onPopulate() line: 498
>>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>>> TestPage$3(Component).internalBeforeRender() line: 981
>>> TestPage$3(Component).beforeRender() line: 1015
>>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>>> TestPage(Component).onBeforeRender() line: 3774
>>> TestPage(Page).onBeforeRender() line: 823
>>> TestPage(Component).internalBeforeRender() line: 981
>>> TestPage(Component).beforeRender() line: 1015
>>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>>> TestPage(Component).render() line: 2271
>>> TestPage(Page).renderPage() line: 1035
>>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>>> WebPageRenderer.respond(RequestCycle) line: 182
>>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>>
>>> *** 2nd DETACH LIST MODEL ***
>>> Same as before.
>>>
>>> *** 3rd DETACH LIST MODEL ***
>>> Same as before.
>>>
>>> *** Now populateItem is called 3 times on the list view to render the
>>> 3 list elements...
>>>
>>> *** 4th DETACH LIST MODEL ***
>>> TestPage$2.detach() line: 39
>>> TestPage$3(Component).detachModel() line: 3536
>>> TestPage$3(Component).detachModels() line: 1203
>>> TestPage$3(Component).detach() line: 1154
>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>> TestPage(Component).detach() line: 1161
>>> PageProvider.detach() line: 318
>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>> RequestCycle.onDetach() line: 565
>>> RequestCycle.detach() line: 508
>>> RequestCycle.processRequestAndDetach() line: 284
>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>> FilterChain) line: 162
>>>
>>> *** 5th DETACH LIST MODEL ***
>>> TestPage$2.detach() line: 39
>>> TestPage$3$1(Component).detachModel() line: 3536
>>> TestPage$3$1(Component).detachModels() line: 1203
>>> TestPage$3$1(Component).detach() line: 1154
>>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>>> ListItem<T>(Component).detach() line: 1161
>>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>>> TestPage$3(Component).detach() line: 1161
>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>> TestPage(Component).detach() line: 1161
>>> PageProvider.detach() line: 318
>>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>>> RequestCycle.onDetach() line: 565
>>> RequestCycle.detach() line: 508
>>> RequestCycle.processRequestAndDetach() line: 284
>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>> FilterChain) line: 162
>>>
>>> *** 6th DETACH LIST MODEL ***
>>> Same as before
>>>
>>> *** 7th DETACH LIST MODEL ***
>>> Same as before
>>>
>>> *** 8th DETACH LIST MODEL ***
>>> TestPage$2.detach() line: 39
>>> TestPage$3(Component).detachModel() line: 3536
>>> TestPage$3(Component).detachModels() line: 1203
>>> TestPage$3(Component).detach() line: 1154
>>> TestPage(MarkupContainer).detachChildren() line: 1710
>>> TestPage(Component).detach() line: 1161
>>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>>> line: 156
>>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
>>> PageAccessSynchronizer$2.commitRequest() line: 281
>>> Application$2.onDetach(RequestCycle) line: 1588
>>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
>>> RequestCycleListenerCollection$3.notify(Object) line: 97
>>> ListenerCollection$1.notify(T) line: 119
>>>
>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>>> line: 143
>>>
>>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>>> line: 113
>>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>>> RequestCycle.onDetach() line: 569
>>> RequestCycle.detach() line: 508
>>> RequestCycle.processRequestAndDetach() line: 284
>>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>>> FilterChain) line: 162
>>>
>>> *** 9th DETACH LIST MODEL ***
>>> Same as before
>>>
>>> *** 10th DETACH LIST MODEL ***
>>> Same as before
>>>
>>> *** 11th DETACH LIST MODEL ***
>>> Same as before
>>>
>>> To show this behaviour I've created the following test page.
>>> TestPage.java
>>>
>>> public class TestPage extends WebPage
>>> {
>>>    private static List<String>  testList = new ArrayList<String>() {{
>>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>>
>>>    public TestPage()
>>>    {
>>>        final IModel<List<String>>  model = new
>>> LoadableDetachableModel<List<String>>()
>>>                {
>>>                    @Override
>>>                    protected List<String>  load()
>>>                    {
>>>                        System.out.println("LOAD LIST MODEL " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>                        return testList; // would normally get list
>>> from db, in this case just retun static list.
>>>                    }
>>>
>>>                    @Override
>>>                    public void detach()
>>>                    {
>>>                        System.out.println("DETACH LIST MODEL " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>                        super.detach(); // detach model. "heavyweight"
>>> model no longer needed.
>>>                    }
>>>                };
>>>
>>>        final ListView<String>  rows = new ListView<String>("rows", model)
>>>        {
>>>            @Override
>>>            protected void populateItem(ListItem<String>  item)
>>>            {
>>>                System.out.println("populateItem " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>
>>>                // with ajax link.
>>>                AjaxLink<List<String>>  link = new
>>> AjaxLink<List<String>>("link", model)
>>>                    {
>>>                        @Override
>>>                        public void onClick(AjaxRequestTarget target)
>>>                        {
>>>                            // Do nothing.
>>>                        }
>>>                    };
>>>                link.add(new Label("label", item.getModel()));
>>>                item.add(link);
>>>
>>> //                item.add(new Label("label", item.getModel())); // no
>>> ajax link.
>>>
>>>            }
>>>        };
>>>
>>>        add(rows);
>>>    }
>>>
>>>    private void readObject(ObjectInputStream in) throws IOException,
>>> ClassNotFoundException
>>>    {
>>>        System.out.println("READ OBJECT " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>        in.defaultReadObject();
>>>    }
>>>
>>>    private void writeObject(ObjectOutputStream out) throws
>>> IOException, ClassNotFoundException
>>>    {
>>>        System.out.println("WRITE OBJECT " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>        out.defaultWriteObject();
>>>    }
>>>
>>>    @Override
>>>    public void detachModels()
>>>    {
>>>        System.out.println("DETACH MODELS " +
>>> Integer.toHexString(TestPage.this.hashCode()));
>>>        super.detachModels();
>>>    }
>>> }
>>>
>>> with the TestPage.html
>>>
>>> <?xml version="1.0" encoding="UTF-8" ?>
>>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>>> <html xmlns="http://www.w3.org/1999/xhtml"
>>> xmlns:wicket="http://wicket.apache.org/"
>>>    xml:lang="en" lang="en">
>>> <head>
>>> </head>
>>> <body>
>>>   <div wicket:id="rows">
>>>         <a wicket:id="link"><label wicket:id="label"></label></a>
>>>       <!--<label wicket:id="label"></label>  -->
>>>   </div>
>>> </body>
>>> </html>
>>>
>>> I understand that I could use a ListModel, and not worry about this
>>> detaching behaviour, as the list would be a serializable, but I'd like
>>> the list model to always be up to date from the database on page
>>> refresh, which wouldn't be the case for a ListModel. (it would always
>>> have the same values as it did on the initial page render)
>>>
>>> I am using wicket 1.5.3 and firefox 8.0.
>>>
>>> Thanks.
>>>
>>> Phil.
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>>> For additional commands, e-mail: users-help@wicket.apache.org
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>

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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Philip Wilkinson <wi...@gmail.com>.
Sven,

Thanks for your speedy reply.

Indeed you are correct, removing the model from the AjaxLink stops the
model from becoming detached immediately before rendering.

I now realise that my cut-down example was too cut-down! The AjaxLink
is actually there to remove the entry from the list, requiring the
list model to be accessible to the link. So the implementation would
be something like:-

public class TestPage extends WebPage
{
    private List<String> testList = new ArrayList<String>() {{
add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};

    public TestPage()
    {
        setOutputMarkupId(true);
        final IModel<List<String>> model = new
LoadableDetachableModel<List<String>>()
                {
                    @Override
                    protected List<String> load()
                    {
                        System.out.println("LOAD LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                        return testList;
                    }

                    @Override
                    public void detach()
                    {
                        System.out.println("DETACH LIST MODEL " +
Integer.toHexString(TestPage.this.hashCode()));
                        super.detach();
                    }
                };

        final ListView<String> rows = new ListView<String>("rows", model)
        {
            @Override
            protected void populateItem(final ListItem<String> item)
            {
                System.out.println("populateItem " +
Integer.toHexString(TestPage.this.hashCode()));

                // with ajax link to remove entry.
                AjaxLink<List<String>> link = new
AjaxLink<List<String>>("link", model)
                    {
                        @Override
                        public void onClick(AjaxRequestTarget target)
                        {
                            getModelObject().remove(item.getIndex()-1);
                            target.add(TestPage.this); // normally the
target would be a containing panel, but for this example, the page
will do!
                        }
                    };
                link.add(new Label("label", item.getModel()));
                item.add(link);
            }
        };

        add(rows);
    }

    @Override
    public void detachModels()
    {
        System.out.println("DETACH MODELS " +
Integer.toHexString(TestPage.this.hashCode()));
        super.detachModels();
    }
}

Possibly I could get around this by not setting the model on the
AjaxLink, so wicket doesn't try to detach it, and passing the list
model in separately. But that feels like I'm not following the "wicket
way" if I do something like that :(. I think that knowing what the
AjaxLink is doing, removing an entry from the list, the most
appropriate model for the AjaxLink is the list model!

Do you think there's a case here for the wicket framework being a
little smarter about when it detaches its models?
I think that only the relevant models need to be detached only once
after every response has been rendered. So that they can be lazy
loaded again when required during the next response \ render cycle.

Regards,

Phil.


On Mon, Jan 9, 2012 at 8:03 AM, Sven Meier <sv...@meiers.net> wrote:
> Hi Philip,
>
> ListView throws away all its children before rendering. When a component is
> removed, its model gets detached.
> Your AjaxLink has your LoadableDetachableModel as model, thus it forces it
> to be detached too.
>
> As a possible solution, don't keep a reference to the model as your
> AjaxLink's model:
>                AjaxLink<Void> link = new AjaxLink<Void>("link")
>
> Hope this helps
> Sven
>
>
> On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
>>
>> Hi,
>>
>> Can anyone tell me why when I refresh a wicket page which has on it an
>> AjaxLink, my list model is being detached just
>>
>> before its contents are rendered by a list view?
>>
>> I would expect detach only to be called after everything has been
>> rendered.
>>
>> Detaching just before rendering would call a potentially 2nd
>> unnecessary expensive database operation to load the
>>
>> model.
>>
>> I have an Ajax‌Link call back which seems to be creating this
>> behaviour. Without it the models are detached afer
>>
>> rendering as expected.
>>
>> E.g. for the attached simple test page, pressing refresh in the
>> browser, when there is no AjaxLink I get the following
>>
>> diagnostic output from Testpage.java...
>>
>> LOAD LIST MODEL 79e14338
>> populateItem 79e14338
>> populateItem 79e14338
>> populateItem 79e14338
>> DETACH MODELS 79e14338
>> DETACH LIST MODEL 79e14338
>> DETACH MODELS 79e14338
>> DETACH LIST MODEL 79e14338
>>
>> When I surround the label with an ajax link, pressing refresh in the
>> browser, I get..
>>
>> LOAD LIST MODEL 62b512e7
>> DETACH LIST MODEL 62b512e7 //1st ????
>> DETACH LIST MODEL 62b512e7 //2nd ????
>> DETACH LIST MODEL 62b512e7 //3rd ????
>> populateItem 62b512e7 // render "Aaaaa"
>> populateItem 62b512e7 // render "Bbbbb"
>> populateItem 62b512e7 // render "Ccccc"
>> LOAD LIST MODEL 62b512e7
>> DETACH MODELS 62b512e7
>> DETACH LIST MODEL 62b512e7 //4th
>> DETACH LIST MODEL 62b512e7 //5th
>> DETACH LIST MODEL 62b512e7 //6th
>> DETACH LIST MODEL 62b512e7 //7th
>> DETACH MODELS 62b512e7
>> DETACH LIST MODEL 62b512e7 //8th
>> DETACH LIST MODEL 62b512e7 //9th
>> DETACH LIST MODEL 62b512e7 //10th
>> DETACH LIST MODEL 62b512e7 //11th
>> WRITE OBJECT 62b512e7
>>
>> .. 11 calls to .detach() on the list model!
>> between the 3rd and 4th detach list model the list is actually
>> rendered. Had this been for real this would have ment
>>
>> that the list model would have been loaded again at this point.
>> Here's the detail stack traces of where these 11 detach list models
>> are being called from when refresh on my test page with
>> the AjaxLink...
>>
>> *** 1st DETACH LIST MODEL ***
>> TestPage$2.detach() line: 39
>> TestPage$3$1(Component).detachModel() line: 3536
>> TestPage$3$1(Component).detachModels() line: 1203
>> TestPage$3$1(Component).detach() line: 1154
>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>> ListItem<T>(Component).detach() line: 1161
>> TestPage$3(MarkupContainer).removeAll() line: 672
>> TestPage$3(ListView<T>).onPopulate() line: 498
>> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
>> TestPage$3(Component).internalBeforeRender() line: 981
>> TestPage$3(Component).beforeRender() line: 1015
>> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
>> TestPage(Component).onBeforeRender() line: 3774
>> TestPage(Page).onBeforeRender() line: 823
>> TestPage(Component).internalBeforeRender() line: 981
>> TestPage(Component).beforeRender() line: 1015
>> TestPage(Component).internalPrepareForRender(boolean) line: 2184
>> TestPage(Page).internalPrepareForRender(boolean) line: 280
>> TestPage(Component).render() line: 2271
>> TestPage(Page).renderPage() line: 1035
>> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
>> WebPageRenderer.respond(RequestCycle) line: 182
>> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>>
>> *** 2nd DETACH LIST MODEL ***
>> Same as before.
>>
>> *** 3rd DETACH LIST MODEL ***
>> Same as before.
>>
>> *** Now populateItem is called 3 times on the list view to render the
>> 3 list elements...
>>
>> *** 4th DETACH LIST MODEL ***
>> TestPage$2.detach() line: 39
>> TestPage$3(Component).detachModel() line: 3536
>> TestPage$3(Component).detachModels() line: 1203
>> TestPage$3(Component).detach() line: 1154
>> TestPage(MarkupContainer).detachChildren() line: 1710
>> TestPage(Component).detach() line: 1161
>> PageProvider.detach() line: 318
>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>> RequestCycle.onDetach() line: 565
>> RequestCycle.detach() line: 508
>> RequestCycle.processRequestAndDetach() line: 284
>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>> FilterChain) line: 162
>>
>> *** 5th DETACH LIST MODEL ***
>> TestPage$2.detach() line: 39
>> TestPage$3$1(Component).detachModel() line: 3536
>> TestPage$3$1(Component).detachModels() line: 1203
>> TestPage$3$1(Component).detach() line: 1154
>> ListItem<T>(MarkupContainer).detachChildren() line: 1710
>> ListItem<T>(Component).detach() line: 1161
>> TestPage$3(MarkupContainer).detachChildren() line: 1710
>> TestPage$3(Component).detach() line: 1161
>> TestPage(MarkupContainer).detachChildren() line: 1710
>> TestPage(Component).detach() line: 1161
>> PageProvider.detach() line: 318
>> RenderPageRequestHandler.detach(IRequestCycle) line: 148
>> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
>> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
>> RequestCycle.onDetach() line: 565
>> RequestCycle.detach() line: 508
>> RequestCycle.processRequestAndDetach() line: 284
>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>> FilterChain) line: 162
>>
>> *** 6th DETACH LIST MODEL ***
>> Same as before
>>
>> *** 7th DETACH LIST MODEL ***
>> Same as before
>>
>> *** 8th DETACH LIST MODEL ***
>> TestPage$2.detach() line: 39
>> TestPage$3(Component).detachModel() line: 3536
>> TestPage$3(Component).detachModels() line: 1203
>> TestPage$3(Component).detach() line: 1154
>> TestPage(MarkupContainer).detachChildren() line: 1710
>> TestPage(Component).detach() line: 1161
>> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
>> line: 156
>> PageStoreManager(AbstractPageManager).commitRequest() line: 94
>> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
>> PageAccessSynchronizer$2.commitRequest() line: 281
>> Application$2.onDetach(RequestCycle) line: 1588
>> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
>> RequestCycleListenerCollection$3.notify(Object) line: 97
>> ListenerCollection$1.notify(T) line: 119
>>
>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
>> line: 143
>>
>> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
>> line: 113
>> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
>> RequestCycle.onDetach() line: 569
>> RequestCycle.detach() line: 508
>> RequestCycle.processRequestAndDetach() line: 284
>> WicketFilter.processRequest(ServletRequest, ServletResponse,
>> FilterChain) line: 162
>>
>> *** 9th DETACH LIST MODEL ***
>> Same as before
>>
>> *** 10th DETACH LIST MODEL ***
>> Same as before
>>
>> *** 11th DETACH LIST MODEL ***
>> Same as before
>>
>> To show this behaviour I've created the following test page.
>> TestPage.java
>>
>> public class TestPage extends WebPage
>> {
>>    private static List<String>  testList = new ArrayList<String>() {{
>> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>>
>>    public TestPage()
>>    {
>>        final IModel<List<String>>  model = new
>> LoadableDetachableModel<List<String>>()
>>                {
>>                    @Override
>>                    protected List<String>  load()
>>                    {
>>                        System.out.println("LOAD LIST MODEL " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>                        return testList; // would normally get list
>> from db, in this case just retun static list.
>>                    }
>>
>>                    @Override
>>                    public void detach()
>>                    {
>>                        System.out.println("DETACH LIST MODEL " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>                        super.detach(); // detach model. "heavyweight"
>> model no longer needed.
>>                    }
>>                };
>>
>>        final ListView<String>  rows = new ListView<String>("rows", model)
>>        {
>>            @Override
>>            protected void populateItem(ListItem<String>  item)
>>            {
>>                System.out.println("populateItem " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>
>>                // with ajax link.
>>                AjaxLink<List<String>>  link = new
>> AjaxLink<List<String>>("link", model)
>>                    {
>>                        @Override
>>                        public void onClick(AjaxRequestTarget target)
>>                        {
>>                            // Do nothing.
>>                        }
>>                    };
>>                link.add(new Label("label", item.getModel()));
>>                item.add(link);
>>
>> //                item.add(new Label("label", item.getModel())); // no
>> ajax link.
>>
>>            }
>>        };
>>
>>        add(rows);
>>    }
>>
>>    private void readObject(ObjectInputStream in) throws IOException,
>> ClassNotFoundException
>>    {
>>        System.out.println("READ OBJECT " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>        in.defaultReadObject();
>>    }
>>
>>    private void writeObject(ObjectOutputStream out) throws
>> IOException, ClassNotFoundException
>>    {
>>        System.out.println("WRITE OBJECT " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>        out.defaultWriteObject();
>>    }
>>
>>    @Override
>>    public void detachModels()
>>    {
>>        System.out.println("DETACH MODELS " +
>> Integer.toHexString(TestPage.this.hashCode()));
>>        super.detachModels();
>>    }
>> }
>>
>> with the TestPage.html
>>
>> <?xml version="1.0" encoding="UTF-8" ?>
>> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
>> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
>> <html xmlns="http://www.w3.org/1999/xhtml"
>> xmlns:wicket="http://wicket.apache.org/"
>>    xml:lang="en" lang="en">
>> <head>
>> </head>
>> <body>
>>   <div wicket:id="rows">
>>         <a wicket:id="link"><label wicket:id="label"></label></a>
>>       <!--<label wicket:id="label"></label>  -->
>>   </div>
>> </body>
>> </html>
>>
>> I understand that I could use a ListModel, and not worry about this
>> detaching behaviour, as the list would be a serializable, but I'd like
>> the list model to always be up to date from the database on page
>> refresh, which wouldn't be the case for a ListModel. (it would always
>> have the same values as it did on the initial page render)
>>
>> I am using wicket 1.5.3 and firefox 8.0.
>>
>> Thanks.
>>
>> Phil.
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
>> For additional commands, e-mail: users-help@wicket.apache.org
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>

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


Re: AjaxLink causes list model to be detached before rendering!

Posted by Sven Meier <sv...@meiers.net>.
Hi Philip,

ListView throws away all its children before rendering. When a component 
is removed, its model gets detached.
Your AjaxLink has your LoadableDetachableModel as model, thus it forces 
it to be detached too.

As a possible solution, don't keep a reference to the model as your 
AjaxLink's model:
                 AjaxLink<Void> link = new AjaxLink<Void>("link")

Hope this helps
Sven

On 01/08/2012 09:22 PM, Philip Wilkinson wrote:
> Hi,
>
> Can anyone tell me why when I refresh a wicket page which has on it an
> AjaxLink, my list model is being detached just
>
> before its contents are rendered by a list view?
>
> I would expect detach only to be called after everything has been rendered.
>
> Detaching just before rendering would call a potentially 2nd
> unnecessary expensive database operation to load the
>
> model.
>
> I have an Ajax‌Link call back which seems to be creating this
> behaviour. Without it the models are detached afer
>
> rendering as expected.
>
> E.g. for the attached simple test page, pressing refresh in the
> browser, when there is no AjaxLink I get the following
>
> diagnostic output from Testpage.java...
>
> LOAD LIST MODEL 79e14338
> populateItem 79e14338
> populateItem 79e14338
> populateItem 79e14338
> DETACH MODELS 79e14338
> DETACH LIST MODEL 79e14338
> DETACH MODELS 79e14338
> DETACH LIST MODEL 79e14338
>
> When I surround the label with an ajax link, pressing refresh in the
> browser, I get..
>
> LOAD LIST MODEL 62b512e7
> DETACH LIST MODEL 62b512e7 //1st ????
> DETACH LIST MODEL 62b512e7 //2nd ????
> DETACH LIST MODEL 62b512e7 //3rd ????
> populateItem 62b512e7 // render "Aaaaa"
> populateItem 62b512e7 // render "Bbbbb"
> populateItem 62b512e7 // render "Ccccc"
> LOAD LIST MODEL 62b512e7
> DETACH MODELS 62b512e7
> DETACH LIST MODEL 62b512e7 //4th
> DETACH LIST MODEL 62b512e7 //5th
> DETACH LIST MODEL 62b512e7 //6th
> DETACH LIST MODEL 62b512e7 //7th
> DETACH MODELS 62b512e7
> DETACH LIST MODEL 62b512e7 //8th
> DETACH LIST MODEL 62b512e7 //9th
> DETACH LIST MODEL 62b512e7 //10th
> DETACH LIST MODEL 62b512e7 //11th
> WRITE OBJECT 62b512e7
>
> .. 11 calls to .detach() on the list model!
> between the 3rd and 4th detach list model the list is actually
> rendered. Had this been for real this would have ment
>
> that the list model would have been loaded again at this point.
> Here's the detail stack traces of where these 11 detach list models
> are being called from when refresh on my test page with
> the AjaxLink...
>
> *** 1st DETACH LIST MODEL ***
> TestPage$2.detach() line: 39
> TestPage$3$1(Component).detachModel() line: 3536
> TestPage$3$1(Component).detachModels() line: 1203
> TestPage$3$1(Component).detach() line: 1154
> ListItem<T>(MarkupContainer).detachChildren() line: 1710
> ListItem<T>(Component).detach() line: 1161
> TestPage$3(MarkupContainer).removeAll() line: 672
> TestPage$3(ListView<T>).onPopulate() line: 498
> TestPage$3(AbstractRepeater).onBeforeRender() line: 119
> TestPage$3(Component).internalBeforeRender() line: 981
> TestPage$3(Component).beforeRender() line: 1015
> TestPage(MarkupContainer).onBeforeRenderChildren() line: 1785
> TestPage(Component).onBeforeRender() line: 3774
> TestPage(Page).onBeforeRender() line: 823
> TestPage(Component).internalBeforeRender() line: 981
> TestPage(Component).beforeRender() line: 1015
> TestPage(Component).internalPrepareForRender(boolean) line: 2184
> TestPage(Page).internalPrepareForRender(boolean) line: 280
> TestPage(Component).render() line: 2271
> TestPage(Page).renderPage() line: 1035
> WebPageRenderer.renderPage(Url, RequestCycle) line: 105
> WebPageRenderer.respond(RequestCycle) line: 182
> RenderPageRequestHandler.respond(IRequestCycle) line: 167
>
> *** 2nd DETACH LIST MODEL ***
> Same as before.
>
> *** 3rd DETACH LIST MODEL ***
> Same as before.
>
> *** Now populateItem is called 3 times on the list view to render the
> 3 list elements...
>
> *** 4th DETACH LIST MODEL ***
> TestPage$2.detach() line: 39
> TestPage$3(Component).detachModel() line: 3536
> TestPage$3(Component).detachModels() line: 1203
> TestPage$3(Component).detach() line: 1154
> TestPage(MarkupContainer).detachChildren() line: 1710
> TestPage(Component).detach() line: 1161
> PageProvider.detach() line: 318
> RenderPageRequestHandler.detach(IRequestCycle) line: 148
> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
> RequestCycle.onDetach() line: 565
> RequestCycle.detach() line: 508
> RequestCycle.processRequestAndDetach() line: 284
> WicketFilter.processRequest(ServletRequest, ServletResponse,
> FilterChain) line: 162
>
> *** 5th DETACH LIST MODEL ***
> TestPage$2.detach() line: 39
> TestPage$3$1(Component).detachModel() line: 3536
> TestPage$3$1(Component).detachModels() line: 1203
> TestPage$3$1(Component).detach() line: 1154
> ListItem<T>(MarkupContainer).detachChildren() line: 1710
> ListItem<T>(Component).detach() line: 1161
> TestPage$3(MarkupContainer).detachChildren() line: 1710
> TestPage$3(Component).detach() line: 1161
> TestPage(MarkupContainer).detachChildren() line: 1710
> TestPage(Component).detach() line: 1161
> PageProvider.detach() line: 318
> RenderPageRequestHandler.detach(IRequestCycle) line: 148
> RequestCycle$HandlerExecutor.detach(IRequestHandler) line: 761
> RequestCycle$HandlerExecutor(RequestHandlerStack).detach() line: 180
> RequestCycle.onDetach() line: 565
> RequestCycle.detach() line: 508
> RequestCycle.processRequestAndDetach() line: 284
> WicketFilter.processRequest(ServletRequest, ServletResponse,
> FilterChain) line: 162
>
> *** 6th DETACH LIST MODEL ***
> Same as before
>
> *** 7th DETACH LIST MODEL ***
> Same as before
>
> *** 8th DETACH LIST MODEL ***
> TestPage$2.detach() line: 39
> TestPage$3(Component).detachModel() line: 3536
> TestPage$3(Component).detachModels() line: 1203
> TestPage$3(Component).detach() line: 1154
> TestPage(MarkupContainer).detachChildren() line: 1710
> TestPage(Component).detach() line: 1161
> PageStoreManager$PersistentRequestAdapter(RequestAdapter).commitRequest()
> line: 156
> PageStoreManager(AbstractPageManager).commitRequest() line: 94
> PageAccessSynchronizer$2(PageManagerDecorator).commitRequest() line: 68
> PageAccessSynchronizer$2.commitRequest() line: 281
> Application$2.onDetach(RequestCycle) line: 1588
> RequestCycleListenerCollection$3.notify(IRequestCycleListener) line: 99
> RequestCycleListenerCollection$3.notify(Object) line: 97
> ListenerCollection$1.notify(T) line: 119
> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotify(INotifier<T>)
> line: 143
> RequestCycleListenerCollection(ListenerCollection<T>).reversedNotifyIgnoringExceptions(INotifier<T>)
> line: 113
> RequestCycleListenerCollection.onDetach(RequestCycle) line: 95
> RequestCycle.onDetach() line: 569
> RequestCycle.detach() line: 508
> RequestCycle.processRequestAndDetach() line: 284
> WicketFilter.processRequest(ServletRequest, ServletResponse,
> FilterChain) line: 162
>
> *** 9th DETACH LIST MODEL ***
> Same as before
>
> *** 10th DETACH LIST MODEL ***
> Same as before
>
> *** 11th DETACH LIST MODEL ***
> Same as before
>
> To show this behaviour I've created the following test page.
> TestPage.java
>
> public class TestPage extends WebPage
> {
>     private static List<String>  testList = new ArrayList<String>() {{
> add("Aaaaa"); add("Bbbbb"); add("Ccccc");}};
>
>     public TestPage()
>     {
>         final IModel<List<String>>  model = new
> LoadableDetachableModel<List<String>>()
>                 {
>                     @Override
>                     protected List<String>  load()
>                     {
>                         System.out.println("LOAD LIST MODEL " +
> Integer.toHexString(TestPage.this.hashCode()));
>                         return testList; // would normally get list
> from db, in this case just retun static list.
>                     }
>
>                     @Override
>                     public void detach()
>                     {
>                         System.out.println("DETACH LIST MODEL " +
> Integer.toHexString(TestPage.this.hashCode()));
>                         super.detach(); // detach model. "heavyweight"
> model no longer needed.
>                     }
>                 };
>
>         final ListView<String>  rows = new ListView<String>("rows", model)
>         {
>             @Override
>             protected void populateItem(ListItem<String>  item)
>             {
>                 System.out.println("populateItem " +
> Integer.toHexString(TestPage.this.hashCode()));
>
>                 // with ajax link.
>                 AjaxLink<List<String>>  link = new
> AjaxLink<List<String>>("link", model)
>                     {
>                         @Override
>                         public void onClick(AjaxRequestTarget target)
>                         {
>                             // Do nothing.
>                         }
>                     };
>                 link.add(new Label("label", item.getModel()));
>                 item.add(link);
>
> //                item.add(new Label("label", item.getModel())); // no
> ajax link.
>
>             }
>         };
>
>         add(rows);
>     }
>
>     private void readObject(ObjectInputStream in) throws IOException,
> ClassNotFoundException
>     {
>         System.out.println("READ OBJECT " +
> Integer.toHexString(TestPage.this.hashCode()));
>         in.defaultReadObject();
>     }
>
>     private void writeObject(ObjectOutputStream out) throws
> IOException, ClassNotFoundException
>     {
>         System.out.println("WRITE OBJECT " +
> Integer.toHexString(TestPage.this.hashCode()));
>         out.defaultWriteObject();
>     }
>
>     @Override
>     public void detachModels()
>     {
>         System.out.println("DETACH MODELS " +
> Integer.toHexString(TestPage.this.hashCode()));
>         super.detachModels();
>     }
> }
>
> with the TestPage.html
>
> <?xml version="1.0" encoding="UTF-8" ?>
> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
> <html xmlns="http://www.w3.org/1999/xhtml"
> xmlns:wicket="http://wicket.apache.org/"
>     xml:lang="en" lang="en">
> <head>
> </head>
> <body>
>    <div wicket:id="rows">
>          <a wicket:id="link"><label wicket:id="label"></label></a>
>        <!--<label wicket:id="label"></label>  -->
>    </div>
> </body>
> </html>
>
> I understand that I could use a ListModel, and not worry about this
> detaching behaviour, as the list would be a serializable, but I'd like
> the list model to always be up to date from the database on page
> refresh, which wouldn't be the case for a ListModel. (it would always
> have the same values as it did on the initial page render)
>
> I am using wicket 1.5.3 and firefox 8.0.
>
> Thanks.
>
> Phil.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@wicket.apache.org
> For additional commands, e-mail: users-help@wicket.apache.org
>


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