@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
247 lines (232 loc) • 9.46 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides helper module:sap/ui/core/ComponentSupport
sap.ui.define([
'sap/ui/base/DataType',
'sap/ui/core/ComponentContainer',
'sap/ui/core/library',
"sap/base/future",
"sap/base/Log",
"sap/base/util/ObjectPath",
"sap/base/strings/camelize"
], function(
DataType,
ComponentContainer,
library,
future,
Log,
ObjectPath,
camelize
) {
"use strict";
var ComponentLifecycle = library.ComponentLifecycle;
var ComponentContainerMetadata = ComponentContainer.getMetadata();
/**
* The module <code>sap/ui/core/ComponentSupport</code> provides functionality
* which is used to find declared Components in the HTML page and to create
* the Component instances which will be put into a {@link sap.ui.core.ComponentContainer}.
*
* The {@link module:sap/ui/core/ComponentSupport.run} function is called automatically once the module has been required.
* This allows declarative support for components.
*
* <h3>Usage</h3>
* To enable the <code>sap/ui/core/ComponentSupport</code> include it as the <code>oninit</code> module in the bootstrap:
* <pre>
* <script id="sap-ui-bootstrap"
* src="/resources/sap-ui-core.js"
* ...
* data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
* ...>
* </script>
* </pre>
*
* To load and render components inside the HTML page, a special data attribute has to be specified
* on the respective DOM elements: <code>data-sap-ui-component</code>.
* All DOM elements marked with this data attribute will be regarded as container elements for the created
* <code>ComponentContainer</code> instances.
*
* <pre>
* <body id="content" class="sapUiBody sapUiSizeCompact">
* ...
* <div data-sap-ui-component
* data-id="container"
* data-name="sap.ui.core.samples.formatting"
* ...
* data-handle-validation="true"
* ...>
* </div>
* ...
* </body>
* </pre>
*
* <h3>Configuration</h3>
* All configuration settings for the <code>ComponentContainer</code> have to be defined as <code>data</code>
* attributes on the respective HTML tags.
* Each data attribute will be interpreted as a setting and parsed considering
* the data type of the matching property in the <code>ComponentContainer</code>.
*
* <b>NOTE:</b>
* The following <code>data</code> attributes for registering event handlers have been deprecated since
* UI5 version 1.120 and won't work in the next major version because of the removal of accessing the
* global namespace:
* <ul>
* <li><code>data-component-created</code></li>
* <li><code>data-component-failed</code></li>
* </ul>
*
* Alternatively, you can provide your own module in the bootstrap via <code>oninit</code>, in which you
* create an instance of the {@link sap.ui.core.ComponentContainer ComponentContainer} in the JavaScript
* code.
*
* As HTML is case-insensitive, in order to define a property with upper-case characters, you have to "escape" them
* with a hyphen character, similar to CSS attributes.
* The following code gives an example:
*
* <pre>
* <div data-sap-ui-component ... data-handle-validation="true" ...></div>
* </pre>
*
* <b>Beware:</b>
*
* The <code>ComponentSupport</code> module enforces asynchronous loading of the
* respective component and its library dependencies.
* This is done by applying default settings for the following properties of the <code>ComponentContainer</code>:
*
* <ul>
* <li><code>async</code> {boolean} (<b>forced to <code>true</code></b>)</li>
* <li><code>manifest</code> {boolean|string} (<b>forced to <code>true</code> if no string is provided to ensure manifest first</b>)</li>
* <li><code>lifecycle</code> {sap.ui.core.ComponentLifecycle} (defaults to <code>Container</code>)</li>
* <li><code>autoPrefixId</code> {boolean} (defaults to <code>true</code>)</li>
* </ul>
*
* See {@link topic:82a0fcecc3cb427c91469bc537ebdddf Declarative API for Initial Components}.
*
* @author SAP SE
* @public
* @since 1.58.0
* @version 1.147.0
* @namespace
* @alias module:sap/ui/core/ComponentSupport
*/
var ComponentSupport = function() {
};
/**
* Find all DOM elements with the attribute <code>data-sap-ui-component</code>
* and parse the attributes from these DOM elements for the settings of the
* <code>ComponentContainer</code> which will be placed into these DOM elements.
*
* This function is called automatically once the module has been required.
*
* @public
*/
ComponentSupport.run = function() {
var aElements = ComponentSupport._find();
for (var i = 0, l = aElements.length; i < l; i++) {
var oElement = aElements[i];
Log.debug("Parsing element " + oElement.outerHTML, "", "sap/ui/core/ComponentSupport");
var mSettings = ComponentSupport._parse(oElement);
ComponentSupport._applyDefaultSettings(mSettings);
Log.debug("Creating ComponentContainer with the following settings", JSON.stringify(mSettings, 0, 2), "sap/ui/core/ComponentSupport");
new ComponentContainer(mSettings).placeAt(oElement);
// Remove marker so that the element won't be processed again in case "run" is called again
oElement.removeAttribute("data-sap-ui-component");
}
};
/**
* Find all DOM elements with the attribute <code>data-sap-ui-component</code>
* and parse the attributes from these DOM elements for the settings of the
* <code>ComponentContainer</code> which will be placed into these DOM elements.
*
* @returns {NodeList} array of <code>Node</code>s
* @private
*/
ComponentSupport._find = function() {
return document.querySelectorAll("[data-sap-ui-component]");
};
/**
* Parses the attributes of the given DOM element and creates a settings
* map. Each attribute starting with <code>data-</code> will be interpret
* as setting and be parsed by considering the data type of the matching
* property in the <code>ComponentContainer</code>. As HTML is case-insensitive
* camel cased properties have to be written in hyphenated form, e.g.
* <code>componentCreated</code> as <code>data-component-created</code>.
*
* @param {Node} oElement the DOM element to be parsed
* @returns {object} settings map
* @private
*/
ComponentSupport._parse = function(oElement) {
var mSettings = {};
for (var i = 0, l = oElement.attributes.length; i < l; i++) {
var oAttribute = oElement.attributes[i];
// parse every data- property besides data-sap-ui-component
var oParsedAttributeName = /^data-((?!sap-ui-component).+)/g.exec(oAttribute.name);
if (oParsedAttributeName) {
var sKey = camelize(oParsedAttributeName[1]);
var oValue = oAttribute.value;
// special handling for id property
if (sKey !== "id") {
var oProperty = ComponentContainerMetadata.getProperty(sKey);
var oEvent = !oProperty && ComponentContainerMetadata.getEvent(sKey);
if (!oProperty && !oEvent) {
future.warningThrows("Property or event \"" + sKey + "\" does not exist in sap.ui.core.ComponentContainer.", { suffix: "It will be ignored."});
continue;
}
if (oProperty) {
var oType = DataType.getType(oProperty.type);
if (!oType) {
throw new Error("Property \"" + oProperty.name + "\" has no known type");
}
oValue = oType.parseValue(oValue);
}
/**
* @deprecated
*/
if (oEvent) {
var fnCallback = ObjectPath.get(oValue);
if (typeof fnCallback !== "function") {
throw new Error("Callback handler for event \"" + oEvent.name + "\" not found");
}
oValue = fnCallback;
}
}
mSettings[sKey] = oValue;
}
}
return mSettings;
};
/**
* Applies the default settings for the <code>ComponentContainer</code>
* for some properties such as:
* <ul>
* <li><code>async</code> {boolean} (<b>forced to <code>true</code></b>)</li>
* <li><code>manifest</code> {boolean|string} (<b>forced to <code>true</code> if no string is provided to ensure manifest first</b>)</li>
* <li><code>lifecycle</code> {sap.ui.core.ComponentLifecycle} (defaults to <code>Container</code>)</li>
* <li><code>autoPrefixId</code> {boolean} (defaults to <code>true</code>)</li>
* </ul>
*
* @param {object} mSettings settings map to be adopted
* @private
*/
ComponentSupport._applyDefaultSettings = function(mSettings) {
// force async loading behavior
mSettings.async = true;
// ignore boolean values for manifest property and force manifest first
if (mSettings.manifest === undefined || mSettings.manifest === "true") {
mSettings.manifest = true;
} else if (mSettings.manifest === "false") {
future.errorThrows("Defining \"manifest=false\" for ComponentContainer of component \"" + mSettings.name + "\" is not supported by ComponentSupport.", {
suffix: "Forcing \"manifest=true\""}, "", "sap/ui/core/ComponentSupport");
mSettings.manifest = true;
}
// different default value behavior for declarative components
mSettings.lifecycle = mSettings.lifecycle === undefined ? ComponentLifecycle.Container : mSettings.lifecycle;
mSettings.autoPrefixId = mSettings.autoPrefixId === undefined ? true : mSettings.autoPrefixId;
};
// Automatically run once
ComponentSupport.run();
return ComponentSupport;
});