On Fri, Jul 12, 2013 at 1:50 AM, Martin Grigorov <[email protected]> wrote:
> On Fri, Jul 12, 2013 at 8:59 AM, Igor Vaynberg <[email protected]>wrote:
>
>> On Thu, Jul 11, 2013 at 7:22 AM, Martin Grigorov <[email protected]>
>> wrote:
>> > On Thu, Jul 11, 2013 at 4:48 PM, Sven Meier <[email protected]> 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.

i did say we need to figure out a way to encode it right above the example :)

>> 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.

umm, why does it make things slower exactly? this has virtually no
overhead, events bubble up anyways...so where does the slowness come
from?

-igor

>
> 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 <[email protected]> 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 <
>> [email protected]
>> >>>>
>> >>>>> 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!
>> >>>>>
>> >>>>>
>> >>
>>

Reply via email to