@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
580 lines (521 loc) • 16.1 kB
JavaScript
// valid-jsdoc disabled because this check is validating just the params and return statement and those are all inherited from BaseTreeModifier.
/* eslint-disable valid-jsdoc */
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"sap/base/util/merge",
"sap/base/util/ObjectPath",
"sap/ui/base/BindingParser",
"sap/ui/core/util/reflection/BaseTreeModifier",
"sap/ui/core/util/reflection/XmlTreeModifier",
"sap/ui/core/Element",
"sap/ui/core/Fragment",
"sap/ui/util/XMLHelper"
], function(
merge,
ObjectPath,
BindingParser,
BaseTreeModifier,
XmlTreeModifier,
Element,
Fragment,
XMLHelper
) {
"use strict";
function requireClass(sClassName) {
return new Promise(function(fnResolve, fnReject) {
sap.ui.require([sClassName],
function(oClassObject) { fnResolve(oClassObject); },
function() {
fnReject(new Error("Required control '" + sClassName
+ "' couldn't be created asynchronously"));
}
);
});
}
/**
* Static utility class to access ManagedObjects in a harmonized way with XMLNodes.
*
* @namespace sap.ui.core.util.reflection.JsControlTreeModifier
* @extends sap.ui.core.util.reflection.BaseTreeModifier
* @private
* @ui5-restricted
* @since 1.56.0
*/
const JsControlTreeModifier = /** @lends sap.ui.core.util.reflection.JsControlTreeModifier */ {
targets: "jsControlTree",
/**
* @inheritDoc
*/
setVisible: function(oControl, bVisible) {
if (oControl.setVisible) {
this.unbindProperty(oControl, "visible");
oControl.setVisible(bVisible);
} else {
throw new Error("Provided control instance has no setVisible method");
}
},
/**
* @inheritDoc
*/
getVisible: function(oControl) {
if (oControl.getVisible) {
return Promise.resolve(oControl.getVisible());
} else {
return Promise.reject(new Error("Provided control instance has no getVisible method"));
}
},
/**
* @inheritDoc
*/
setStashed: async function(oControl, bStashed) {
bStashed = !!bStashed;
if (oControl.unstash) {
// check if the control is stashed and should be unstashed
if (oControl.isStashed() === true && bStashed === false) {
oControl = await oControl.unstash(true);
}
// ensure original control's visible property is set
if (oControl.setVisible) {
this.setVisible(oControl, !bStashed);
}
return oControl;
} else {
throw new Error("Provided control instance has no unstash method");
}
},
/**
* @inheritDoc
*/
getStashed: async function(oControl) {
if (oControl.isStashed) {
if (oControl.isStashed()) {
return true;
}
const bIsVisible = await this.getVisible(oControl);
return !bIsVisible;
}
throw new Error("Provided control instance has no isStashed method");
},
/**
* @inheritDoc
*/
bindProperty: function(oControl, sPropertyName, vBindingInfos) {
oControl.bindProperty(sPropertyName, vBindingInfos);
},
/**
* @inheritDoc
*/
unbindProperty: function(oControl, sPropertyName) {
if (oControl) {
oControl.unbindProperty(sPropertyName, /*bSuppressReset = */true);
}
},
/**
* @inheritDoc
*/
setProperty: function(oControl, sPropertyName, vPropertyValue) {
const oMetadata = oControl.getMetadata().getPropertyLikeSetting(sPropertyName);
let oBindingParserResult;
let bError;
this.unbindProperty(oControl, sPropertyName);
try {
oBindingParserResult = BindingParser.complexParser(vPropertyValue, undefined, true);
} catch (error) {
bError = true;
}
//For compatibility with XMLTreeModifier the value should be serializable
if (oMetadata) {
if (this._isSerializable(vPropertyValue)) {
if (oBindingParserResult && typeof oBindingParserResult === "object" || bError) {
vPropertyValue = this._escapeSpecialCharactersInString(vPropertyValue);
}
const sPropertySetter = oMetadata._sMutator;
oControl[sPropertySetter](vPropertyValue);
} else {
throw new TypeError("Value cannot be stringified", "sap.ui.core.util.reflection.JsControlTreeModifier");
}
}
},
/**
* @inheritDoc
*/
getProperty: function(oControl, sPropertyName) {
const oMetadata = oControl.getMetadata().getPropertyLikeSetting(sPropertyName);
let oProperty;
if (oMetadata) {
const sPropertyGetter = oMetadata._sGetter;
oProperty = oControl[sPropertyGetter]();
}
return Promise.resolve(oProperty);
},
/**
* @inheritDoc
*/
isPropertyInitial: function(oControl, sPropertyName) {
return oControl.isPropertyInitial(sPropertyName);
},
/**
* @inheritDoc
*/
setPropertyBinding: function(oControl, sPropertyName, oPropertyBinding) {
this.unbindProperty(oControl, sPropertyName);
const mSettings = {};
mSettings[sPropertyName] = oPropertyBinding;
return oControl.applySettings(mSettings);
},
/**
* @inheritDoc
*/
getPropertyBinding: function(oControl, sPropertyName) {
return oControl.getBindingInfo(sPropertyName);
},
/**
* @inheritDoc
*/
createAndAddCustomData: async function(oControl, sCustomDataKey, sValue, oAppComponent) {
const oCustomData = await this.createControl("sap.ui.core.CustomData", oAppComponent);
this.setProperty(oCustomData, "key", sCustomDataKey);
this.setProperty(oCustomData, "value", sValue);
return this.insertAggregation(oControl, "customData", oCustomData, 0);
},
/**
* @inheritDoc
*/
getCustomDataInfo: function(oControl, sCustomDataKey) {
let oCustomData;
if (oControl.getCustomData) {
oControl.getCustomData().some(function(oCurrentCustomData) {
if (oCurrentCustomData.getKey() === sCustomDataKey) {
oCustomData = oCurrentCustomData;
return true;
}
return false;
});
}
if (oCustomData) {
return {
customData: oCustomData,
customDataValue: oCustomData.getValue()
};
} else {
return {};
}
},
/**
* @inheritDoc
*/
createControl: async function(sClassName, oAppComponent, oView, oSelector, mSettings) {
sClassName = sClassName.replace(/\./g,"/");
if (this.bySelector(oSelector, oAppComponent)) {
const sErrorMessage = "Can't create a control with duplicated ID " + (oSelector.id || oSelector);
return Promise.reject(sErrorMessage);
}
const ClassObject = sap.ui.require(sClassName) || await requireClass(sClassName);
const sId = this.getControlIdBySelector(oSelector, oAppComponent);
return new ClassObject(sId, mSettings);
},
/**
* @inheritDoc
*/
applySettings: function(oControl, mSettings) {
return Promise.resolve(oControl.applySettings(mSettings));
},
/**
* @inheritDoc
*/
_byId: function(sId) {
return Element.getElementById(sId);
},
/**
* @inheritDoc
*/
getId: function(oControl) {
return oControl.getId();
},
/**
* @inheritDoc
*/
getParent: function(oControl) {
return oControl.getParent?.();
},
/**
* @inheritDoc
*/
getControlMetadata: function(oControl) {
return Promise.resolve(oControl?.getMetadata());
},
/**
* @inheritDoc
*/
getControlType: function(oControl) {
return oControl?.getMetadata().getName();
},
/**
* @inheritDoc
*/
setAssociation: function(vParent, sName, sId) {
const oMetadata = vParent.getMetadata().getAssociation(sName);
oMetadata.set(vParent, sId);
},
/**
* @inheritDoc
*/
getAssociation: function(vParent, sName) {
const oMetadata = vParent.getMetadata().getAssociation(sName);
return oMetadata.get(vParent);
},
/**
* @inheritDoc
*/
getAllAggregations: function(oParent) {
return Promise.resolve(oParent.getMetadata().getAllAggregations());
},
/**
* @inheritDoc
*/
getAggregation: async function(oParent, sName) {
const oAggregation = await this.findAggregation(oParent, sName);
if (oAggregation) {
return oParent[oAggregation._sGetter]();
}
return undefined;
},
/**
* @inheritDoc
*/
insertAggregation: async function(oParent, sName, oObject, iIndex) {
//special handling without invalidation for customData
if (sName === "customData") {
oParent.insertAggregation(sName, oObject, iIndex, /*bSuppressInvalidate=*/true);
return;
}
const oAggregation = await this.findAggregation(oParent, sName);
if (oAggregation) {
if (oAggregation.multiple) {
const iInsertIndex = iIndex || 0;
oParent[oAggregation._sInsertMutator](oObject, iInsertIndex);
} else {
oParent[oAggregation._sMutator](oObject);
}
}
},
/**
* @inheritDoc
*/
removeAggregation: async function(oParent, sName, oObject) {
//special handling without invalidation for customData
if (sName === "customData") {
oParent.removeAggregation(sName, oObject, /*bSuppressInvalidate=*/true);
return;
}
const oAggregation = await this.findAggregation(oParent, sName);
if (oAggregation) {
oParent[oAggregation._sRemoveMutator](oObject);
}
},
/**
* @inheritDoc
*/
moveAggregation: async function(oSourceParent, sSourceAggregationName, oTargetParent, sTargetAggregationName, oObject, iIndex) {
let oSourceAggregation;
let oTargetAggregation;
// customData aggregations are always multiple and use the standard "removeAggregation" mutator
if (sSourceAggregationName === "customData") {
oSourceParent.removeAggregation(sSourceAggregationName, oObject, /*bSuppressInvalidate=*/true);
} else {
oSourceAggregation = await this.findAggregation(oSourceParent, sSourceAggregationName);
}
if (sTargetAggregationName === "customData") {
oTargetParent.insertAggregation(sTargetAggregationName, oObject, iIndex, /*bSuppressInvalidate=*/true);
} else {
oTargetAggregation = await this.findAggregation(oTargetParent, sTargetAggregationName);
}
if (oSourceAggregation && oTargetAggregation) {
oSourceParent[oSourceAggregation._sRemoveMutator](oObject);
if (oTargetAggregation.multiple) {
oTargetParent[oTargetAggregation._sInsertMutator](oObject, iIndex);
} else {
oTargetParent[oTargetAggregation._sMutator](oObject);
}
}
},
/**
* @inheritDoc
*/
replaceAllAggregation: async function(oControl, sAggregationName, aNewControls) {
const oAggregation = await this.findAggregation(oControl, sAggregationName);
oControl[oAggregation._sRemoveAllMutator]();
aNewControls.forEach((oNewControl, iIndex) => {
oControl[oAggregation._sInsertMutator](oNewControl, iIndex);
});
},
/**
* @inheritDoc
*/
removeAllAggregation: async function(oControl, sName) {
//special handling without invalidation for customData
if (sName === "customData") {
oControl.removeAllAggregation(sName, /*bSuppressInvalidate=*/true);
} else {
const oAggregation = await this.findAggregation(oControl, sName);
if (oAggregation) {
oControl[oAggregation._sRemoveAllMutator]();
}
}
},
/**
* @inheritDoc
*/
getBindingTemplate: function(oControl, sAggregationName) {
const oBinding = oControl.getBindingInfo(sAggregationName);
return Promise.resolve(oBinding?.template);
},
/**
* @inheritDoc
*/
updateAggregation: async function(oControl, sAggregationName) {
const oAggregation = await this.findAggregation(oControl, sAggregationName);
if (oAggregation && oControl.getBinding(sAggregationName)) {
oControl[oAggregation._sDestructor]();
oControl.updateAggregation(sAggregationName);
}
},
/**
* @inheritDoc
*/
findIndexInParentAggregation: async function(oControl) {
const oParent = this.getParent(oControl);
if (!oParent) {
return -1;
}
const sParentAggregationName = await this.getParentAggregationName(oControl);
// we need all controls in the aggregation
const aControlsInAggregation = await this.getAggregation(oParent, sParentAggregationName);
// if aControls is an array:
if (Array.isArray(aControlsInAggregation)) {
// then the aggregation is multiple and we can find the index of oControl in the array
return aControlsInAggregation.indexOf(oControl);
} else {
// if aControlsInAggregation is not an array, then the aggregation is
// of type 0..1 and aControlsInAggregation is the oControl provided
// to the function initially, so its index is 0
return 0;
}
},
/**
* @inheritDoc
*/
getParentAggregationName: function(oControl) {
return Promise.resolve(oControl.sParentAggregationName);
},
/**
* @inheritDoc
*/
findAggregation: function(oControl, sAggregationName) {
if (oControl?.getMetadata) {
const oMetadata = oControl.getMetadata();
const oAggregations = oMetadata.getAllAggregations();
return Promise.resolve(oAggregations[sAggregationName]);
}
return Promise.resolve(undefined);
},
/**
* @inheritDoc
*/
validateType: async function(oControl, oAggregationMetadata, oParent, sFragment) {
const sTypeOrInterface = oAggregationMetadata.type;
const oAggregation = await this.getAggregation(oParent, oAggregationMetadata.name);
// if aggregation is not multiple and already has element inside, then it is not valid for element
if (oAggregationMetadata.multiple === false && oAggregation && oAggregation.length > 0) {
return false;
}
return oControl.isA(sTypeOrInterface);
},
/**
* @inheritDoc
*/
instantiateFragment: async function(sFragment, sNamespace, oView) {
const oInputFragment = XMLHelper.parse(sFragment);
const oFragment = await this._checkAndPrefixIdsInFragment(oInputFragment, sNamespace);
const vNewControls = await Fragment.load({
definition: oFragment,
sId: oView && oView.getId(),
controller: oView.getController()
});
if (vNewControls && !Array.isArray(vNewControls)) {
return [vNewControls];
}
return vNewControls || [];
},
/**
* @inheritDoc
*/
templateControlFragment: async function(sFragmentName, mPreprocessorSettings, oView) {
const oFragment = await BaseTreeModifier._templateFragment(
sFragmentName,
mPreprocessorSettings
);
const oController = oView?.getController();
return Fragment.load({
definition: oFragment,
controller: oController
});
},
/**
* @inheritDoc
*/
destroy: function(oControl, bSuppressInvalidate) {
oControl.destroy(bSuppressInvalidate);
},
_getFlexCustomData: function(oControl, sType) {
const oCustomData = typeof oControl === "object"
&& typeof oControl.data === "function"
&& oControl.data("sap-ui-custom-settings");
return ObjectPath.get(["sap.ui.fl", sType], oCustomData);
},
/**
* @inheritDoc
*/
bindAggregation: function(oControl, sAggregationName, oBindingInfo) {
return Promise.resolve(oControl.bindAggregation(sAggregationName, oBindingInfo));
},
/**
* @inheritDoc
*/
unbindAggregation: function(oControl, sAggregationName) {
// bSuppressReset is not supported
return Promise.resolve(oControl.unbindAggregation(sAggregationName));
},
/**
* @inheritDoc
*/
getExtensionPointInfo: async function(sExtensionPointName, oView) {
const oViewNode = (oView._xContent) ? oView._xContent : oView;
const oExtensionPointInfo = await XmlTreeModifier.getExtensionPointInfo(sExtensionPointName, oViewNode);
if (oExtensionPointInfo) {
// decrease the index by 1 to get the index of the extension point itself for js-case
oExtensionPointInfo.index--;
oExtensionPointInfo.parent = oExtensionPointInfo.parent && this._byId(oView.createId(oExtensionPointInfo.parent.getAttribute("id")));
oExtensionPointInfo.defaultContent = oExtensionPointInfo.defaultContent
.map((oNode) => {
const sId = oView.createId(oNode.getAttribute("id"));
return this._byId(sId);
})
.filter((oControl) => {
return !!oControl;
});
}
return oExtensionPointInfo;
}
};
return merge(
{} /* target object, to avoid changing of original modifier */,
BaseTreeModifier,
JsControlTreeModifier
);
},
/* bExport= */true);