openui5 icon indicating copy to clipboard operation
openui5 copied to clipboard

Can not set focus of input field in SimpleForm other than by setTimeout

Open s-v-o opened this issue 2 years ago • 14 comments

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

s-v-o avatar Sep 27 '22 20:09 s-v-o

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.

UserAbcd1234 avatar Sep 30 '22 04:09 UserAbcd1234

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.

s-v-o avatar Sep 30 '22 10:09 s-v-o

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

UserAbcd1234 avatar Oct 04 '22 04:10 UserAbcd1234

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?

s-v-o avatar Oct 04 '22 18:10 s-v-o

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.

UserAbcd1234 avatar Oct 05 '22 03:10 UserAbcd1234

Nope, at least not in my example: https://plnkr.co/edit/hN1vf5V9P8tpukNW

s-v-o avatar Oct 05 '22 07:10 s-v-o

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

UserAbcd1234 avatar Oct 05 '22 08:10 UserAbcd1234

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.

s-v-o avatar Oct 05 '22 08:10 s-v-o

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.

s-v-o avatar Oct 05 '22 09:10 s-v-o

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!

ThomasChadzelek avatar Oct 05 '22 09:10 ThomasChadzelek

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 avatar Oct 05 '22 11:10 s-v-o

@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 ;)

boghyon avatar Oct 05 '22 14:10 boghyon

In the case above, it's the control with id="someControlIdInDetail". If the initialFocusTarget is not defined, then the control from the first target that has initialFocus defined. Any thoughts?

How would that play together with UI5 adaptation/p13n?

ThomasChadzelek avatar Oct 05 '22 14:10 ThomasChadzelek

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

s-v-o avatar Oct 05 '22 18:10 s-v-o

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

flovogt avatar Oct 28 '22 12:10 flovogt

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

stopcoder avatar Nov 29 '22 09:11 stopcoder

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.

s-v-o avatar Dec 08 '22 19:12 s-v-o

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.

s-v-o avatar Dec 15 '22 16:12 s-v-o

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:

  1. Open https://embed.plnkr.co/wp6yes?show=view%2FHome.view.xml,controller%2FHome.controller.js,preview:%3Finitial-focus%3Dtarget2
  2. Notice how the SimpleForm receives the initial focus, even after navigating forward and back.
    Make sure also that your sap.m.App has autoFocus="false".
  3. To reproduce the issue: in the Home.view.xml definition, either remove layout="ColumnLayout" or set layout="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?

boghyon avatar Jan 24 '23 17:01 boghyon

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 avatar Jan 30 '23 14:01 s-v-o

@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 if autoFocus is set to false.

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.

boghyon avatar Feb 27 '23 12:02 boghyon

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;
}

s-v-o avatar Mar 14 '24 14:03 s-v-o