Vaadin item click listener double click behaviour - client-server

I am working on a vaadin application. I have provided left single click and left double click actions on a grid row via item click listner. In case of left single click it works correctly, but problem is that whenever i do left double click on grid, the listner gets invoked 3 times i.e.
Single Click
Single Click, then
Double Click
On first invoke, selected item is correct but on second and third invoke the selected item is null. Moreover sometimes there is delay in sending the click action to server(i am not getting it why). On my machine it is working correctly because client and server are both on same machine, but on actual system it looks like the request from client(browser) either reach late to server or does not reach at all.
Can anyone help me in this?
Note- Grid selection model is SingleSelection.
#Override
public void itemClick(final ItemClick<PersonData> itemClickEvent) {
final PersonData data = itemClickEvent.getItem();
if (itemClickEvent.getMouseEventDetails().isDoubleClick()) {
//some logic
} else {
//some logic
}
}

First, there's a potential bug or a limitation: the way click events work in browsers is that when you double click an element, it actually fires three events:
a single click event after the first click
a second single click event after the second click
a double click event (assuming the second click happened fast enough after the first one)
You can test this out in a plain HTML document like this:
<!DOCTYPE html>
<html>
<body>
<button ondblclick="d()" onclick="s()">Double-click here</button>
<p id="demo"></p>
<script>
function d() {
document.getElementById("demo").innerHTML += "Double";
}
function s() {
document.getElementById("demo").innerHTML += "Single";
}
</script>
</body>
</html>
You'll note that if you double-click the button, it'll print "SingleSingleDouble". So the framework's server-side events reflect the browser events in a naive way.
It would be difficult to create a server-side fix that would make the server-side events behave in a more intuitive way due to the way client-server RPC calls work in Vaadin. Here's a simplified breakdown:
If there's a server-side listener for a particular event like "click":
If there is no ongoing request, an RPC is sent to the server immediately when the event is caught in the browser. So when the first click of the double-click occurs -> boom, sent to the server
If there is an ongoing request, any client-side events that should be sent to the server are placed in a queue and the queue's events are sent to the server once the previous response has arrived
As you've noticed, the network latency may come into play here - if the request-response cycle is very fast, you might even end up making three separate requests to the server; on a slow network, it might take a noticeable delay after the first server-side listener activation before the second one does.
So: while it would seemingly make sense to deduce on the server that in the case of "single-single-double" triplet the "logical" event would be just one double-click event, what the server usually receives first is just the one single-click event, while the second click event and the double click event are possibly waiting in a queue in the browser. The server has no way of knowing that it was actually a double-click. If there's a fix to be made, it needs to be done on the client-side of the Grid implementation; I'd recommend creating a GitHub ticket about it.
Next up, as you have Grid where rows can be selected, the first click on the item selects it, the second click deselects it. Here, I'd recommend using a multi-selection grid, as there the selection is not connected to clicking a row but happens via checking a checkbox. That way, you can still have a click listener on the row and a selection that doesn't get deselected accidentally. You'll just need to make sure the previous selection is cleared when a new row is selected; this can be done in a selection listener.

Related

How to avoid events to popup to accordion

I have an Accordion Panel whose summary is a search field:
When the content of the search field change, the panel shall be opened and show search result.
It happens that the events in the search field pop-up to the the tab that consequently opens and closes the tab when space or enter keys are pressed. I tried to wrap the summary in a special div that prevents pop-up:
class MyDiv extends Div {
public MyDiv(Component... components) {
super(components);
getElement().executeJs("addEventListener('keydown', (e) => {console.log('pop up prevented'); e.stopPropagation();});");
}
}
...
accordion.add(searchAccordionPanel = new AccordionPanel(new MyDiv(searchField),
searchItemsPage = new MenuItemsPage(this)));
And in fact I see the events intercepted ("pop up prevented" on console) but for some reason the accordion still responds to keypress. I have also tried keyup and keypress events, same result.
Maybe Vaadin transmit another event? Can anybody suggest a solution?
By default TextField is in eager value change mode, i.e. it emits value change after each key stroke. This does not fit in your use case. You can use textField.setValueChangeMode(ValueChangeMode.LAZY) for example. Then TextField will emits value change after user has paused typing. There are also couple of other modes, pick the one that fits the use case the best. Some times it requires trial and error.

