How to Access Elements from XML Fragment by ID
Asked Answered
L

3

24

I am working on a SAPUI5 application. I have an XML view which contains an XML Fragment and a Button to save.

The fragment contains a few controls like drop-down, text field and a table. When I press on the save button, I need to get all the rows in the table and call an OData update service.

The problem is in the onSave method in view controller. I get an error while accessing the table using its ID. Can anyone help me and advice how can I access controls used in fragments by their ID in the controller?

Here is the code snippet:

View:

<mvc:View xmlns:mvc="sap.ui.core.mvc" xmlns:core="sap.ui.core" xmlns:form="sap.ui.layout.form" xmlns="sap.m">
    <Page>
        ...
        <form:SimpleForm>
            <core:Fragment id ="fr1" fragmentName="first" type="XML" />
            <Button id="id1" press="onSave" />
        </form:SimpleForm>
    </Page>
</mvc:View>

Fragment definition:

<core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
    <Table id="tab1" mode="MultiSelect">
        ...
    </Table>
</core:FragmentDefinition>

Controller:

sap.ui.controller("view", {
    onSave: function() {
        //var tab = this.getView().byId("tab1"); // Not working
        var tab  = sap.ui.getCore().byId("tab1"); // Not working
    },
    // ...
});
Lulalulea answered 23/9, 2016 at 11:54 Comment(0)
S
27

Looking at the OpenUI5 code at GitHub, it seems that the Fragment delegates the local ID generation to the containing view if the <Fragment/> itself does not have an explicit ID.

So your code this.getView().byId("tab1") should work as soon as you remove the id="fr1" attribute from your <Fragment/> element.

When using explicit IDs there is a static Fragment.byId method to retrieve the control. I guess you have to use it like this:

// Fragment required from "sap/ui/core/Fragment"
var fragmentId = this.getView().createId("fr1");
var tab = Fragment.byId(fragmentId, "tab1");
Schulein answered 23/9, 2016 at 12:51 Comment(0)
S
42

Accessing controls inside a fragment depends on how your fragment was created. Here is a list of scenarios with the respective API to use to get the control reference.

Given:

  • this as a reference to the current controller instance
  • <MyControl id="controlId"/> in the fragment definition
  • Fragment required from sap/ui/core/Fragment
    Element required from sap/ui/core/Element

API to choose


πŸ‘‰this.byId("controlId");

... if the fragment was created with the view ID (recommended) either directly or indirectly:

  • // Recommended; API since 1.93
    this.loadFragment({ name: "..." }); // id: view ID given by default, API since 1.93
    
  • <!-- Recommended; in the view embedding the fragment declaratively -->
    <core:Fragment fragmentName="..." type="XML"/><!-- id = view ID given by default -->
    
  • Fragment.load({ // API since 1.58
      id: this.getView().getId(),
      name: "...",
      controller: this,
    });
    
  • sap.ui.xmlfragment(this.getView().getId(), "...", this); // Deprecated

    Resulting global ID: "componentId---viewId--controlId" *


πŸ‘‰this.byId(Fragment.createId("fragmentId", "controlId"));

... if a fragment ID was given with the view ID combined:

  • this.loadFragment({ id: this.createId("fragmentId"), name: "..." });
    
  • <core:Fragment id="fragmentId" fragmentName="..." type="XML"/>
    
  • Fragment.load({
      id: this.createId("fragmentId"),
      name: "...",
      controller: this,
    });
    
  • sap.ui.xmlfragment(this.createId("fragmentId"), "...", this); // Deprecated

    Resulting global ID: "componentId---viewId--fragmentId--controlId" *


πŸ‘‰Fragment.byId("fragmentId", "controlId");

... if only the fragment ID was given without combining with the view ID:

  • this.loadFragment({
      id: "fragmentId",
      name: "...",
      autoPrefixId: false, // Explicitly disabled view ID as prefix
    });
    
  • Fragment.load({
      id: "fragmentId",
      name: "...",
      controller: this,
    });
    
  • sap.ui.xmlfragment("fragmentId", "...", this); // Deprecated

    Resulting global ID: "fragmentId--controlId" *


πŸ‘‰Element.getElementById("controlId") // since 1.119

(Formerly sap.ui.getCore().byId("controlId"))

... if no ID to prefix was given. The below settings are not recommended as all control IDs within the fragment definition will be registered globally without any prefix. The uniqueness of the IDs is not guaranteed!

  • this.loadFragment({ name: "...", autoPrefixId: false }); // Not recommended if no id
  • Fragment.load({ name: "...", controller: this }); // Not recommended
  • sap.ui.xmlfragment("demo.view.MyFragment", this); // Deprecated

    Resulting global ID: "controlId"


* Do not rely on the resulting global ID, for example, concatenating ID parts manually in your application. Always use the dedicated APIs mentioned above such as byId and createId. See Stable IDs: All You Need to Know.


Favor model-first approach over byId

Instead of accessing the fragment controls directly, consider manipulating the UI via data binding. Changes in the model will be reflected in the UI automatically, and, if two-way binding is enabled, user inputs from the UI will be stored in the model directly.


SAP Fiori elements guidelines

When developing Fiori elements extensions, make sure to adhere to the documented compatibility guidelines, especially regarding byId:

[...] Don't access or manipulate controls, properties, models, or other internal objects created by the SAP Fiori elements framework.
[...] Must not access any UI elements that are not defined within your view extensions.

⚠ Caution
If you do not adhere to this guideline, your app may not work with future SAPUI5 versions because SAP Fiori elements might exchange controls for new ones that have a different API.

Siding answered 18/12, 2017 at 16:26 Comment(2)
Why are you using core:Fragment instead of core:FragmentDefinition? – Thetes
Hi @zygimantus, <core:FragmentDefinition> is used to define a Fragment, and that only as a wrapper for multiple controls (XML allows only one root node). With <core:Fragment fragmentName="..." type="XML" />, you're assigning that fragment to the view or to other fragments. See Instantiating Fragments in Declarative Views – Siding
S
27

Looking at the OpenUI5 code at GitHub, it seems that the Fragment delegates the local ID generation to the containing view if the <Fragment/> itself does not have an explicit ID.

So your code this.getView().byId("tab1") should work as soon as you remove the id="fr1" attribute from your <Fragment/> element.

When using explicit IDs there is a static Fragment.byId method to retrieve the control. I guess you have to use it like this:

// Fragment required from "sap/ui/core/Fragment"
var fragmentId = this.getView().createId("fr1");
var tab = Fragment.byId(fragmentId, "tab1");
Schulein answered 23/9, 2016 at 12:51 Comment(0)
B
2

To make it work without explicit fragment ID and without static Fragment.byId() I used the following code snippet:

var prefix = this.getView().createId("").replace("--", "");
var fragment = sap.ui.xmlfragment(prefix, "-- XML fragment name --", this);

after this you can use this.getView().byId("tab1") as with any other control.

Bernoulli answered 23/9, 2016 at 13:38 Comment(2)
Beware that the prefixes like -- and --- used by the framework may change in the future. Thats why it's reccommended to use the public api functions the framework supplies like byId() and createId(). – Schulein
Please, do not fiddle around with the global ID. View IDs don't always start with the user-defined or generated ID but often with component's ID which is also the case when the app is deployed on FLP even if there was no explicite component ID set. It gets even worse when the app has a nested component structure with autoPrefixId enabled, making the global ID very long with multiple and single hyphens in between. In such cases, calling this.getView().createId("").replace("--", "") won't work as expected: "myComponent---myView--".replace("--", "") ==> "myComponent-myView--". – Siding

© 2022 - 2024 β€” McMap. All rights reserved.