UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

1,007 lines (974 loc) 117 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /** * Model and related classes like bindings for OData V4. * * <b>Note:</b> Smart controls (<code>sap.ui.comp</code> library) do not support the SAPUI5 OData V4 * model. Also controls such as {@link sap.ui.table.TreeTable} and * {@link sap.ui.table.AnalyticalTable} are not supported together with the SAPUI5 OData V4 model. * The interface for applications has been changed for easier and more efficient use of the model. * For a summary of these changes, see * {@link topic:abd4d7c7548d4c29ab8364d3904a6d74 Changes Compared to OData V2 Model}. * * @name sap.ui.model.odata.v4 * @namespace * @public * @since 1.37.0 */ //Provides class sap.ui.model.odata.v4.ODataModel sap.ui.define([ "./ODataContextBinding", "./ODataListBinding", "./ODataMetaModel", "./ODataPropertyBinding", "./SubmitMode", "./lib/_GroupLock", "./lib/_Helper", "./lib/_MetadataRequestor", "./lib/_Parser", "./lib/_Requestor", "sap/base/assert", "sap/base/Log", "sap/ui/base/SyncPromise", "sap/ui/core/cache/CacheManager", "sap/ui/core/Configuration", "sap/ui/core/library", "sap/ui/core/message/Message", "sap/ui/model/BindingMode", "sap/ui/model/Context", "sap/ui/model/Model", "sap/ui/model/odata/OperationMode", "sap/ui/thirdparty/URI" ], function (ODataContextBinding, ODataListBinding, ODataMetaModel, ODataPropertyBinding, SubmitMode, _GroupLock, _Helper, _MetadataRequestor, _Parser, _Requestor, assert, Log, SyncPromise, CacheManager, Configuration, coreLibrary, Message, BindingMode, BaseContext, Model, OperationMode, URI) { "use strict"; var sClassName = "sap.ui.model.odata.v4.ODataModel", // system query options allowed within a $expand query option aExpandQueryOptions = ["$count", "$expand", "$filter", "$levels", "$orderby", "$search", "$select"], // binding-specific parameters allowed in getKeepAliveContext aGetKeepAliveParameters = ["$$groupId", "$$patchWithoutSideEffects", "$$updateGroupId"], MessageType = coreLibrary.MessageType, aMessageTypes = [ undefined, MessageType.Success, MessageType.Information, MessageType.Warning, MessageType.Error ], mSupportedEvents = { dataReceived : true, dataRequested : true, messageChange : true, propertyChange : true, sessionTimeout : true }, mSupportedParameters = { annotationURI : true, autoExpandSelect : true, earlyRequests : true, groupId : true, groupProperties : true, httpHeaders : true, ignoreAnnotationsFromMetadata : true, metadataUrlParams : true, odataVersion : true, operationMode : true, serviceUrl : true, sharedRequests : true, supportReferences : true, synchronizationMode : true, updateGroupId : true }, // system query options allowed in mParameters aSystemQueryOptions = ["$apply", "$count", "$expand", "$filter", "$orderby", "$search", "$select"], // valid header values: non-empty, only US-ASCII, no control chars rValidHeader = /^[ -~]+$/, /** * Constructor for a new ODataModel. * * @param {object} mParameters * The parameters * @param {string|string[]} [mParameters.annotationURI] * The URL (or an array of URLs) from which the annotation metadata are loaded. * The annotation files are merged into the service metadata in the given order (last one * wins). The same annotations are overwritten; if an annotation file contains other * elements (like a type definition) that are already merged, an error is thrown. * Supported since 1.41.0 * @param {boolean} [mParameters.autoExpandSelect] * Whether the OData model's bindings automatically generate $select and $expand system * query options from the binding hierarchy. Note: Dynamic changes to the binding * hierarchy are not supported. This parameter is supported since 1.47.0, and since 1.75.0 * it also enables property paths containing navigation properties in * <code>$select</code>. * @param {boolean} [mParameters.earlyRequests] * Whether the following is requested at the earliest convenience: * <ul> * <li> root $metadata document and annotation files; * <li> the security token. * </ul> * Note: The root $metadata document and annotation files are just requested but not yet * converted from XML to JSON unless really needed. * Supported since 1.53.0. * <b>BEWARE:</b> The default value may change to <code>true</code> in later releases. * You may also set {@link topic:26ba6a5c1e5c417f8b21cce1411dba2c Manifest Model Preload} * in order to further speed up the start of a UI5 component. * @param {string} [mParameters.groupId="$auto"] * Controls the model's use of batch requests: '$auto' bundles requests from the model in * a batch request which is sent automatically before rendering; '$direct' sends requests * directly without batch; other values result in an error * @param {object} [mParameters.groupProperties] * Controls the use of batch requests for application groups. A map of application * group IDs having an object with exactly one property <code>submit</code>. Valid values * are 'API', 'Auto', 'Direct' see {@link sap.ui.model.odata.v4.SubmitMode}. Supported * since 1.51.0 * @param {object} [mParameters.httpHeaders] * Map of HTTP header names to their values, see {@link #changeHttpHeaders} * @param {boolean} [mParameters.ignoreAnnotationsFromMetadata] * Whether to ignore all annotations from service metadata and "cross-service references"; * only the value <code>true</code> is allowed. Only annotations from annotation files * (see parameter "annotationURI") are loaded. This parameter is not inherited by value * list models. @experimental as of version 1.111.0 * @param {object} [mParameters.metadataUrlParams] * Additional map of URL parameters used specifically for $metadata requests. Note that * "sap-context-token" applies only to the service's root $metadata, but not to * "cross-service references". Supported since 1.81.0 * @param {string} [mParameters.odataVersion="4.0"] * The version of the OData service. Supported values are "2.0" and "4.0". * @param {sap.ui.model.odata.OperationMode} [mParameters.operationMode] * The operation mode for filtering and sorting. Since 1.39.0, the operation mode * {@link sap.ui.model.odata.OperationMode.Server} is supported. All other operation modes * including <code>undefined</code> lead to an error if 'vFilters' or 'vSorters' are given * or if {@link sap.ui.model.odata.v4.ODataListBinding#filter} or * {@link sap.ui.model.odata.v4.ODataListBinding#sort} is called. * @param {string} mParameters.serviceUrl * Root URL of the service to request data from. The path part of the URL must end with a * forward slash according to OData V4 specification ABNF, rule "serviceRoot". You may * append OData custom query options to the service root URL separated with a "?", for * example "/MyService/?custom=foo". See specification <a href= * "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html#_Custom_Query_Options" * >"OData Version 4.0 Part 2: URL Conventions", "5.2 Custom Query Options"</a>. OData * system query options and OData parameter aliases lead to an error. * @param {boolean} [mParameters.sharedRequests] * Whether all list bindings for the same resource path share their data, so that it is * requested only once; only the value <code>true</code> is allowed; see parameter * "$$sharedRequest" of {@link #bindList}. Additionally, * {@link sap.ui.model.BindingMode.OneWay} becomes the default binding mode and * {@link sap.ui.model.BindingMode.TwoWay} is forbidden. Note: This makes all bindings * read-only, so it may be especially useful for value list models. Supported since 1.80.0 * @param {boolean} [mParameters.supportReferences=true] * Whether <code>&lt;edmx:Reference></code> and <code>&lt;edmx:Include></code> directives * are supported in order to load schemas on demand from other $metadata documents and * include them into the current service ("cross-service references"). * @param {string} [mParameters.synchronizationMode] * (Controls synchronization between different bindings which refer to the same data for * the case data changes in one binding. Must be set to 'None' which means bindings are * not synchronized at all; all other values are not supported and lead to an error.) * <b>deprecated:</b> As of version 1.110.0, this parameter is optional; see also * {@link topic:648e360fa22d46248ca783dc6eb44531 Data Reuse} * @param {string} [mParameters.updateGroupId] * The group ID that is used for update requests. If no update group ID is specified, * <code>mParameters.groupId</code> is used. Valid update group IDs are * <code>undefined</code>, '$auto', '$direct' or an application group ID. * @throws {Error} If an unsupported synchronization mode is given, if the given service * root URL does not end with a forward slash, if an unsupported parameter is given, if * OData system query options or parameter aliases are specified as parameters, if an * invalid group ID or update group ID is given, if the given operation mode is not * supported, if an annotation file cannot be merged into the service metadata, if an * unsupported value for <code>odataVersion</code> is given. * * @alias sap.ui.model.odata.v4.ODataModel * @author SAP SE * @class Model implementation for OData V4. * * This model is not prepared to be inherited from. * * Every resource path (relative to the service root URL, no query options) according to * <a href= * "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html#resource-pathurl4" * >"4 Resource Path"</a> in specification "OData Version 4.0 Part 2: URL Conventions" is * a valid data binding path within this model if a leading slash is added; for example * "/" + "SalesOrderList('A%2FB%26C')" to access an entity instance with key "A/B&C". Note * that appropriate URI encoding is necessary, see the example of * {@link sap.ui.model.odata.v4.ODataUtils.formatLiteral}. "4.5.1 Addressing Actions" * needs an operation binding, see {@link sap.ui.model.odata.v4.ODataContextBinding}. * * Note that the OData V4 model has its own {@link sap.ui.model.odata.v4.Context} class. * Bindings which are relative to such a V4 context depend on their corresponding parent * binding and do not access data with their own service requests unless parameters are * provided. * * <b>Group IDs</b> control the model's use of batch requests. Valid group IDs are: * <ul> * <li> <b>$auto</b> and <b>$auto.*</b>: Bundles requests from the model in a batch * request which is sent automatically before rendering. You can use different * '$auto.*' group IDs to use different batch requests. The suffix may be any * non-empty string consisting of alphanumeric characters from the basic Latin * alphabet, including the underscore. The submit mode for these group IDs is always * {@link sap.ui.model.odata.v4.SubmitMode#Auto}. * <li> <b>$direct</b>: Sends requests directly without batch. The submit mode for this * group ID is always {@link sap.ui.model.odata.v4.SubmitMode#Direct}. * <li> An application group ID, which is a non-empty string consisting of alphanumeric * characters from the basic Latin alphabet, including the underscore. By default, an * application group has the submit mode {@link sap.ui.model.odata.v4.SubmitMode#API}. * It is possible to use a different submit mode; for details see * <code>mParameters.groupProperties</code>. * </ul> * * @extends sap.ui.model.Model * @public * @since 1.37.0 * @version 1.111.5 */ ODataModel = Model.extend("sap.ui.model.odata.v4.ODataModel", /** @lends sap.ui.model.odata.v4.ODataModel.prototype */{ constructor : constructor }); //********************************************************************************************* // ODataModel //********************************************************************************************* /** * Constructor for a new ODataModel. * * @param {object} mParameters * The parameters * @throws {Error} If an unsupported synchronization mode is given, if the given service root * URL does not end with a forward slash, if an unsupported parameter is given, if OData * system query options or parameter aliases are specified as parameters, if an invalid group * ID or update group ID is given, if the given operation mode is not supported, if an * annotation file cannot be merged into the service metadata, if an unsupported value for * <code>odataVersion</code> is given. */ function constructor(mParameters) { var sGroupId, oGroupProperties, sLanguageTag = Configuration.getLanguageTag(), sODataVersion, sParameter, mQueryParams, sServiceUrl, oUri, mUriParameters, that = this; // do not pass any parameters to Model Model.call(this); mParameters = mParameters || {}; if ("synchronizationMode" in mParameters && mParameters.synchronizationMode !== "None") { throw new Error("Synchronization mode must be 'None'"); } sODataVersion = mParameters.odataVersion || "4.0"; this.sODataVersion = sODataVersion; if (sODataVersion !== "4.0" && sODataVersion !== "2.0") { throw new Error("Unsupported value for parameter odataVersion: " + sODataVersion); } for (sParameter in mParameters) { if (!(sParameter in mSupportedParameters)) { throw new Error("Unsupported parameter: " + sParameter); } } sServiceUrl = mParameters.serviceUrl; if (!sServiceUrl) { throw new Error("Missing service root URL"); } oUri = new URI(sServiceUrl); if (oUri.path()[oUri.path().length - 1] !== "/") { throw new Error("Service root URL must end with '/'"); } if (mParameters.operationMode && mParameters.operationMode !== OperationMode.Server) { throw new Error("Unsupported operation mode: " + mParameters.operationMode); } this.sOperationMode = mParameters.operationMode; // Note: strict checking for model's URI parameters, but "sap-*" is allowed mUriParameters = this.buildQueryOptions(oUri.query(true), false, true); // BEWARE: these are shared across all bindings! this.mUriParameters = mUriParameters; if (Configuration.getStatisticsEnabled()) { // Note: this way, "sap-statistics" is not sent within $batch mUriParameters = Object.assign({"sap-statistics" : true}, mUriParameters); } this.sServiceUrl = oUri.query("").toString(); this.sGroupId = mParameters.groupId; if (this.sGroupId === undefined) { this.sGroupId = "$auto"; } if (this.sGroupId !== "$auto" && this.sGroupId !== "$direct") { throw new Error("Group ID must be '$auto' or '$direct'"); } _Helper.checkGroupId(mParameters.updateGroupId, false, "Invalid update group ID: "); this.sUpdateGroupId = mParameters.updateGroupId || this.getGroupId(); this.mGroupProperties = {}; for (sGroupId in mParameters.groupProperties) { _Helper.checkGroupId(sGroupId, true); oGroupProperties = mParameters.groupProperties[sGroupId]; if (typeof oGroupProperties !== "object" || Object.keys(oGroupProperties).length !== 1 || !(oGroupProperties.submit in SubmitMode)) { throw new Error("Group '" + sGroupId + "' has invalid properties: '" + oGroupProperties + "'"); } } this.mGroupProperties = _Helper.clone(mParameters.groupProperties) || {}; this.mGroupProperties.$auto = {submit : SubmitMode.Auto}; this.mGroupProperties.$direct = {submit : SubmitMode.Direct}; if (mParameters.autoExpandSelect !== undefined && typeof mParameters.autoExpandSelect !== "boolean") { throw new Error("Value for autoExpandSelect must be true or false"); } this.bAutoExpandSelect = mParameters.autoExpandSelect === true; if ("sharedRequests" in mParameters && mParameters.sharedRequests !== true) { throw new Error("Value for sharedRequests must be true"); } this.bSharedRequests = mParameters.sharedRequests === true; this.bIgnoreETag = false; if ("ignoreAnnotationsFromMetadata" in mParameters && mParameters.ignoreAnnotationsFromMetadata !== true) { throw new Error("Value for ignoreAnnotationsFromMetadata must be true"); } // BEWARE: do not share mHeaders between _MetadataRequestor and _Requestor! this.mHeaders = {"Accept-Language" : sLanguageTag}; this.mMetadataHeaders = {"Accept-Language" : sLanguageTag}; mQueryParams = Object.assign({}, mUriParameters, mParameters.metadataUrlParams); this.oMetaModel = new ODataMetaModel( _MetadataRequestor.create(this.mMetadataHeaders, sODataVersion, mParameters.ignoreAnnotationsFromMetadata, mQueryParams), this.sServiceUrl + "$metadata", mParameters.annotationURI, this, mParameters.supportReferences, mQueryParams["sap-language"]); this.oInterface = { fetchEntityContainer : this.oMetaModel.fetchEntityContainer.bind(this.oMetaModel), fetchMetadata : this.oMetaModel.fetchObject.bind(this.oMetaModel), fireMessageChange : this.fireMessageChange.bind(this), fireDataReceived : this.fireDataReceived.bind(this), fireDataRequested : this.fireDataRequested.bind(this), fireSessionTimeout : function () { that.fireEvent("sessionTimeout"); }, getGroupProperty : this.getGroupProperty.bind(this), getMessagesByPath : this.getMessagesByPath.bind(this), getOptimisticBatchEnabler : this.getOptimisticBatchEnabler.bind(this), getReporter : this.getReporter.bind(this), isIgnoreETag : function () { return that.bIgnoreETag; }, onCreateGroup : function (sGroupId) { if (that.isAutoGroup(sGroupId)) { that.addPrerenderingTask(that._submitBatch.bind(that, sGroupId, true)); } }, reportStateMessages : this.reportStateMessages.bind(this), reportTransitionMessages : this.reportTransitionMessages.bind(this) }; this.oRequestor = _Requestor.create(this.sServiceUrl, this.oInterface, this.mHeaders, mUriParameters, sODataVersion); this.changeHttpHeaders(mParameters.httpHeaders); this.bEarlyRequests = mParameters.earlyRequests; if (this.bEarlyRequests) { this.oMetaModel.fetchEntityContainer(true); this.initializeSecurityToken(); this.oRequestor.sendOptimisticBatch(); } this.aAllBindings = []; // The bindings holding keep-alive contexts without a $$getKeepAlive binding this.mKeepAliveBindingsByPath = {}; this.mSupportedBindingModes = { OneTime : true, OneWay : true }; if (mParameters.sharedRequests) { this.sDefaultBindingMode = BindingMode.OneWay; } else { this.sDefaultBindingMode = BindingMode.TwoWay; this.mSupportedBindingModes.TwoWay = true; } this.aPrerenderingTasks = null; // @see #addPrerenderingTask this.fnOptimisticBatchEnabler = null; // maps the path to the error for the next dataReceived event this.mPath2DataReceivedError = {}; // maps a path to the difference between fireDataRequested and fireDataReceived calls, to // ensure the events are respectively fired once for a GET request this.mPath2DataRequestedCount = {}; } /** * Submits the requests associated with this group ID in one batch request. * * @param {string} sGroupId * The group ID * @param {boolean} [bCatch] * Whether the returned promise always resolves and never rejects * @returns {sap.ui.base.SyncPromise} * A promise on the outcome of the HTTP request resolving with <code>undefined</code>; it is * rejected with an error if the batch request itself fails. Use <code>bCatch</code> to catch * that error and make the promise resolve with <code>undefined</code> instead. * * @private */ ODataModel.prototype._submitBatch = function (sGroupId, bCatch) { var that = this; return this.oRequestor.submitBatch(sGroupId).catch(function (oError) { that.reportError("$batch failed", sClassName, oError); if (!bCatch) { throw oError; } }); }; /** * The 'dataReceived' event is fired when the back-end data has been received on the client. It * is only fired for GET requests and is to be used by applications to process an error. For * each 'dataRequested' event, a 'dataReceived' event is fired. * * If a back-end request is successful, the event has almost no parameters. For compatibility * with {@link sap.ui.model.Binding#event:dataReceived 'dataReceived'}, an event parameter * <code>data : {}</code> is provided: "In error cases it will be undefined", but otherwise it * is not. For additional property requests, the absolute path to the entity is also available. * * The 'dataReceived' event can be triggered by a binding or by additional property requests for * an entity that already has been requested. Events triggered by a binding may be bubbled up to * the model, while events triggered by additional property requests are fired directly by the * model. * * If a back-end request fails, the 'dataReceived' event provides an <code>Error</code> in the * 'error' event parameter. If multiple requests are processed within a single $batch * (or even a single change set), the order of 'dataReceived' events is not guaranteed. For * requests which are not processed because a previous request failed, <code>error.cause</code> * points to the root cause error - you should either ignore those events, or unwrap the error * to access the root cause immediately. For additional property requests, the absolute path to * the entity is also available. * * @param {sap.ui.base.Event} oEvent * @param {object} oEvent.getParameters() * @param {object} [oEvent.getParameters().data] * An empty data object if a back-end request succeeds * @param {Error} [oEvent.getParameters().error] * The error object if a back-end request failed. * @param {string} [oEvent.getParameters().path] * The absolute path to the entity which caused the event. The path is only provided for * additional property requests; for other requests it is <code>undefined</code>. * * @event sap.ui.model.odata.v4.ODataModel#dataReceived * @public * @see sap.ui.model.odata.v4.ODataContextBinding#event:dataReceived * @see sap.ui.model.odata.v4.ODataListBinding#event:dataReceived * @see sap.ui.model.odata.v4.ODataModel#event:dataRequested * @since 1.106.0 */ /** * The 'dataRequested' event is fired directly after data has been requested from a back end. * It is only fired for GET requests. For each 'dataRequested' event, a 'dataReceived' event is * fired. * * For additional property requests, the absolute path to the entity is available as an event * parameter. * * The 'dataRequested' event can be triggered by a binding or by additional property requests * for an entity that already has been requested. * Events triggered by a binding may be bubbled up to the model, while events triggered by * additional property requests are fired directly by the model. Every GET request caused by * additional properties is causing one 'dataRequested' event. * * There are two kinds of requests leading to such an event: * <ul> * <li> When a binding requests initial data, or a list binding requests data for additional * rows, the event is fired at the binding and may be bubbled up to the model. This includes * refreshes except those triggered by * {@link sap.ui.model.odata.v4.Context#requestSideEffects}. * <li> For additional property requests for an entity that already has been requested, the * event is only fired at the model. * </ul> * * @param {sap.ui.base.Event} oEvent * @param {object} oEvent.getParameters() * @param {string} [oEvent.getParameters().path] * The absolute path to the entity which caused the event. The path is only provided for * additional property requests; for other requests it is <code>undefined</code>. * * @event sap.ui.model.odata.v4.ODataModel#dataRequested * @public * @see sap.ui.model.odata.v4.ODataContextBinding#event:dataRequested * @see sap.ui.model.odata.v4.ODataListBinding#event:dataRequested * @see sap.ui.model.odata.v4.ODataModel#event:dataReceived * @since 1.106.0 */ /** * The 'parseError' event is not supported by this model. * * @event sap.ui.model.odata.v4.ODataModel#parseError * @public * @since 1.37.0 */ /** * The <code>propertyChange</code> event is fired whenever one of this model's property bindings * successfully {@link sap.ui.model.odata.v4.ODataPropertyBinding#setValue changes its value} * due to {@link sap.ui.model.BindingMode.TwoWay two-way} data binding. This does not apply to * {@link sap.ui.model.odata.v4.Context#setProperty} which represents controller code changes, * not user input. * * @param {sap.ui.base.Event} oEvent * @param {object} oEvent.getParameters() * @param {sap.ui.model.Context} [oEvent.getParameters().context] * The property binding's {@link sap.ui.model.Binding#getContext context}, if available * @param {string} oEvent.getParameters().path * The property binding's {@link sap.ui.model.Binding#getPath path} * @param {Promise} [oEvent.getParameters().promise] * A promise on the outcome of the PATCH request, much like * {@link sap.ui.model.odata.v4.Context#setProperty} provides it for * <code>bRetry === true</code>; missing in case there is no PATCH * @param {sap.ui.model.ChangeReason} oEvent.getParameters().reason * The reason for the property change: always <code>sap.ui.model.ChangeReason.Binding</code> * @param {string} oEvent.getParameters().resolvedPath * The property binding's {@link sap.ui.model.Binding#getResolvedPath resolved path} * @param {any} oEvent.getParameters().value * The property binding's new * {@link sap.ui.model.odata.v4.ODataPropertyBinding#getValue value} * * @event sap.ui.model.odata.v4.ODataModel#propertyChange * @public * @see sap.ui.model.Model#propertyChange * @since 1.110.0 */ /** * The 'requestCompleted' event is not supported by this model. * * @event sap.ui.model.odata.v4.ODataModel#requestCompleted * @public * @since 1.37.0 */ /** * The 'requestFailed' event is not supported by this model. * * @event sap.ui.model.odata.v4.ODataModel#requestFailed * @public * @since 1.37.0 */ /** * The 'requestSent' event is not supported by this model. * * @event sap.ui.model.odata.v4.ODataModel#requestSent * @public * @since 1.37.0 */ /** * The 'sessionTimeout' event is fired when the server has created a session for the model and * this session ran into a timeout due to inactivity. * * @event sap.ui.model.odata.v4.ODataModel#sessionTimeout * @public * @since 1.66.0 */ /** * Adds a task that is guaranteed to run once, just before the next rendering without triggering * a rendering request. A watchdog ensures that the task is executed soon, even if no rendering * occurs. * * @param {function} fnPrerenderingTask * A function that is called before the rendering * @param {boolean} [bFirst] * Whether the task should become the first one, not the last one * @private */ ODataModel.prototype.addPrerenderingTask = function (fnPrerenderingTask, bFirst) { var fnRunTasks, iTimeoutId, that = this; function runTasks(aTasks) { clearTimeout(iTimeoutId); while (aTasks.length) { aTasks.shift()(); } if (that.aPrerenderingTasks === aTasks) { that.aPrerenderingTasks = null; } } if (!this.aPrerenderingTasks) { this.aPrerenderingTasks = []; fnRunTasks = runTasks.bind(null, this.aPrerenderingTasks); sap.ui.getCore().addPrerenderingTask(fnRunTasks); // Add a watchdog to run the tasks in case there is no rendering. Ensure that the task // runs after all setTimeout(0) tasks scheduled from within the current task, even those // that were scheduled afterwards. A simple setTimeout(n) with n > 0 is not sufficient // because this doesn't help if the current task runs very long. iTimeoutId = setTimeout(function () { iTimeoutId = setTimeout(fnRunTasks, 0); }, 0); } if (bFirst) { this.aPrerenderingTasks.unshift(fnPrerenderingTask); } else { this.aPrerenderingTasks.push(fnPrerenderingTask); } }; /** * Attach event handler <code>fnFunction</code> to the 'dataReceived' event of this binding. * * @param {function} fnFunction The function to call when the event occurs * @param {object} [oListener] Object on which to call the given function * @returns {this} <code>this</code> to allow method chaining * * @public * @since 1.106.0 */ ODataModel.prototype.attachDataReceived = function (fnFunction, oListener) { return this.attachEvent("dataReceived", fnFunction, oListener); }; /** * Attach event handler <code>fnFunction</code> to the 'dataRequested' event of this binding. * * @param {function} fnFunction The function to call when the event occurs * @param {object} [oListener] Object on which to call the given function * @returns {this} <code>this</code> to allow method chaining * * @public * @since 1.106.0 */ ODataModel.prototype.attachDataRequested = function (fnFunction, oListener) { return this.attachEvent("dataRequested", fnFunction, oListener); }; /** * See {@link sap.ui.base.EventProvider#attachEvent} * * @param {string} sEventId The identifier of the event to listen for * @param {object} [_oData] * @param {function} [_fnFunction] * @param {object} [_oListener] * @returns {this} <code>this</code> to allow method chaining * * @public * @see sap.ui.base.EventProvider#attachEvent * @since 1.37.0 */ // @override sap.ui.base.EventProvider#attachEvent ODataModel.prototype.attachEvent = function (sEventId, _oData, _fnFunction, _oListener) { if (!(sEventId in mSupportedEvents)) { throw new Error("Unsupported event '" + sEventId + "': v4.ODataModel#attachEvent"); } return Model.prototype.attachEvent.apply(this, arguments); }; /** * Attach event handler <code>fnFunction</code> to the 'sessionTimeout' event of this model. * * @param {function} fnFunction The function to call when the event occurs * @param {object} [oListener] Object on which to call the given function * @returns {this} <code>this</code> to allow method chaining * * @public * @since 1.66.0 */ ODataModel.prototype.attachSessionTimeout = function (fnFunction, oListener) { return this.attachEvent("sessionTimeout", fnFunction, oListener); }; /** * Creates a new context binding for the given path, context and parameters. * * @param {string} sPath * The binding path in the model; must not end with a slash * @param {sap.ui.model.odata.v4.Context} [oContext] * The context which is required as base for a relative path * @param {object} [mParameters] * Map of binding parameters which can be OData query options as specified in <a href= * "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html" * >"OData Version 4.0 Part 2: URL Conventions"</a> or the binding-specific parameters as * specified below. * Note: The binding creates its own data service request if it is absolute or if it has any * parameters or if it is relative and has a context created via * {@link #createBindingContext}. * The following OData query options are allowed: * <ul> * <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-" * (unless starting with "sap-valid-") * <li> The $count, $expand, $filter, $levels, $orderby, $search and $select * "5.1 System Query Options"; OData V4 only allows $count, $filter, $levels, $orderby and * $search inside resource paths that identify a collection. In our case here, this means * you can only use them inside $expand. * </ul> * All other query options lead to an error. * Query options specified for the binding overwrite model query options. * @param {string|object} [mParameters.$expand] * The value for the "5.1.2 System Query Option $expand" or an object which determines that * value. The object is a map from expand path to expand options, where the options are again * maps of system query options, typically with string values. $count can also be given as a * <code>boolean</code> value, $expand can recursively be given as a map, $levels can also be * given as a <code>number</code> value, and $select can also be given as an array (but * without navigation paths). An empty map can also be given as <code>null</code> or * <code>true</code>. See also {@link topic:1ab4f62de6ab467096a2a98b363a1373 Parameters}. * @param {string|string[]} [mParameters.$select] * A comma separated list or an array of items which determine the value for the * "5.1.3 System Query Option $select". Since 1.75.0, when using the "autoExpandSelect" model * parameter (see {@link sap.ui.model.odata.v4.ODataModel#constructor}), paths with navigation * properties can be included and will contribute to the "5.1.2 System Query Option $expand". * @param {boolean} [mParameters.$$canonicalPath] * Whether a binding relative to an {@link sap.ui.model.odata.v4.Context} uses the canonical * path computed from its context's path for data service requests; only the value * <code>true</code> is allowed. * @param {string} [mParameters.$$groupId] * The group ID to be used for <b>read</b> requests triggered by this binding; if not * specified, either the parent binding's group ID (if the binding is relative) or the * model's group ID is used, see {@link sap.ui.model.odata.v4.ODataModel#constructor}. * Valid values are <code>undefined</code>, '$auto', '$auto.*', '$direct' or application group * IDs as specified in {@link sap.ui.model.odata.v4.ODataModel}. * @param {boolean} [mParameters.$$inheritExpandSelect] * For operation bindings only: Whether $expand and $select from the parent binding are used * in the request sent on {@link #execute}. If set to <code>true</code>, the binding must not * set the $expand itself, the operation must be bound, and the return value and the binding * parameter must belong to the same entity set. * @param {boolean} [mParameters.$$ownRequest] * Whether the binding always uses an own service request to read its data; only the value * <code>true</code> is allowed. * @param {boolean} [mParameters.$$patchWithoutSideEffects] * Whether implicit loading of side effects via PATCH requests is switched off; only the value * <code>true</code> is allowed. This sets the preference "return=minimal" and requires the * service to return an ETag header for "204 No Content" responses. If not specified, the * value of the parent binding is used. * @param {string} [mParameters.$$updateGroupId] * The group ID to be used for <b>update</b> requests triggered by this binding; * if not specified, either the parent binding's update group ID (if the binding is relative) * or the model's update group ID is used, see * {@link sap.ui.model.odata.v4.ODataModel#constructor}. * For valid values, see parameter "$$groupId". * @returns {sap.ui.model.odata.v4.ODataContextBinding} * The context binding * @throws {Error} * If disallowed binding parameters are provided, for example if the binding parameter * $$inheritExpandSelect is set to <code>true</code> and the binding is no operation binding * or the binding has the parameter $expand. * * @public * @see sap.ui.model.Model#bindContext * @since 1.37.0 */ // @override sap.ui.model.Model#bindContext ODataModel.prototype.bindContext = function (sPath, oContext, mParameters) { return new ODataContextBinding(this, sPath, oContext, mParameters); }; /** * Callback function for all V4 bindings to add themselves to their model. * * @param {sap.ui.model.odata.v4.ODataContextBinding|sap.ui.model.odata.v4.ODataListBinding|sap.ui.model.odata.v4.ODataPropertyBinding} oBinding * A context, list, or property binding * * @private */ ODataModel.prototype.bindingCreated = function (oBinding) { this.aAllBindings.push(oBinding); }; /** * Callback function for all V4 bindings to remove themselves from their model. * * @param {sap.ui.model.odata.v4.ODataContextBinding|sap.ui.model.odata.v4.ODataListBinding|sap.ui.model.odata.v4.ODataPropertyBinding} oBinding * A context, list, or property binding * @throws {Error} * If a binding is removed twice or without adding. * * @private */ ODataModel.prototype.bindingDestroyed = function (oBinding) { var iIndex = this.aAllBindings.indexOf(oBinding); if (iIndex < 0) { throw new Error("Unknown " + oBinding); } this.aAllBindings.splice(iIndex, 1); }; /** * Creates a new list binding for the given path and optional context which must * resolve to an absolute OData path for an entity set. * * @param {string} sPath * The binding path in the model; must not end with a slash * @param {sap.ui.model.Context} [oContext] * The context which is required as base for a relative path * @param {sap.ui.model.Sorter|sap.ui.model.Sorter[]} [vSorters] * The dynamic sorters to be used initially. Call * {@link sap.ui.model.odata.v4.ODataListBinding#sort} to replace them. Static sorters, as * defined in the '$orderby' binding parameter, are always executed after the dynamic sorters. * Supported since 1.39.0. * @param {sap.ui.model.Filter|sap.ui.model.Filter[]} [vFilters] * The dynamic application filters to be used initially. Call * {@link sap.ui.model.odata.v4.ODataListBinding#filter} to replace them. Static filters, as * defined in the '$filter' binding parameter, are always combined with the dynamic filters * using a logical <code>AND</code>. * Supported since 1.39.0. * @param {object} [mParameters] * Map of binding parameters which can be OData query options as specified in <a href= * "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html" * >"OData Version 4.0 Part 2: URL Conventions"</a> or binding-specific parameters as * specified below. * Note: The binding creates its own data service request if it is absolute or if it has any * parameters or if it is relative and has a context created via {@link #createBindingContext} * or if it has sorters or filters. * The following OData query options are allowed: * <ul> * <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-" * (unless starting with "sap-valid-") * <li> The $apply, $count, $expand, $filter, $levels, $orderby, $search, and $select * "5.1 System Query Options"; OData V4 only allows $levels inside $expand. * </ul> * All other query options lead to an error. * Query options specified for the binding overwrite model query options. * @param {string} [mParameters.$apply] * The value for the "3 System Query Option $apply" (see also * <a href="https://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/">OData * Extension for Data Aggregation Version 4.0</a>) as an alternative to * <code>$$aggregation</code> * @param {string|boolean} [mParameters.$count] * The value for the "5.1.6 System Query Option $count", useful for creation at the end and * {@link sap.ui.model.odata.v4.ODataListBinding#getCount} * @param {string|object} [mParameters.$expand] * The value for the "5.1.2 System Query Option $expand" or an object which determines that * value. The object is a map from expand path to expand options, where the options are again * maps of system query options, typically with string values. $count can also be given as a * <code>boolean</code> value, $expand can recursively be given as a map, $levels can also be * given as a <code>number</code> value, and $select can also be given as an array (but * without navigation paths). An empty map can also be given as <code>null</code> or * <code>true</code>. See also {@link topic:1ab4f62de6ab467096a2a98b363a1373 Parameters}. * @param {string} [mParameters.$filter] * The value for the "5.1.1 System Query Option $filter" used in addition to * <code>vFilters</code> * @param {string|number} [mParameters.$orderby] * The value for the "5.1.4 System Query Option $orderby" used in addition to * <code>vSorters</code> * @param {string} [mParameters.$search] * The value for the "5.1.7 System Query Option $search"; see also * <code>oAggregation.search</code> at * {@link sap.ui.model.odata.v4.ODataListBinding#setAggregation} and the note there! * @param {string|string[]} [mParameters.$select] * A comma separated list or an array of items which determine the value for the * "5.1.3 System Query Option $select". Since 1.75.0, when using the "autoExpandSelect" model * parameter (see {@link sap.ui.model.odata.v4.ODataModel#constructor}), paths with navigation * properties can be included and will contribute to the "5.1.2 System Query Option $expand". * @param {object} [mParameters.$$aggregation] * An object holding the information needed for data aggregation, see * {@link sap.ui.model.odata.v4.ODataListBinding#setAggregation} for details. * @param {boolean} [mParameters.$$canonicalPath] * Whether a binding relative to an {@link sap.ui.model.odata.v4.Context} uses the canonical * path computed from its context's path for data service requests; only the value * <code>true</code> is allowed. * @param {boolean} [mParameters.$$getKeepAliveContext] * Whether this binding is considered for a match when {@link #getKeepAliveContext} is called; * only the value <code>true</code> is allowed. Must not be combined with <code>$apply</code>, * <code>$$aggregation</code>, <code>$$canonicalPath</code>, or <code>$$sharedRequest</code>. * If the binding is relative, <code>$$ownRequest</code> must be set as well. * Supported since 1.99.0 * @param {string} [mParameters.$$groupId] * The group ID to be used for <b>read</b> requests triggered by this binding; if not * specified, either the parent binding's group ID (if the binding is relative) or the * model's group ID is used, see {@link sap.ui.model.odata.v4.ODataModel#constructor}. * Valid values are <code>undefined</code>, '$auto', '$auto.*', '$direct' or application group * IDs as specified in {@link sap.ui.model.odata.v4.ODataModel}. * @param {sap.ui.model.odata.OperationMode} [mParameters.$$operationMode] * The operation mode for filtering and sorting with the model's operation mode as default. * Since 1.39.0, the operation mode {@link sap.ui.model.odata.OperationMode.Server} is * supported. All other operation modes including <code>undefined</code> lead to an error if * 'vFilters' or 'vSorters' are given or if * {@link sap.ui.model.odata.v4.ODataListBinding#filter} or * {@link sap.ui.model.odata.v4.ODataListBinding#sort} is called. * @param {boolean} [mParameters.$$patchWithoutSideEffects] * Whether implicit loading of side effects via PATCH requests is switched off; only the value * <code>true</code> is allowed. This sets the preference "return=minimal" and requires the * service to return an ETag header for "204 No Content" responses. If not specified, the * value of the parent binding is used. * @param {boolean} [mParameters.$$ownRequest] * Whether the binding always uses an own service request to read its data; only the value * <code>true</code> is allowed. * @param {boolean} [mParameters.$$sharedRequest] * Whether multiple bindings for the same resource path share the data, so that it is * requested only once; only the value <code>true</code> is allowed. This parameter can be * inherited from the model's parameter "sharedRequests", see * {@link sap.ui.model.odata.v4.ODataModel#constructor}. Supported since 1.80.0 * <b>Note:</b> These bindings are read-only, so they may be especially useful for value * lists; state messages (since 1.108.0) and the following APIs are <b>not</b> allowed * <ul> * <li> for the list binding itself: * <ul> * <li> {@link sap.ui.model.odata.v4.ODataListBinding#create} * <li> {@link sap.ui.model.odata.v4.ODataListBinding#getKeepAliveContext} or * {@link #getKeepAliveContext} as far as it affects such a list binding * <li> {@link sap.ui.model.odata.v4.ODataListBinding#resetChanges} * </ul> * <li> for the {@link sap.ui.model.odata.v4.ODataListBinding#getHeaderContext header * context} of a list binding: * <ul> * <li> {@link sap.ui.model.odata.v4.Context#requestSideEffects} * </ul> * <li> for the context of a list binding representing a single entity: * <ul> * <li> {@link sap.ui.model.odata.v4.Context#delete} * <li> {@link sap.ui.model.odata.v4.Context#refresh} * <li> {@link sap.ui.model.odata.v4.Context#replaceWith} * <li> {@link sap.ui.model.odata.v4.Context#requestSideEffects} * <li> {@link sap.ui.model.odata.v4.Context#setKeepAlive} * <li> {@link sap.ui.model.odata.v4.Context#setProperty} * <li> executing a bound operation using <code>bReplaceWithRVC</code>, see * {@link sap.ui.model.odata.v4.ODataContextBinding#execute} * </ul> * <li> for a dependent property binding of the list binding: * <ul> * <li> {@link sap.ui.model.odata.v4.ODataPropertyBinding#setValue} * </ul> * </ul> * @param {string} [mParameters.$$updateGroupId] * The group ID to be used for <b>update</b> requests triggered by this binding; * if not specified, either the parent binding's update group ID (if the binding is relative) * or the model's update group ID is used, * see {@link sap.ui.model.odata.v4.ODataModel#constructor}. * For valid values, see parameter "$$groupId". * @returns {sap.ui.model.odata.v4.ODataListBinding} * The list binding * @throws {Error} * If disallowed binding parameters are provided or an unsupported operation mode is used * * @public * @see sap.ui.model.Model#bindList * @since 1.37.0 */ ODataModel.prototype.bindList = function (sPath, oContext, vSorters, vFilters, mParameters) { return new ODataListBinding(this, sPath, oContext, vSorters, vFilters, mParameters); }; /** * Creates a new property binding for the given path. This binding is inactive and will not * know the property value initially. You have to call {@link sap.ui.model.Binding#initialize} * to get it updated asynchronously and register a change listener at the binding to be informed * when the value is available. * * It is possible to create a property binding pointing to metadata. A '##' in the * binding's path is recognized as a separator and splits it into two parts. * The part before the separator is resolved with the binding's context and the result is * transformed into a metadata context (see * {@link sap.ui.model.odata.v4.ODataMetaModel#getMetaContext}). The part following the * separator is then interpreted relative to this metadata context, even if it starts with * a '/'; see {@link sap.ui.model.odata.v4.ODataMetaModel#requestObject} for more details. * * If the target type specified in the corresponding control property's binding info is "any" * and the binding is relative or points to metadata, the binding may have an object value; * in this case and unless the binding refers to an action advertisement the binding's mode must * be {@link sap.ui.model.BindingMode.OneTime}. * * @param {string} sPath * The binding path in the model; must not end with a slash * @param {sap.ui.model.Context} [oContext] * The context which is required as base for a relative path * @param {object} [mParameters] * Map of binding parameters which can be OData query options as specified in <a href= * "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html" * >"OData Version 4.0 Part 2: URL Conventions"</a> or the binding-specific parameters as * specified below. The following OData query options are allowed: * <ul> * <li> All "5.2 Custom Query Options" except for those with a name starting with "sap-" * (unless starting with "sap-valid-") * <li> The $apply, $filter, and $search "5.1 System Query Options" if the path ends with a * "$count" segment. * </ul> * All other query options lead to an error. * Query options specified for the binding overwrite model query options. * Note: The binding only creates its own data service request if it is absolute or if it is * relative to a context created via {@link #createBindingContext}. The binding parameters are * ignored in case the binding creates no own data service request or in case the binding * points to metadata. * @param {string} [mParameters.$apply] * The value for the "3 System Query Option $apply" (see also * <a href="https://docs.oasis-open.org/odata/odata-data-aggregation-ext/v4.0/">OData * Extension for Data Aggregation Version 4.0</a>), if the path ends with a "$count" segment * @param {string} [mParameters.$filter] * The value for the "5.