Angular Material md-select load options in async way

I need to load select's options asynchronously (
through a service), using the Angular Material md-select component.
Actually, I use a click event to load data. It works but I need to click the select twice to show the options. That it's a problem.
The expected behavior is shown at this link (AngularJs Material)
The actual behavior is shown at this link.
Is Async options' loading supported by md-select?
The reason you need to click twice is because when you first click, there are no options in the select control and so it doesn't try and open the panel. Then your async method loads the options into the DOM and on the next click it will open.
In order to deal with this, you must always include at least one <mat-option> in your <mat-select>. You can add a disabled <mat-option> with a <mat-spinner> showing that the data is loading for example.
Here the most simple example of that. This is not the best approach... see below.
However, this still uses the click event which isn't the best approach. If you put the click event on the <mat-select> there are spots where you can click on the control but your click event wont trigger even though the dropdown panel still opens (places like the floating label area). You could put the click event on the <mat-form-field> but then there will be places where you can click and trigger the click event but the dropdown panel wont open (places like the hint/error text area). In both cases you lose keyboard support.
I would suggest using the <mat-select> openChanged event instead of a click event. This has its own quirks too but they are manageable.
Here is an example using the openChanged event. I also made the component more robust overall.
I also made a version that uses the placeholder element to show the spinner instead of using a disabled mat-option. This required View Encapsulation to be turned off.
Note: In my example, the service can return different data depending on the circumstances. To simulate this my fake service keeps track of how many requests you send it and changes the options returned accordingly. So I have to set the option list back to empty and clear the formControl's value every time the list is opened. I save the selected value before clearing the formControl so that if the service returns a list with the same item I can automatically reselect the value. If you only need to load the options once, then you would want to modify the code a bit to only load the options the first time the user opens the select.

Get value from autocomplete text field in ApEx

