Flex TextInput with default mobile skins automatically regains focus on callout close - actionscript-3

I'm working on a simple auto complete function for my Flex mobile app. For that I've got a CalloutButton that triggers a Callout.
The Callout holds some lists from which the user can select items. On item select, the callout gets closed (calloutButton.closeDropDown()).
The very same behavior is done for a TextInput. The user inputs text, the callout opens and according to the entered text, the lists change. Works fine so far. Now, when the user selects an item from any of the lists, the callout closes. Also fine.
Now the issue, after the callout is closed, the TextInput automatically regains focus.
On a mobile device this is more than disturbing.
I narrowed this behavior down to the mobile TextInput skin (spark.skins.mobile.TextInputSkin) since a TextInput without this skin class doesn't show this behavior.
Now you might say just use the default skin instead but unfortunately I can't. The default skin has a bug with Android devices that doesn't pass though the enter event. That I could live with since I'n not necessarily dependent on the enter event, however, the spark mobile skin allows be to continue entering text in the TextInput even after the callout has been opened, which is desperately needed as the lists change according to the entered text.
I can't provide any code as the problem has been narrowed down to the skinClass, thus should not be in my own code. Believe me, I tried every nice and not so nice method to prevent the TextInput from getting focus again, but nothing worked.
So, totally stuck here!
Hopefully you guys have some ideas on how to solve this.
Edit:
Below the steps of my application's behaviour. (to be fair, the workflow inside the callout is a little more complex, I'm working with several lists and a SplitViewNavigator here (thus can't use the Flextras autocomplete), but that doesn't affect the TextInput focus issue I'm facing).
Enter text in TextInput
On key change a Callout with two Lists is opened.
The first List receives results according to the entered text from a webservice
User selects item from list
Second list receives results according to selection from first list from webservice
User selects item on second list
Callout closes
This all works fine, except that the TextInput receives focus after the Callout closes.
I really don't know what triggers this behavior.
Edit2: Code to further illustrate the issue.
Naturally, this is stripped down to the very basics, but it mirrors the behavior of the component and focus.
First the CalloutButton and TextInput that can both control the Callout:
<ui:SearchCallout id="detailSearch"/>
<s:TextInput id="searchInput" skinClass="spark.skins.mobile.TextInputSkin"
enter="historySearch(searchInput.text)"
focusOut="searchFocusOutEvent(event)"
focusIn="searchFocusInEvent(event)"/>
The TextInput's focus handlers don't do anything related to the callout, they just set current states of another component, so I'll leave them out here.
Function historySearch closes the CallOut (I forced it to close as it wouldn't close with the usual closeDropDown()), formats the search text, handles a searchHistory and eventually triggers the search function that passes the formatted search text to the selected component.
Here are the parts that matter for this case:
private function historySearch(val:String):void {
detailSearch.forceClose=true;
detailSearch.closeDropDown();
searchEvent(_searchSyms, true);
}
Note: 'val' (the TextInput's text) is trimmed and whatnot, eventually it will result in an array of strings, represented by '_searchSyms'.
Further eventListeners are as follows:
searchInput.addEventListener(KeyboardEvent.KEY_DOWN, onKeyEvent);
searchInput.addEventListener(FlexEvent.VALUE_COMMIT, onKeyEvent);
searchInput.addEventListener(TextOperationEvent.CHANGE, onTextChange);
KEY_DOWN and VALUE_COMMIT don't have anything to do with the Callout, they are used to handle stuff for the searchHistory, hence I'll leave the onKeyEvent function out here.
onTextChange triggers the string search on the server and closes the Callout in case the TextInput's text is an empty string:
private function onTextChange(event:Event):void {
if(searchInput.text=="") {
if(detailSearch.isDropDownOpen) {
(detailSearch.rightView.activeView as RightView).clearDetailList();
detailSearch.isCloseable=true;
}
}
_searchManager.getRicsByChar(searchInput.text);
}
Eventually the server will respond and passes an array of responses. The Callout gets opened and it's lists are filled with the responses.
I won't paste all the Callout content's code here as it would be way too much. Basically, the user selects an item from any of the lists, the Callout is forced to close and the search function that passes the value to the component (not yet pasted here, be patient ;), is given the item's value. That basically looks like this (never mind the FlexGlobals stuff, this gets all refactored once the focus issue has been resolved):
var search:String = String(event.currentTarget.selectedItem);
FlexGlobals.topLevelApplication.detailSearch.forceClose=true;
FlexGlobals.topLevelApplication.detailSearch.closeDropDown();
FlexGlobals.topLevelApplication.searchEvent(new Array(search), true);
Allright, now the final step of the whole functionality, the searchEvent. As already said, this function basically only passes the formatted search value to the selected component. This either happened on 'Enter' of the TextInput (as the code above shows), or on selection of an item from one of the Callout's lists.
public function searchEvent(_searchSymbols:Array, setText:Boolean):void {
if(setText) {
var _searchString:String="";
for each (var _sym:String in _searchSymbols) {
_searchString += _sym + ", ";
}
searchInput.text = _searchString.substring(0, _searchString.length-2);
}
stage.focus=null;
if(selectedWindowContainer) {
// set the array of search items to the selected component here
selectedWindowContainer.setFocus();
} else
trace("[MAIN] no component selected");
}
And that is basically it. This function is the last step of my search routine, and the selected component (that will get the search items), is getting the focus.
Yet, it automatically loses focus again and the TextInput will receive it. I have no idea where and why this happens, and I need to get rid of this behavior asap!
Wow, what a post, anyone still reading this? ;) Well, I hope so.

Well, after hours of testing several ways to prevent the TextInput from automatically gaining focus, I (kinda) solved the issue, although I think it's more a dirty hack than anything else.
Setting the TextInput's focusEnabled to false works fine, even though the focus indication (border around the TextInput) won't work with this any more (an issue I can live with for now).
Still, I'd really like to know what exactly is going on here, especially in the mobile skin class.

Related

How can I subscribe only once to the app-db in re-frame?

I need to subscribe to the app-db for a value that I want to check only once when the parent component is rendered. For example, when I click a button "Click me", and there's a certain on-click event being processed, whose status I have saved on the app-db with the list of processes that are being done, I just want to check against that value once, and display two different components based on that value.
If the value is empty, I want to proceed with the normal event. If not, I'd like to show something else to the user, a popup for example.
Now the thing is that, because it's actively listening to the app-db, and the value is changing almost every second (or in a matter of milliseconds), the said popup appears, disappears, reappears, and disappears again super fast with each change to the app-db, which isn't helpful at all.
I would like to just subscribe once, get the value, and do the checks based on the value when the parent was first rendered. And then I'll do something to make that go away.
If I click the "Click me" button once again, that's only when I'd like for it to re-render.
I haven't quite been able to achieve this. I tried numerous methods such as introducing a delay during the dispatch of popup as well as after introducing processing states to the app-db in the first place hoping that since the data will already be in a steady state, it might not change as much, but now that I realize it's actively listening to it, it's expected that the values would change.
I did try using the subscription without the deref, but that only introduced an error to my frontend, so I'm not sure which way to go now.
My error with the component diappearing/reappearing turned out to be triggered by something else. A conflict/mismatch with popup-ids and a dispatch to clear one popup leading to destroying all of them.
But to answer the question, it works when you introduce a (fn []) block after the let binding where you actually do the subscription, and calling the components from inside the fn.

trigger itemSelect event on Primefaces autocomplete

I have a primefaces autocomplete element which works great except one thig. The problem is that when I enter a valid text (which is mappable to the data behind) but I don't select the element from the propositions, and don't press tab or enter, nothing happens.
So I enter a value and click into another field, the element is not selected and the validation fails. However, I don't want the user to force to explicitly select an item.
My ideas were, that i put an onchange listener to the input element and trigger the primefaces itemSelect event within. But I don't know how to do that, if it's even possible.
Or maybe there are other solutions?
Thanks in advance!
I found a way, although it might not be the most beautiful and easy one.
Maybe it helps somebody...
This is a specific solution for primefaces (5.3), but it should work for other versions too.
$('#form\\:txtAutoComplete_input').val('Foo');
$('#form\\:txtAutoComplete_input').trigger('keydown');
$('#form\\:txtAutoComplete_input').trigger('input');
$('#form\\:txtAutoComplete_panel .ui-autocomplete-item').trigger('click');
For some reason, after the value is entered into the input field, you have to trigger the keydown and the input event in that order. After these events the autocomplete list shows with the matching values. There you have to trigger click on a certain element, so all the backing bean stuff is properly executed.
There is an API for Primefaces widgets.
For instance ;
<p:autoComplete id="gtautoaomplet" value="#{item.soemprop}" completeMethod="#{bean_CrossCheckNew.completeText}" scrollHeight="250"/>
In JS code ;
widget_form_mapingGrid_0_gtautoaomplet.search('foo');
When you write "PrimeFaces.widgets" in your browser console you can see all the widgets available in your page.
For more datail :
https://primefaces.github.io/primefaces/jsdocs/index.html
Good luck.

Run JS function (with a refreshed bean parameter) after commandLink's action that moves the user on another page?

This is my first question here and on top of that I'm really, really new to Spring/JSF/RichFaces (about 3 weeks) so, I'm sorry if my question is dumb :)
Long story short, having the following:
a4j:commandLink
action="myBean.doSomething" method that (among other things) updates a JSON parameter and returns another navigation page (so clicking on the commandLink moves the user on another page)
in the above case the oncomplete event does not trigger.
What I need to do is to refresh the JSON parameter so I can use it in a JS function before the user is moved to the next page. Since oncomplete doesn't trigger in my case I had to find a way around it but I am really not sure how "bullet proof" is the way I implemented. (not to mention that it is quite troublesome to implement it all over the place)
What I did so far was to have the commandLink's action point to the bean's method that does everything except returning the new navigation page and on oncomplete event to run my JS function with the updated JSON object then call a previously declared jsFunction that all it does is to return the navigation page.
It works ok, I guess, but I was wondering if there's "an official" (proper) way to do it (like forcing the oncomplete or similar event that comes after the action where I can use javascript).
Here's the example:
<a4j:form id="myForm">
<a4j:jsFunction name="moveToNextPage" action="next_page"/>
<a4j:commandLink action="#{myBean.doLogicAndUpdateJSON}"
oncomplete="callJSfunction(#{myBean.objJSON});moveToNextPage();">
</a4j:commandLink>
</a4j:form>
Thanks!

Connecting UI Elements in Google Apps Script (TabPanel - UiApp)

I've already asked a similar question, but I really can't figure out how to connect these elements together. I'm still not very good with Handlers, and I guess my question is:
How can I access UI Widgets (and their children) while outside of the doGet() function?
My use case is this: I have a list of projects/IDs. I have all the data I want based on the ID that will populate the Project Details tab of this application. I created 'unique' Buttons for each of these Projects, and threw them into a Grid. Now, I want to generate the Project Details (detailPanel) Widgets specifically for each Button if/when it is clicked.
I have the Project ID attached to each Button (uniquely) through a Hidden, but I can't seem to attach the Project Details tab (detailPanel) to the Button so that, when clicked, I can set the values for the TextBox, DateBox, ListBox, etc. Widgets of the detailPanel.
I think I'm missing something obvious about this. I want to avoid attaching each child Widget of the detailPanel as a callbackElement of the Button at all costs. There are around 40 elements (I've lost count), and it seems really inefficient. I'm almost sure that if I can add one Widget as a callback element, that I get access to all child Widgets. But I tried, and that doesn't seem to be the case.
Here is the link to the public UiApp, which shows the UI. And the sister Script Project (uneditable).
You dont need to add callback elements that you will write to, callback elements are only for reading their data. If the detailspanel id is dynamic have a hidden that has its id stored inside and pass it to the handler. from your handler you getelementbyid and set its data.

