On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <igor.vaynb...@gmail.com>wrote:
> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <mgrigo...@apache.org> > wrote: > > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <s...@meiers.net> wrote: > > > >> Hi, > >> > >> > >> >The idea with plain JS solution I cannot visualize in my head yet. > >> > >> EventDelegatingBehavior is just a collector of JavaScript snippets. The > >> actual magic runs in the browser: a custom bubbling of events and > >> delegation to the actual behavior. > >> It should be possible to do this plain with JavaScript: > >> > >> public class DelegatingAjax implements IAjax { > >> > >> public ajax(IHeaderResponse response, Component component, > >> AjaxRequestAttributes attributes) { > >> CharSequence ajaxAttributes = renderAjaxAttributes(**component, > >> attributes); > >> > >> > response.render(**OnDomReadyHeaderItem.**forScript("Wicket.Event.***delegate*(" > >> + ajaxAttributes + ");"); > >> } > >> } > >> > >> This would be page-global though. > > > > > > This is an important detail! > > I'll consult with my frontend colleagues but so far I don't see problems. > > > > For every delegated component we can set special CSS class, e.g. > > 'wicket-delegated'. > > The binding will be: $(document).on('click', '.wicket-delegated', > > function(event) {....}) > > i.e. we will take advantage of jQuery delegation/live support. > > This way even newly added items in the repeaters will be automatically > > supported. > > > this is partially on the right track, but there are still some > optimization that can be made. > > first, the ajax attributes need to be moved into a data attribute that > is written out on the tag. the final output of attaching a onclick > ajax behavior to a tag should end up looking like this: > > <a wicket:id="ajaxlink" > data-w-click="u/?0.foo:bar.ILinkListener/c/default/pd/true"/> > > (we will need to figure out how to encode ajax attributes into a string) > example: <a id="c23" data-w-attrs='{"u":"someUrl","m":"post"}' ...> $('#c23').data("w-attrs") === {u: "someUrl", m: "post"} This works for valid JSON, but it doesn't for the enhancement we use - the functions for the call listeners. > > then you can have the one global listener: > > $(document).on("click", function(e) { > The problem here is that using 'document' will make the things actually slower. We need to find a simple way to be able to bind on a parent component. In Sven's example - a table with many cells the most appropriate element is the <table> itself. In event-delegating-behavior branch I need to traverse the parent components and their behaviors to be able to find the appropriate parent. So we win some performance in JS execution but lose some in Java :-/ var element=$(this), attrs=element.attr("data-w-click"); > if (attrs&&!e.handledByWicket) > Wicket.Ajax.call(attrs); > e.handledByWicket=true; // if there are more handlers above, do > not double process the event - read below > } > } > > the advantage here is that we only have one javascript listener that > needs to be registered. > > however, there are a few disadvantages: > * event propagation options wont work anymore, because the event has > to propagate all the way to the document in order to trigger. > * some libraries block events. for example if there is a panel with an > ajax link inside a third party modal window. the modal window lib may > prevent any clicks from propagating out of itself, which means the > handler on the document will never see them. > > we can sort of solve this by having a behavior that would write out > the listener above, but attached to the component not the document. > > that way, if we look at my example with the panel inside the modal, > the user can add this behavior to the panel that will be in the modal > and still be able to capture the event. > > this does, however, make troubleshooting more difficult. why didnt my > ajax event trigger? you will have to be a lot more aware about what > javascript you have in the dom. > i think a short term goal might be to move the ajax attributes into a > dom attribute and change our ajax code to simply say > Wicket.Ajax.bind("click", "component234"); > see above (valid JSON) we can enrich the DOM: <a ... onsuccess="someScript"> but I think this is a step back to Wicket 1.5 days (ajax decorators on strings, etc.) > > this will register the listener like above on the element directly. so > no delegation yet but cleaner javascript/html. also the browser doesnt > have to parse as much javascript, so it will be a bit speedier. > > potentially we can collect ids to further optimize js size: > Wicket.Ajax.bind({click, ["c34", "c32"], blur: ["c22","c98"]); > > -igor > > > > > > > >> > >> > >> Sven > >> > >> > >> > >> On 07/11/2013 03:40 PM, Martin Grigorov wrote: > >> > >>> On Thu, Jul 11, 2013 at 4:30 PM, Nick Pratt <nbpr...@gmail.com> wrote: > >>> > >>> I think this is great - we have some tables now with a ton of JS > events > >>>> on > >>>> the child elements. Just to clarify, will this make the rendered page > >>>> smaller since there will only be a single JS handler for the event for > >>>> the > >>>> container rather than N JS handlers? > >>>> > >>>> At the moment all attributes for an inner element are preserved. > >>> 'e' (the event name), 'c' (the component markup id), pd (prevent > default), > >>> sp (stop propagation) can be removed because they are not really used. > >>> But every inner element can have its own call listeners, form > submitters > >>> can also have custom settings ('f', 'sc', 'mp', 'm'), so I think they > have > >>> to be preserved. > >>> If you look in #updateAjaxAttributes() for your ajax behaviors in your > >>> table cells you will probably notice that they have their own > attributes. > >>> > >>> > >>> Making it switchable (I think how Sven suggested) would be an > >>>> improvement - > >>>> we could leave it off by default, but provide a simple switch on a > >>>> per-container (or per-app) basis that would allow the dev to choose. > >>>> > >>>> Yes, it looks as an improvement. > >>> Moving the current code to such implementation is easy. > >>> The idea with plain JS solution I cannot visualize in my head yet. > >>> > >>> > >>> Regards > >>>> > >>>> Nick > >>>> > >>>> On Thu, Jul 11, 2013 at 4:59 AM, Martin Grigorov < > mgrigo...@apache.org > >>>> > >>>>> wrote: > >>>>> Hi, > >>>>> > >>>>> At https://github.com/apache/**wicket/compare/event-** > >>>>> delegating-behavioryou< > https://github.com/apache/wicket/compare/event-delegating-behavioryou> > >>>>> may see the diff between master and event-delegating-behavior > branches. > >>>>> > >>>>> The latter provides a new AjaxEventBehavior (AEB) - > >>>>> > >>>> EventDelegatingBehavior > >>>> > >>>>> (EDB), that suppresses the JS event binding for all > AjaxEventBehaviors > >>>>> > >>>> for > >>>> > >>>>> a given event type (click, submit, change, ...) in the children > >>>>> > >>>> components > >>>> > >>>>> of the host component of EDB. > >>>>> > >>>>> How EDB works: > >>>>> > >>>>> - until now AjaxEventBehavior#renderHead() renders ondomready header > >>>>> item > >>>>> with JS snippet like: > >>>>> Wicket.Ajax.ajax(**attributesObject); > >>>>> In the new branch there is a check if some parent has EDB for the > event > >>>>> type of this AEB, and if there is such then the AEB "donates" its > >>>>> attributes to the EDB. > >>>>> > >>>>> - EventDelegatingBehavior#**getCallbackScript() renders : > >>>>> Wicket.Event.delegate('**edbComponentMarkupId', 'eventType', > >>>>> edbAttributes, > >>>>> childrenAttrsMap); > >>>>> > >>>>> - when a delegated component fires its event (e.g. the user clicks > on an > >>>>> AjaxLink) the event is handled by EDB's event handler. It extracts > the > >>>>> markupId of the inner HTML element and fires Wicket.Ajax.Call with > the > >>>>> specific attributes for the extracted inner element. > >>>>> > >>>>> Pros: > >>>>> > >>>>> - simple to use - just add EDB to a container component around your > Ajax > >>>>> heavy component (e.g. repeater with many Ajax behaviors). See the > demo > >>>>> > >>>> app > >>>> > >>>>> at https://issues.apache.org/**jira/browse/WICKET-5267< > https://issues.apache.org/jira/browse/WICKET-5267> > >>>>> > >>>>> - faster JS execution > >>>>> -- faster execution of the domready handler because there is just one > >>>>> binding instead of N > >>>>> -- faster reaction because the browser finds the event handler much > >>>>> > >>>> faster. > >>>> > >>>>> I wasn't able to prove this with numbers because there is no way to > >>>>> > >>>> detect > >>>> > >>>>> the 'start time', i.e. when the user makes the action. With JS the > >>>>> > >>>> earliest > >>>> > >>>>> point is when the browser has already looked up the event handler. > >>>>> Chrome Dev tools (timeline, profiling, pagespeed) don't help too. So > my > >>>>> reference that it is faster are the articles in the web and a use > case > >>>>> in > >>>>> our application. > >>>>> > >>>>> Cons: > >>>>> > >>>>> - AEB#renderHead() needs to check whether there is EDB up in the > >>>>> > >>>> hierarchy > >>>> > >>>>> to be able to decide what to do. > >>>>> This is ugly, I agree. But I see no other solution that will preserve > >>>>> the > >>>>> transparent usage of something like EDB and will not require a major > >>>>> rewrite of user applications to be able to use event delegation. > >>>>> -- there are some optimizations to lower the impact of the new > checks: > >>>>> --- a new setting (IAjaxSettings#**useEventDelegation) - a global > >>>>> property > >>>>> that prevents visiting the parent components and their behaviors for > all > >>>>> apps which do not use EDB > >>>>> --- when EDB is bound it registers a metadata for its event type in > the > >>>>> page instance. This prevents visiting all behaviors of all parent > >>>>> components > >>>>> > >>>>> > >>>>> I have no more ideas how to further optimize it. > >>>>> > >>>>> Any feedback is welcome! Even if you have a completely different idea > >>>>> how > >>>>> to implement this functionality. > >>>>> > >>>>> Thanks for reading! > >>>>> > >>>>> > >> >