I want to create a dynamic action, that will set a value to an item on the page, when the value of another item (autocomplete text field) is set.
So the proccess goes like this:
Click on the autocomplete field
type some letters
choose one of the suggested values
I cannot find an event that will be executed when the selection of one of the suggested values happens. This way, I cannot see how I can read the value of the autocomplete field, once a suggested value is selected.
The change event doesn't fit my needs, it doesn't execute when one suggested value is selected.
I had the same problem, found this link: https://community.oracle.com/thread/2130716?tstart=0 and modified my dynamic action as follows to get the desired behaviour:
Event = Custom
Custom Event = result
From the link:
the problem seems to be the default behavior of the browser. When you
enter some text into the autocomplete and the list is displayed, the
focus is still in the text field. Also if you use the keyboard cursors
to pick an entry the focus will still be in the textfield. That's why
the change event of the textfield doesn't fire. It only fires if you
leave the field.
On the other side if you pick an entry with the mouse, the browser
will remove the focus from the text field for a moment (before the
JavaScript code puts the focus back), because you clicked outside of
the field. But that's enough so that the browser fires the change
event.
I had a look into documentation of the underlaying jQuery Autocomplete
widget
(http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/) and
there is actually an event called "result" which can be captures if an
entry is selected from the drop down list.
Try "Lose Focus" as event. It will trigger your dynamic action when you leave the autocomplete field, i.e. your curosr is moved to another field.
This probably depends on the APEX version you are using.
In case of 18.2, because the underlying component is based on Oracle JET's "inputSearch" component, you need to use following configure to capture the select change event for text with autocomplete:
event: Custom
custom event: ojupdate
Reference:
https://docs.oracle.com/cd/E87657_01/jet/reference-jet/oj.ojInputSearch.html#event:optionChange
I turned on the browser console, then turned ApEx Developer toolbar debug, and found that, on the contrary, the "Change" event does fire upon user clicking with the mouse on one of the selections. However if the user uses keyboard (type a few letters to narrow the list down, then use down arrow key to arrive at desired value, then press enter) then the Change event does not fire, just as you say.
Moreover: even when you do get the value sent back via mouse-click initiated Change event, the value isn't the autocomplete's complete and valid value, but instead the possibly partial and wrong-case value just as typed by the user. I.e., the the change event's submission of the value precedes the autocomplete's substitution.
The answer that #VincentDeelen gave is the best alternative that I can see, although it doesn't quite give that "instantantenous synchronicity" feel. You could maybe use the "Key Down" event, but be careful with that. You could get a really excessive amount of web and db traffic as each and every keystroke (including corrections) results in another firing of the dynamic action.
Testing environment: ApEx 4.2.3 with Chrome 33 as well as IE 9.
p.s. This might be worth a mention to the ApEx development team as well.
It's not really ideal, but you could use onfocus(). I'm looking for the same thing you are, I think, a custom event that fires when the selection of a suggested value happens. I haven't found it yet though and that is my work-around for now. It will run whatever function you've created for this initially with no value, but once the selection is made it will return focus and run the function again with the right value. Like I said, not ideal but it works.
Jeffrey Kemp is right. You can set it up through a dynamic action using the custom event, result. You can also register it on page load using document.getElementById("{id}").addEventListener("result", {function}); or $("#{id}").result( function( event, data, formatted ) { //something here });.
Oracle apex 19 now added a "component event" when you create a dynamic action called "Update [Text Field with autocomplete]" - this action is fired when you select a value from the list, but not when you leave the field (similar to adding the custom event "ojupdate").

AjaxFormValidatingBehavior Performance and Lost focus on Firefox

My project is using Wicket's AjaxFormValidatingBehavior to auto-save form content to Session on sort of a multi-tab form with a tree menu (there is no save button on individual tabs, though there is a "Save" button that actually submits the form, runs the validations and saves contents to database). I am facing few issues:
Since the behavior is added to all form components' onChange event, there is a server-trip every time user moves from one field to another. I know that a throttle duration can be specified to prevent this, but its not possible to set in my case as my forms are of different lengths/complexity, many components dynamically generated (including the tree menu). But is there a more elegant solution to auto-save form content (that doesn't have a submit button) rather than this annoying solution.
Another issue I am facing is that post onChange event, on Firefox the component loses its focus after the "server trip" ends. While on IE7 it works fine.
For the first question I think you need to add a pipelining facility, on your components' onchange call a javascript function of your which calls your webapp. You can include a feature similar to the one provided with the throttle duration but page-wide (delay each calls and only trigger the last if it is older than x milliseconds for example).
For the second one, I think you have to use the AjaxRequestTarget#focusComponent in your behaviors, or handle this thing in your "wrapper" as described in the first answer.

Why some time jquery click event fails?

My page is divided into two parts vertically.Left part is like a menu section. Clicking on
any menu brings the proper data related to that menu in the right part of the page. I am
doing it using ajax call and the only div on the right part get refreshed. I am using jquery click event for that ..
say:
$("#left_part").click(function() { // ajax call here });
Now I have some more jquery action on the right part. (say Show hide some div which also work on click event.)
But when new div reloads on the right part those click event on the right part is not working.
But when I bind the click event it works.
say:
$("#some_right_part").click(function() {/ some hide show here )}; Not working
$("#some_right_part").bind('click', function(event){ // some hide show here )}; works fine
Important: When I am on fresh page (no ajax call yet to bring right part) $("#some_right_part").click(function() {/ some hide show here )}; works fine.
But what I know is: $().click(function) calls $().bind('click',function)
So, What is the reason behind this? Or What is the perfect way to solve such problem
Thanks
When you assign a click event via $().click when the page loads, the click event is bound to all matching elements. However, when you do the ajax call and recieve new markup - the new elements are not bound to the click event because they did not exist when you first called $().click.
There is a way to get a click event to be bound to all elements and all future elements that may be created. You must use the live method like so:
$('#elements').live('click', function() {
// do logic
});

Resources