How to get text from textbox in GAS?

I run my GAS as an Apps-script-gadget on my web-page and use the GUI-builder. I have two questions:
1.
I know how to set text in a textbox:
app.getElementById("TextBox1").setText("BigSister");
But how do i get text from a textbox?
I tried:
app.getElementById("TextBox1").getText();
...getValue();
...value;
etc.
Nothing works...
2.
Is it possible to print a message window from the code to the screen f.Ex. for debugging purposes?
Any advice would be appreciated.
Within your handler code, you can access the value of the text box as
var value = e.parameter.TextBox1 ;
Before doing this you must have run the setName() method on your text box.
var tetxbox1 = app.createTextBox().setName('TextBox1');
I suggest you run through some of the tutorials at https://developers.google.com/apps-script/articles where you can find answers to many of your questions.
You can retrieve the value of your ListBox when it is passed back through a handler. It's passed in the parameter class under the Id of the ListBox. The following is a fool-proof method of retrieving that value:
function changeHandler(e)
{
var source = e.parameter['source']; //source is your Id of the element
var value = e.parameter[source]; //value is the selected item as ['a' | 'a,b,c']
//More code here
}
I'm still waiting for a solution to retrieve that value without a handler.
EDIT here is a workin example for you to test
online test
and the script + GUI is here
=======================
Srik answer is absolutely right - as usual ;-) - but to make things a bit more clear to you since you are using GUI builder here is a small screen capture that shows the relevant parameters.
As for your second question, the simplest thing to do to get a momentary showing window to 'print' some data to screen is to use a texBox or a textAreal (or anything else that allows to show text...) and play with the setVisible(true) & setVisible(false) to show/hide the widget. In the GUI builder you can put it anywhere in your UI, even over other elements that you normally use since it will be invisible most of the time (the visibility parameter is available in the GUI builder environment as well). It will behave exactly as a 'popup' in other environments...