openui5
openui5 copied to clipboard
Can not set focus of input field in SimpleForm other than by setTimeout
OpenUI5 version: 1.106.0
Browser/version (+device/version): Vivaldi / Chromium
Any other tested browsers/devices(OK/FAIL): FAIL
I've tried to set the focus on an input field within a SimpleForm. All the documented ways (or more precisely the few stackoverflow answers and the proposed way in #2037) don't work and in most cases I end up to set the focus through using setTimeout. But this feels like a workaround and a little bit awkward. Also, I don't know how reliable this solution is. Especially in regard to network timings.
What I have tried:
- addEventDelegate with onAfterShow (after I learned, that this has to be called in onInit the handler is also called, but focus don't works)
- register afterPatternMatched in onInit and in onAfterPatternMatched called focus(), but don't works either
I really don't understand why such a simple task like setting the initial focus after entering a view to a field is particularly so hard to achieve. I've lost hours in finding a way other than setTimeout, where it should be simply an attribute in the XML-View or a simple call to the API in a predefined controller hook (like onInit, but always executed when entering the view).
URL (minimal example if possible): https://plnkr.co/edit/vWYn7RL5FtXvZxVO
The SimpleForm internally creates a Form control with the corresponding settings. As the SimpleForm and the Form can have different Layout elements to use for rendering the corresponding Layout is created when the SimpleForm get's it settings. To prevent the loading of all possible layout modules only the required one is loaded. So the SimpleForm checks if the required modules are already loaded, if not it loads it. This could be asynchronously. The rendering of the Form and its content needs to wait until the modules are loaded, so this could lead to the case the content is not rendered synchronously.
However, if the modules are already loaded the content is rendered directly. In the most cases all required modules are somehow loaded by the application in some preload configuration, so normally it don't run into the asynchronous case. In your example this could be done by loading the module in the controllers define section.
As the default layout of the SimpleForrm is deprecated for a while I suggest to use the "ColumnLayout" as layout in the SimpleForm (defined in the view XML). (We cannot change the default for compatibility reasons.) Then in the Controller define section add "sap/ui/layout/form/ColumnLayout", this leads to the loading of the module before the rendering starts. Then the focus to the content control can be set directly.
You should also add the sap.ui.layout library to data-sap-ui-libs in index.htlm to make sure all necessary things of the layout library are loaded in time.
I've tried to change the example as you recommend, but it doesn't work either (the focus should be set to the second input field): https://plnkr.co/edit/o5msMxOJe6r5jxte?preview
Anyhow it is in my opinion bad DX, if the developer has to consider these things when he wants to set the focus to a field.
If you add "sap/ui/layout/form/ColumnLayout" to the define section of the Controller it will work, like I mentioned before. (I tested this.)
Yes, you are right: In the above posted plnkr it is missing (I'm sure I had tested it) But it didn't work without setTimeout (what is my point): https://plnkr.co/edit/rBi6w5TJxSJTCjNw
Maybe I understand you wrong? Maybe you could post a working example?
If you activate this.getView().addEventDelegate({ onAfterShow: ()=> { this.getView().byId("shorttextsimpleform").focus() } }) on the init function it should work. The onPatternMatched function is executed before the rendering of the SimpleForm starts at all.
Nope, at least not in my example: https://plnkr.co/edit/hN1vf5V9P8tpukNW
If I run the sample it works, at least if you press the refresh icon in the preview window. Initially focus on the preview window seems not to work at all. Maybe because the focus is set outside the iframe.....
Ok, I had to try it outside of plnkr.
But I observed exactly that behavior especially when working in the launchpad shell: sometimes the focus is set, sometimes the focus switches i.e. to a launchpad button. This is for the user very annoying, especially when working with a mobile scanning device.
What I want is, that the focus is in the specified field (when first loading the view, when navigate back to the view, etc.).
Overall programming with sapui5 is fun (more so since I discovered typescript), but the focus handling is a hassle.
But I observed exactly that behavior especially when working in the launchpad shell: sometimes the focus is set, sometimes the focus switches i.e. to a launchpad button. This is for the user very annoying, especially when working with a mobile scanning device.
And it is exactly that what happens with the suggested change: I enter the app from launchpad. The focus is set to the wanted field and shortly after that the focus switches to the launchpad shell button (with the app name). The user is unable to enter something into the input field with the scanner.
Maybe this cheers you up. In Alan Cooper's book "About face" he quotes a Windows developer claiming that "focus is actually a contraction of two words, the second one being us". This topic is known to be frustrating since decades!
I give up.
First it seems, that the addEventDelegate
variant works at least for subsequent views. But I discovered unwanted behavior there also. Some really weird one was that every second refresh the focus switches from the wanted field to the mentioned launchpad button.
I think I have to accept, that there is no really solution in the framework and I have to go with the setTimeout variant. This at least seems to work in most cases. So I go with the 80 % solution.
This topic is known to be frustrating since decades!
Yes, it is frustrating. But it shouldn't be. Nevertheless thank you for the kind words. Some point for good old ABAP: I never had a problem with SET CURSOR 😄
@s-v-o What do you think about https://github.com/SAP/openui5/issues/2306#issuecomment-451465876? It's just an idea for now. But a declarative way to set the initial focus would definitely reduce some of the frustration. At least for application developers ;)
In the case above, it's the control with
id="someControlIdInDetail"
. If theinitialFocusTarget
is not defined, then the control from the first target that hasinitialFocus
defined. Any thoughts?
How would that play together with UI5 adaptation/p13n?
@boghyon Thank you for referencing the issue, I haven't noted it. By the way, thank you also for your well appreciated engagement on stackoverflow.
The manifest wouldn't be the place I would have expected. But I understand the reasoning and when it would work reliably I will be quite happy.
But a declarative way to set the initial focus would definitely reduce some of the frustration.
That would be really great.
UI5 adaptation/p13n
I would expect that p13n beats declaration or development. But is it possible for the user to set the initial focus?
I think a place/hook in the Controller, where the developer can be sure, that everything is loaded and rendered and which is called every time the view is shown, is really needed.
Also I would expect, that the framework would take care to place the focus where the developer said it should be. When there are other thinks which have to be executed before, the framework should postpone setting the focus or at least there should be a well documented and reliable way to set the focus.
Hello @s-v-o , Thank you for sharing this finding. I've created an internal incident 2270167967. The status of the issue will be updated here in GitHub. Florian
Hi @s-v-o,
I would like to provide you with some of our findings about setting focus to a DOM element and the reason why we can't provide a mechanism to take care of setting the initial focus for a UI5 application. First of all, the initial focus is an important topic that must be done correctly in order to provide the end user with a proper starting point for the keyboard navigation. A DOM element needs to fulfill several prerequisites defined in the web browsers in order to be set with focus. Those prerequisites are well described in the jQuery documentation of the "focusable" pseudo selector. Without fulfilling those prerequisites, browsers simply ignore the call of the "focus" function on a DOM element. It could happen that the given control from the application for being set with the initial focus doesn't fulfill the prerequisites and UI5 framework doesn't know which element should be taken as an alternative for setting the focus. This leads to the result of having the focus set on the document.body
which is bad for the keyboard navigation. Furthermore, the browsers on mobile devices have even more strict rules defined for setting focus to an input-like DOM element. The call to "focus" is accepted only when the call is done within the same call stack of an event handler, like a handler of the "touchstart" or "touchend" event. Some workaround is available but it's not guaranteed to work with the future version of the mobile browser. Due to the above reasons, we decided not to provide a general mechanism for setting the initial focus but providing some events to the application for informing that the DOM elements are ready for receiving the focus.
Some really weird one was that every second refresh the focus switches from the wanted field to the mentioned launchpad button.
Regarding the above issue, is it possible to provide us with an example? I can't reproduce the issue within FLP.
Best regards, Jiawei
Thank you for your extensive answer.
It could happen that the given control from the application for being set with the initial focus doesn't fulfill the prerequisites and UI5 framework doesn't know which element should be taken as an alternative for setting the focus.
But when I set the focus to a input control there should be a defined place where it is assured that it will work as expected. Currently this is not the case.
Regarding the above issue, is it possible to provide us with an example? I can't reproduce the issue within FLP.
Maybe this is a timing / caching issue. Currently I'm not very motivated to provide further examples.
For my problem I go now with the setTimeout variant because no other is working.
Some really weird one was that every second refresh the focus switches from the wanted field to the mentioned launchpad button
Currently I can observe this behaviour when using a SimpleForm with attachAfterShow (including a focus call and a setTimer call to focus and an import of ResponsiveGridLayout, see previous discussion), but the focus isn't set to the desired control when loading the app the first time on a mobile device with fiori client. After the first call the focus isn't set (also not on the launchpad button), when the app is started again, the focus is set.
All the documented ways [...] don't work
@s-v-o I noticed setting the initial focus to the SimpleForm
(in the documented way without setTimeout
) works only if its layout
is not sap.ui.layout.form.SimpleFormLayout.ResponsiveLayout
which is, unfortunately, the default value for the layout
property.
Solution: as suggested by @UserAbcd1234, setting another layout, e.g. layout="ColumnLayout"
, resolves the issue.
Here I extended my sample accordingly:
- Open https://embed.plnkr.co/wp6yes?show=view%2FHome.view.xml,controller%2FHome.controller.js,preview:%3Finitial-focus%3Dtarget2
- Notice how the
SimpleForm
receives the initial focus, even after navigating forward and back.
Make sure also that yoursap.m.App
hasautoFocus="false"
. - To reproduce the issue: in the
Home.view.xml
definition, either removelayout="ColumnLayout"
or setlayout="ResponsiveLayout"
to apply the deprecated form layout. Only then, setting the initial focus fails.
Could you try the same solution within your Fiori Client?
And with which SAPUI5 version does the Fiori Client run?
Thank you for your reply and your effort.
Also with ColumnLayout the behaviour is the same (tested in sandbox environment).
Launching from the sandbox launchpad
- Launching the app
- Focus is set to the shell (more specifically the button with the app name)
In the attached focus.zip I have set up a local environment.
If you want to try it:
-
download a local SDK (i.e. 1.71.53)
-
adjust the two paths in ui5.yaml
-
npm i
-
npm run start
We need to use SAPUI5 version 1.71.46 (I think 1.71 is also the highest SAPUI5 version available for non S/4 SAP Fiori Front End Server).
@s-v-o Thanks for the steps! I knew the issue in FLP is not reproducible with the "recent" SAPUI5 versions but not clearly since when. So I tried a bunch of SAPUI5 versions. To sum it up:
Test case
- Navigating from FLP home to the FLP app
- Opening the app directly via deep link in FLP
- With and without disabling the cache
- In mobile (simulated via the browser devtools) and desktop
- With and without FLP
Given
Application code with a NavContainer
and an afterShow
handler for setting the initial focus as documented in the API reference.
[...] The
afterShow
event can be used to focus another element, only ifautoFocus
is set tofalse
.
Sample code from the community: https://stackoverflow.com/a/48559689/5846045
Result
- The issue is not reproducible if the app runs standalone (without the FLP shell), even in UI5 1.71.
- The issue is reproducible only in combination with the FLP shell + SAPUI5 v1.103 or lower, most likely since the change at
sap.ushell.renderers.fiori2.Shell#getWrappedApplicationWithMoreStrictnessInIntention
available in SAPUI5 1.104.0.
The FLP shell code is not part of this OpenUI5 repo. If possible, you could create a customer incident instead.
I find a hack that solves the issue for me.
Component.js init
this.saveSendFocusBackToShell = AccessKeysHandler?.sendFocusBackToShell;
if (this.saveSendFocusBackToShell) {
AccessKeysHandler.sendFocusBackToShell = function () {};
}
Component.js exit
if (this.saveSendFocusBackToShell) {
AccessKeysHandler.sendFocusBackToShell = this.saveSendFocusBackToShell;
}