UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

630 lines (572 loc) 19.7 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define(["sap/base/assert", "sap/base/Log"], function (assert, Log) { "use strict"; /** * Proxy for tree controls, such as <code>sap.ui.table.TreeTable</code> * or <code>sap.m.Tree</code>. * Provides proxy methods for binding methods, which can act differently in different * binding types. * Enables the usage of OData V4 bindings in tree controls. * * @class * * @param {sap.ui.core.Control} oControl control instance * @param {string} sAggregation aggregation name to be applied to, e.g. in TreeTable "rows" * * @private * @ui5-restricted sap.m.Tree, sap.ui.table.TreeTable */ var TreeBindingProxy = function (oControl, sAggregation) { this._oControl = oControl; this._sAggregation = sAggregation; var oParams = new URLSearchParams(window.location.search); this._bEnableV4 = oParams.get("sap-ui-xx-v4tree") === "true"; }; /** * Determines whether the control's binding is a tree binding. * @returns {boolean} Whether binding is a tree binding. OData V4 bindings * are list bindings even in Tree or TreeTable cases, therefore the method will * always return <code>false</code>, if the binding is a OData V4 binding. */ TreeBindingProxy.prototype.isTreeBinding = function () { var oModel = this._oControl.getModel( this._oControl.getBindingInfo(this._sAggregation).model); if (oModel.isA("sap.ui.model.odata.v4.ODataModel")) { return false; } return true; }; /** * Determines whether the node with the given index is a leaf. * @param {int} iIndex Index of the node * @returns {boolean} Leaf state. If the binding is undefined, the method * will return <code>true</code>. */ TreeBindingProxy.prototype.isLeaf = function (iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return true; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return false; } var oContext = this.getContextByIndex(iIndex); return oContext ? oContext.getProperty("@$ui5.node.isExpanded") === undefined : true; default: var oNode = this.getNodeByIndex(iIndex); return !oBinding.nodeHasChildren(oNode); } }; /** * Retrieves a node object by an index. * @param {int} iIndex Index of the node * @returns {undefined|sap.ui.model.Context|object} Returns <code>undefined</code> * if no binding is given, a binding context if the binding is a OData V4 binding * and by default a node object. */ TreeBindingProxy.prototype.getNodeByIndex = function(iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return undefined; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } return this.getContextByIndex(iIndex); default: return oBinding.getNodeByIndex(iIndex); } }; /** * Retrieves the context of a node by an index. * @param {int} iIndex Index of the node * @returns {undefined|sap.ui.model.Context} Binding context of the node or undefined * if no binding exists */ TreeBindingProxy.prototype.getContextByIndex = function (iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return undefined; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } return oBinding.getContexts(iIndex, 1, 0, true)[0]; default: return oBinding.getContextByIndex(iIndex); } }; /** * Returns the expansion state of a node. * @param {int} iIndex Index of the node * @returns {boolean} Always returns <code>false</code> if binding is undefined, * otherwise returns the expansion state. */ TreeBindingProxy.prototype.isExpanded = function (iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return false; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return false; } var oContext = this.getContextByIndex(iIndex); return oContext ? !!oContext.getProperty("@$ui5.node.isExpanded") : false; default: return oBinding ? oBinding.isExpanded(iIndex) : false; } }; /** * Expands the nodes for the given indices. * @param {int|int[]} vIndices A single index, or an array of indices * of the nodes to be expanded */ TreeBindingProxy.prototype.expand = function (vIndices) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); var mSettings = { proxy: this, binding: oBinding, indices: vIndices, expanded: true }; if (typeof mSettings.indices === "number") { mSettings.indices = [vIndices]; } switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } changeExpandedStateV4(mSettings); break; default: changeExpandedStatePreV4(mSettings); } }; /** * Collapses the nodes for the given indices. * @param {int|int[]} vIndices A single index, or an array of indices of the nodes * to be collapsed */ TreeBindingProxy.prototype.collapse = function (vIndices) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); var mSettings = { proxy: this, binding: oBinding, indices: vIndices, expanded: false }; if (typeof mSettings.indices === "number") { mSettings.indices = [vIndices]; } switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } changeExpandedStateV4(mSettings); break; default: changeExpandedStatePreV4(mSettings); } }; /** * Changes the expansion state for "older" bindings, such as ODataV2, ODataV1, etc. * @param {object} mSettings settings object * @param {object} mSettings.proxy proxy object * @param {sap.ui.model.Binding} mSettings.binding binding * @param {int|int[]} mSettings.indices A single index, or an array of indices * of the to be changed nodes * @param {boolean} mSettings.expanded If true, state will be changed to * expanded otherwise to collapsed. */ function changeExpandedStatePreV4(mSettings) { // Operations need to be performed from the highest index to the lowest. // This ensures correct results with ODataV2 bindings. The indices // are sorted ascending, so the array is iterated backwards. var aValidSortedIndices = mSettings.indices.filter(function(iIndex) { // Only indices of existing, expandable/collapsible nodes must be considered. // Otherwise there might be no change event on the final // expand/collapse (Client + ODataV2). return iIndex >= 0 && iIndex < mSettings.binding.getLength() && !mSettings.proxy.isLeaf(iIndex) && mSettings.expanded !== mSettings.proxy.isExpanded(iIndex); }).sort(function(a, b) { return a - b; }); if (aValidSortedIndices.length === 0) { return; } // Expand/Collapse all nodes except the first, and suppress the change event. for (var i = aValidSortedIndices.length - 1; i > 0; i--) { if (mSettings.expanded) { mSettings.binding.expand(aValidSortedIndices[i], true); } else { mSettings.binding.collapse(aValidSortedIndices[i], true); } } // Expand/Collapse the first node without suppressing the change event. if (mSettings.expanded) { mSettings.binding.expand(aValidSortedIndices[0], false); } else { mSettings.binding.collapse(aValidSortedIndices[0], false); } } /** * Changes the expansion state for ODataV4 bindings. * @param {object} mSettings settings object * @param {object} mSettings.proxy proxy object * @param {sap.ui.model.Binding} mSettings.binding binding * @param {int|int[]} mSettings.indices A single index, or an array of indices of * the to be changed nodes * @param {boolean} mSettings.expanded If true, state will be changed to expanded * otherwise to collapsed. */ function changeExpandedStateV4(mSettings) { for (var i = 0; i < mSettings.indices.length; i++) { var oContext = mSettings.proxy.getContextByIndex(mSettings.indices[i]); if (oContext) { if (mSettings.expanded) { oContext.expand(); } else { oContext.collapse(); } } } } /** * Toggles the expansion state for the given node index. * @param {int} iIndex Index of the node */ TreeBindingProxy.prototype.toggleExpandedState = function (iIndex) { if (this.isExpanded(iIndex)) { this.collapse(iIndex); } else { this.expand(iIndex); } }; /** * Retrieves the contexts for a given range. * @param {int} iStartIndex start index * @param {int} iLength length to retrieve * @param {int} iThreshold threshold * @returns {sap.ui.model.Context[]|object[]} Returns empty array if binding is * <code>undefined</code>, list of contexts in OData V4 case or by default an array * of node objects. */ TreeBindingProxy.prototype.getContexts = function(iStartIndex, iLength, iThreshold, bKeepCurrent) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); var aContexts = []; switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return []; } aContexts = oBinding.getContexts(iStartIndex, iLength, iThreshold, bKeepCurrent); aContexts.forEach(function (oContext) { if (!oContext) { return; } oContext._mProxyInfo = {}; oContext._mProxyInfo.level = getLevelFromObject(oContext, false); oContext._mProxyInfo.isLeaf = oContext.getProperty("@$ui5.node.isExpanded") === undefined; oContext._mProxyInfo.isExpanded = !!oContext .getProperty("@$ui5.node.isExpanded"); }, this); break; default: var aNodes = oBinding ? oBinding.getNodes(iStartIndex, iLength, iThreshold) : []; aNodes.forEach(function (oNode, iIndex) { if (!oNode) { return; } var iRowIndex = iIndex + iStartIndex; var oContext = oNode.context; if (oContext) { oContext._mProxyInfo = {}; if (oNode.nodeState) { oContext._mProxyInfo.nodeState = oNode.nodeState; } oContext._mProxyInfo.level = getLevelFromObject(oNode, true); oContext._mProxyInfo.isLeaf = !oBinding.nodeHasChildren(oNode); oContext._mProxyInfo.isExpanded = oBinding.isExpanded(iRowIndex); aContexts.push(oNode.context); } }, this); break; } return aContexts; }; /** * Retrieves the level property from a given object. * @param {object|sap.ui.model.Context} oObject object to retrieve the level from, either * a node or a binding context * @param {boolean} bIsNode indicates whether the given object is a node or a binding context * @returns {undefined|int} If the object does not exist, returns undefined otherwise the level */ function getLevelFromObject(oObject, bIsNode) { if (oObject) { return bIsNode ? oObject.level + 1 : oObject.getProperty("@$ui5.node.level"); } } function expandToV4(oBinding, iLevel) { var oAggregation = Object.assign(oBinding.getAggregation(), { expandTo: iLevel }); oBinding.setAggregation(oAggregation); } /** * Collapses all nodes. */ TreeBindingProxy.prototype.collapseAll = function () { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } expandToV4(oBinding, 1); break; default: oBinding.collapseToLevel(0); } }; /** * Expands the tree to the given level. * @param {int} iLevel target level */ TreeBindingProxy.prototype.expandToLevel = function (iLevel) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } expandToV4(oBinding, iLevel); break; default: if (oBinding.expandToLevel) { oBinding.expandToLevel(iLevel); } else { assert(oBinding.expandToLevel, "Expanding all nodes to a certain level" + " is not supported with your current binding."); } } }; /** * Sets the root level. * @param {int} iRootLevel root level */ TreeBindingProxy.prototype.setRootLevel = function(iRootLevel) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } throw Error("Setting the root level is not supported with your current binding."); default: if (oBinding.setRootLevel) { oBinding.setRootLevel(iRootLevel); } else { assert(oBinding.setRootLevel, "Setting the root level is not supported with" + " your current binding."); } } }; /** * Sets recursive collapse. * @param {boolean} bCollapseRecursive collapseRecursive */ TreeBindingProxy.prototype.setCollapseRecursive = function(bCollapseRecursive) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: break; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return; } throw Error("Setting 'collapseRecursive' is not supported with your" + " current binding."); default: if (oBinding.setCollapseRecursive) { oBinding.setCollapseRecursive(bCollapseRecursive); } else { assert(oBinding.setCollapseRecursive, "Setting 'collapseRecursive' is" + " not supported with your current binding."); } } }; /** * Retrieves the level for a given index of a node. * @param {int} iIndex Index of the node * @returns {int} level */ TreeBindingProxy.prototype.getLevel = function (iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return undefined; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return 0; } var oContext = this.getContextByIndex(iIndex); return getLevelFromObject(oContext, false); default: var oNode = this.getNodeByIndex(iIndex); return getLevelFromObject(oNode, true); } }; /** * Retrieves the amount of siblings for the given index of the node. * @param {int} iIndex Index of the node * @returns {int} Sibling count. If binding is undefined returns 0. * In ODataV4 case will throw an error. */ TreeBindingProxy.prototype.getSiblingCount = function(iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return 0; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return 0; } throw Error("The number of siblings of a node cannot be determined" + " with your current binding."); default: var oNode = this.getNodeByIndex(iIndex); return oNode && oNode.parent ? oNode.parent.children.length : 0; } }; /** * Retrieves the position of a node in the parent. * @param {int} iIndex Index of the node * @returns Position in parent. If binding is undefined returns -1. * In ODataV4 case will throw an error. */ TreeBindingProxy.prototype.getPositionInParent = function(iIndex) { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: return -1; case "sap.ui.model.odata.v4.ODataListBinding": if (!this._bEnableV4) { Log.error("UnsupportedOperationException: OData V4 is not supported"); return -1; } throw Error("The position of a node in its parent cannot be determined" + " with your current binding."); default: var oNode = this.getNodeByIndex(iIndex); return oNode ? oNode.positionInParent : -1; } }; /** * Checks if the selection is supported. * @returns {boolean} Selection support * * Note: If the binding is either undefined or an ODataV4 binding, selection is not supported. */ TreeBindingProxy.prototype.isSelectionSupported = function() { var oBinding = this._oControl.getBinding(); var sTreeBinding = this._getBindingName(oBinding); switch (sTreeBinding) { case undefined: case "sap.ui.model.odata.v4.ODataListBinding": return false; default: return true; } }; /** * Applies legacy settings to the binding information. Only applicable for pre-ODataV4 * bindings. * @param {object} oBindingInfo binding infos * @param {object} mLegacySettings settings object * @param {int} mLegacySettings.rootLevel root level * @param {object} mLegacySettings.collapseRecursive collapse recursive * @param {object} mLegacySettings.numberOfExpandedLevels number of expanded levels */ TreeBindingProxy.prototype.applyLegacySettingsToBindingInfo = function(oBindingInfo, mLegacySettings) { if (!oBindingInfo.parameters) { oBindingInfo.parameters = {}; } if (!("rootLevel" in oBindingInfo.parameters) && mLegacySettings.rootLevel !== undefined) { oBindingInfo.parameters.rootLevel = mLegacySettings.rootLevel; } if (!("collapseRecursive" in oBindingInfo.parameters) && mLegacySettings.collapseRecursive !== undefined) { oBindingInfo.parameters.collapseRecursive = mLegacySettings.collapseRecursive; } if (!("numberOfExpandedLevels" in oBindingInfo.parameters) && mLegacySettings.numberOfExpandedLevels !== undefined) { oBindingInfo.parameters.numberOfExpandedLevels = mLegacySettings.numberOfExpandedLevels; } }; /** * Retrieves the binding name for a given binding. * @param {sap.ui.model.Binding} oBinding binding object * @returns {string|undefined} Name of the binding or undefined if the binding does not exist. */ TreeBindingProxy.prototype._getBindingName = function (oBinding) { assert(oBinding, "Control does not have a binding."); return oBinding ? oBinding.getMetadata().getName() : undefined; }; return TreeBindingProxy; });