3

I understand that OpenUI5's has a registry of instantiated controls and can be queried with sap.ui.getCore().byId.

But, is there a way to get a full list of instances in the control registry?

Something like this:

var aControls = sap.ui.getCore().allControls();
Jaro
  • 1,757
  • 1
  • 15
  • 36
Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115

3 Answers3

8

≥ UI5 1.67

With the commit 54df6ca, no more workarounds are needed. The modules sap/ui/core/Element and sap/ui/core/Component provide public APIs such as .all(), .filter(), .forEach(), .get(), and .size. Take a look at the API reference respectively:

API reference: sap/ui/core/Element.registry.*

Registry of all sap.ui.core.Elements that currently exist.

API reference: sap/ui/core/Component.registry.*

Registry of all Components that currently exist.

Sample

sap.ui.require([
  "sap/ui/core/Element" // or "sap/ui/core/Component"
], ({ registry }) => {
  const registeredStuff = registry.all(); // or .filter(fn, this), .forEach(fn, this), .get("..."), .size
  console.log(registeredStuff);
});

ui5 get registered elements


If the application runs in UI5 below 1.67, keep reading for workarounds..


≤ UI5 1.66 (Original answer)

Is there a way to get a full list of instances in the control registry?

With a bit of cheating, yes!

Option 1 - via .mElements from the real core

getRegisteredElements: function() {
  let core;
  const fakePlugin = {
    startPlugin: realCore => core = realCore
  };
  // "Core" required from "sap/ui/core/Core"
  Core.registerPlugin(fakePlugin);
  Core.unregisterPlugin(fakePlugin);
  return core.mElements;
},

The API registerPlugin awaits an object that contains a method startPlugin (and stopPlugin) as an argument. It invokes the startPlugin method immediately as long as the core is initialized. As a parameter, we're getting the real core from which we can get the map of all registered elements via mElements (thanks to the hint from Serban).

UI5 - get registered elements

⚠️ .mElements is a private member. It has been removed in future UI5 releases!
⚠️ Core.registerPlugin() and .unregisterPlugin() are deprecated since 1.73.

Option 2 - via Core.byFieldGroupId (controls only)

getRegisteredControls: function() { // "Core" required from "sap/ui/core/Core"
  return Core.byFieldGroupId("" || []); // pass an empty string or an empty array!
},

This will return an array of all registered elements that are of type sap.ui.core.Control (source). Passing "" or [] ensures that all controls are returned, regardless whether the control has a field group ID or not.

Option 3 - via Opa Plugin (for tests)

When writing tests, another option is to use the dedicated public API getAllControls from sap.ui.test.OpaPlugin:

new OpaPlugin().getAllControls(); // "OpaPlugin" required from "sap/ui/test/OpaPlugin"

Although the name suggests it will return Controls, it actually returns Element instances as well.
The plugin provides some other interesting APIs too, such as getMatchingControls (with options to provide controlType?, visible?, interactable?, etc..) which might be useful.

Boghyon Hoffmann
  • 17,103
  • 12
  • 72
  • 170
  • Thanks for the update regarding 1.67+, could you, please, provide some code snippet for `sap.ui.core.Element.registry.filter(callback, thisArg?)`? Thanks. – Mike Jul 06 '19 at 12:16
  • 1
    @MikeB. Here is an example: visit https://ui5.sap.com and enter `sap.ui.require(["sap/ui/core/Element"], El => console.log(El.registry.filter(el => el.isA("sap.m.Button"))));` in the console. The browser will log all instances that are of type `sap.m.Button`. Functions like `.filter` are called _[higher order functions](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99)_. As an argument, you just need to pass a function that returns a boolean value. That function will than be called for every element in the registry. – Boghyon Hoffmann Jul 06 '19 at 13:29
  • thanks for the code sample. It works for me, but now I want not just get the elements, but also update their values. But it looks like that `filter` returns a _new_ array. Is there a way to iterate and update the values of multiple `sap.m.Input` items? Thanks. – Mike Jul 06 '19 at 20:57
  • 1
    @MikeB. What is the actual intent? Would be nice if you could open a new question with more details so that more people can help with better answers – Boghyon Hoffmann Jul 06 '19 at 22:08
  • I've opened a question: https://stackoverflow.com/questions/56920185, I can reset the input fields, but I'm not sure regarding the performance point of view. – Mike Jul 07 '19 at 10:35
1

There is no currently documented way of obtaining the complete list of elements. The elements are registered in the mElements private map (object) inside the core instance. You can check this object's usages inside the Core source code. It is never exposed directly to the outside world via a method.

Normally, you would be able to simply go around the 'private' access level which is just a convention in JavaScript and just do sap.ui.getCore().mElements, but this will not work in this case. This is because the core is wrapped into an Interface (through a closure) which only holds proxies to the public methods. This implies that you have no way of getting the real core instance from the sap.ui.getCore() call, so you cannot access the mElements property from there.

I don't know any way of obtaining the 'raw' core instance (and normally it should not be possible - the guys at SAP intended to do defensive programming here and not allow users to meddle with the core internals). If you would manage to obtain it through some way, then you could access this 'private' property and obtain the element list (actually, a map between ID and reference).

Serban Petrescu
  • 5,127
  • 2
  • 17
  • 34
0

There is no public API for this.

If your page has a root view you could create a recuresive function that traverse the view tree by applying getters for each control aggregations and going over all they content (and so on).

You can use the control's getMetadata() method for getting the public aggregations names.

Note that in this way you only have access to controls on the public aggregations and not to inner ones.

amiramw
  • 506
  • 2
  • 10