How to Set Initial Focus in a View?
Asked Answered
M

3

4

I have a Detail view in an SAPUI5 app which contains a single input field with ID "bestellmenge_tu". Whenever this view is called, the focus should be on that input field. Unfortunately, when setting the focus on the field in the controller's onInit method, the focus will be set but some milliseconds later the UI5 takes it away and transfers it to the "navigation back" button of the detail view.

By putting a log.trace() on the input field's blur event, I found out that the focus is taken away by a method called sap.ui.define.NavContainer._afterTransitionCallback which is called asynchronously (some window.setTimeouts between trigger and execution). The function simply looks for the first focusable element in the view and brutally switches the focus on it.

My workaround was to redefine the method jQuery.fn.firstFocusableDomRef which is used to find this "first focusable element":

// DIESE KANONE FUNKTIONIERT
jQuery.fn.firstFocusableDomRef = (function() {
  var _default = jQuery.fn.firstFocusableDomRef;
  return function() {
    var bestellmenge_tu = document.querySelector("input[id$='bestellmenge_tu-inner']");
    if (bestellmenge_tu &&
        bestellmenge_tu.style.display !="none" &&
        bestellmenge_tu.style.visibility != "hidden") return bestellmenge_tu;
    else return _default.apply(this);
  }
})();

But this could be a performance issue (querySelector called during DOM transversal at any page load from there on), and it is too much coding for the desired effect.

Is there an easier method to achieve this?

I thought of something like

<mvc:View controllerName="zrt_dispo.view.Detail"...>
  <Page id="detailPage" initialFocus="bestellmenge_tu">  <!-- ID of the element to carry the focus -->
  </Page>
</mvc:view>
Melany answered 2/4, 2016 at 17:32 Comment(0)
G
10

Here is a working example: https://embed.plnkr.co/wp6yes/

<App autoFocus="false" xmlns="sap.m"><!-- AND/OR -->
<f:FlexibleColumnLayout autoFocus="false" xmlns:f="sap.f" /><!-- autoFocus in FCL available since 1.76 -->
{ // Controller of the target view:
  onInit: function() {
    const myFocusTarget = this.byId("myFocusableControl");
    this.applyInitialFocusTo(myFocusTarget, this.getView());
  },
  applyInitialFocusTo: function(target, navContainerChild) {
    const onAfterShow = () => target && !target.isDestroyed() && target.focus();
    navContainerChild.addEventDelegate({ onAfterShow });
  },
}

If you have a sap.m.NavContainer (I.e. sap.m.App or in sap.f.FlexibleColumnLayout), its direct child, aka. NavContainerChild which is usually a View, can react to navigation related events. Add a delegate to the NavContainerChild event afterShow according to the API reference of NavContainer:

The afterShow event can be used to focus another element, only if autoFocus is set to false.

The event handler is fired after the animation is finished. And most importantly:

This event is fired every time (in contrast to onAfterRendering) when the NavContainer has made this child control visible.

Since sap.ui.core.Control extends sap.ui.core.Element, every focusable control can receive the focus via thatControl.focus().


Known Issues in Older UI5 Versions

  • SAP Fiori launchpad (FLP) running with older SAPUI5 might take control of setting the initial focus when the app renders initially (Cf. FLP: Setting Custom Initial Focus on App Launch Fails). This issue is no longer reproducible since SAPUI5 1.104.0.

  • In UI5 1.62 and below, calling focus() in onAfterShow alone would still make the app set the focus on the first focusable element when the user navigates back, even with autoFocus="false" (See this GitHub issue #2306). In that case, an additional setTimeout with 0 ms (or requestAnimationFrame) is required in order to let the browser know that the element should be focused at the end of the call stack.

    onAfterShow: function() {
      // If UI5 version < 1.63:
      setTimeout(() => this.byId("thatControl").focus());
    },
    

    This issue is no longer reproducible since UI5 1.63 (OpenUI5 commit:6d46cf0).

Giles answered 1/2, 2018 at 9:47 Comment(0)
C
2

The NavContainer has a property autoFocus. App is a descendant of NavContainer so it has that property too. The help (as linked above) states the following:

Determines whether the initial focus is set automatically on first rendering and after navigating to a new page. This is useful when on touch devices the keyboard pops out due to the focus being automatically set on an input field. If necessary the "afterShow" event can be used to focus another element.

Default value is true.

Cobbler answered 3/4, 2016 at 19:56 Comment(0)
A
1

for everyone that uses an older sapui5 version where the answer from Boghyon does not work, this here worked for me:

onInit: function () {
     this.waitForAutoFocus().then(() => {
        oElementYouWantToFocus.focus();
    });
},

waitForAutoFocus: function () {
    return new Promise((resolve, reject) => {
        this.hasAutoFocused(resolve);
    });
},

hasAutoFocused: function (resolve) {
    setTimeout(() => {
        if (this.getCurrentFocusedControl()) {
            resolve();
        } else {
            this.hasAutoFocused(resolve);
        }
    }, 200);
},

//needs "sap/ui/core/Core" to be loaded
getCurrentFocusedControl: function () {
    const currentFocusedControlId = Core.getCurrentFocusedControlId();
    return Core.byId(currentFocusedControlId);
},

it basically waits for the default autofocus and then afterwards focuses your desired element

Aeriell answered 28/2 at 15:0 Comment(3)
Could you share which version you were using where the existing solution was not working? Are you referring to the FLP issue in UI5 < 1.104? And in which context is the oElementYouWantToFocus created? Is it part of an asynchronously created fragment? Is the app generated by Fiori elements? V2? V4?Giles
We are using Version 1.71. App was generated as basic freestyle App via vs code plugin from sap. The Element is declared in view.xml and then referred with this.byId("idOfElement")Aeriell
As stated in my answer, FLP has fixed the issue with the custom initial focus not being applied, but the fix is available only since SAPUI5 1.104. Consider creating a customer case to request the fix to be downported to the 1.71 release. You can include my analysis github.com/SAP/openui5/issues/3602#issuecomment-1446259384 in your request message. The fix should be somewhere in the FLP code sap.ushell.renderers.fiori2.Shell#getWrappedApplicationWithMoreStrictnessInIntention.Giles

© 2022 - 2024 — McMap. All rights reserved.