UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

341 lines (296 loc) 12.9 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/ui/base/EventProvider', 'sap/ui/core/routing/async/TargetCache', 'sap/ui/core/routing/sync/TargetCache', "sap/base/assert", "sap/base/Log" ], function ( EventProvider, asyncCache, syncCache, assert, Log ) { "use strict"; /** * Instantiates a cache repository that creates and caches views and components which are loaded by {@link sap.u.core.routing.Targets}. * * If it is destroyed, all the views and components which it created are destroyed. If the views or components are still being loaded, * they will be destroyed after they are loaded. * * This class is currently private and shouldn't be used out of the sap.ui.core.routing scope. * * @class * @extends sap.ui.base.EventProvider * @private * @param {object} [oOptions] * @param {sap.ui.core.UIComponent} [oOptions.component] the owner of all the views that will be created by this Instance. * @param {boolean} [oOptions.async=true] Whether the views and components which are created through this class are loaded asynchronously. * This option can be set only when TargetCache is used standalone without the involvement of a Router. * Otherwise the async option is inherited from the Router. * @alias sap.ui.core.routing.TargetCache */ var TargetCache = EventProvider.extend("sap.ui.core.routing.TargetCache", /** @lends sap.ui.core.routing.TargetCache.prototype */ { constructor : function (oOptions) { if (!oOptions) { oOptions = {}; } this._oCache = { view: {}, component: {} }; this._oComponent = oOptions.component; if (this._oComponent) { assert(this._oComponent.isA("sap.ui.core.UIComponent"), this + ' - the component passed to the constructor needs to be an instance of UIComponent'); } EventProvider.apply(this, arguments); this.async = oOptions.async; if (this.async === undefined) { // make the default value for async to true this.async = true; } var CacheStub = this.async ? asyncCache : syncCache; for (var fn in CacheStub) { this[fn] = CacheStub[fn]; } }, metadata : { publicMethods: ["get", "set"] }, /** * Returns a cached view or component, for a given name. If it does not exist yet, it will create the view or component with the provided options. * If you provide a "id" in the "oOptions", it will be prefixed with the id of the component. * * @param {object} oOptions see {@link sap.ui.core.mvc.View.create} or {@link sap.ui.core.Component.create} for the documentation. * @param {string} oOptions.name If you do not use setView please see {@link sap.ui.core.mvc.View.create} or {@link sap.ui.core.Component.create} for the documentation. * This is used as a key in the cache of the view or component instance. If you want to retrieve a view or a component that has been given an alternative name in {@link #set}, * you need to provide the same name here and you can skip all the other options. * @param {string} [oOptions.id] The id you pass into the options will be prefixed with the id of the component you pass into the constructor. * So you can retrieve the view later by calling the {@link sap.ui.core.UIComponent#byId} function of the UIComponent. * @param {string} sType whether the object is a "View" or "Component". Views and components are stored separately in the cache. This means that a view and a component instance * could be stored under the same name. * @return {Promise} A promise that is resolved when the view or component is loaded. The view or component instance will be passed to the resolve function. * @private */ get : function (oOptions, sType) { var oObject; try { if (sType === "Component" && !this.async) { Log.error("sap.ui.core.routing.Target doesn't support loading component in synchronous mode, please switch routing to async"); throw new Error("sap.ui.core.routing.Target doesn't support loading component in synchronous mode, please switch routing to async"); } if (!oOptions) { Log.error("the oOptions parameter of getObject is mandatory", this); throw new Error("the oOptions parameter of getObject is mandatory"); } oObject = this._get(oOptions, sType); } catch (e) { return Promise.reject(e); } if (oObject instanceof Promise) { return oObject; } else if (oObject.isA("sap.ui.core.mvc.View")) { return oObject.loaded(); } else { return Promise.resolve(oObject); } }, /** * Determines the object with the given <code>oOptions</code>, <code>sType</code> from the Target cache. * * @param {object} oOptions The options of the desired object * @param {string} sType The type of the desired object, e.g. 'View', 'Component', etc. * @return {sap.ui.core.Control|Promise} The object if it already exists in the cache, if not the promise is returned */ fetch: function(oOptions, sType) { return this._get(oOptions, sType, undefined, undefined, true); }, /** * Adds or overwrites a view or a component in the TargetCache. The given object is cached under its name and the 'undefined' key. * * If the third parameter is set to null or undefined, the previous cache view or component under the same name isn't managed by the TargetCache instance. * The lifecycle (for example the destroy) of the view or component instance should be maintained by additional code. * * * @param {string} sName Name of the view or component, may differ from the actual name of the oObject parameter provided, since you can retrieve this view or component per {@link #.getObject}. * @param {string} sType whether the object is a "View" or "Component". Views and components are stored separately in the cache. This means that a view and a component instance * could be stored under the same name. * @param {sap.ui.core.mvc.View|sap.ui.core.UIComponent|null|undefined} oObject the view or component instance * @return {this} this for chaining. * @private */ set : function (sName, sType, oObject) { var oInstanceCache; this._checkName(sName, sType); assert(sType === "View" || sType === "Component", "sType must be either 'View' or 'Component'"); oInstanceCache = this._oCache[sType.toLowerCase()][sName]; if (!oInstanceCache) { oInstanceCache = this._oCache[sType.toLowerCase()][sName] = {}; } oInstanceCache[undefined] = oObject; return this; }, /** * Destroys all the views and components created by this instance. * * @returns {this} this for chaining. */ destroy : function () { EventProvider.prototype.destroy.apply(this); if (this.bIsDestroyed) { return this; } function destroyObject(oObject) { if (oObject && oObject.destroy && !oObject._bIsBeingDestroyed) { oObject.destroy(); } } Object.keys(this._oCache).forEach(function (sType) { var oTypeCache = this._oCache[sType]; Object.keys(oTypeCache).forEach(function (sKey) { var oInstanceCache = oTypeCache[sKey]; Object.keys(oInstanceCache).forEach(function(sId) { var vObject = oInstanceCache[sId]; if (vObject instanceof Promise) { // if the promise isn't replaced by the real object yet // wait until the promise resolves to destroy the object vObject.then(destroyObject); } else { destroyObject(vObject); } }); }); }.bind(this)); this._oCache = undefined; this.bIsDestroyed = true; return this; }, /** * If a view or component is created the event will be fired. * It will not be fired, if a view or component was read from the cache. * * @name sap.ui.core.routing.TargetCache#created * @event * @param {sap.ui.base.Event} oEvent refer to {@link sap.ui.base.EventProvider} for details about getSource and getParameters * @param {sap.ui.base.EventProvider} oEvent.getSource * @param {object} oEvent.getParameters * @param {sap.ui.core.mvc.View|sap.ui.core.UIComponent} oEvent.getParameters.object the instance of the created view. * @param {string} oEvent.getParameters.type whether it's a "View" or "Component" * @param {object} oEvent.getParameters.options The options passed to {@link sap.ui.core.mvc.View.create} or {@link sap.ui.core.Component.create} * @public */ /** * Attaches event handler <code>fnFunction</code> to the {@link #event:created created} event of this * <code>sap.ui.core.routing.TargetCache</code>. * * When called, the context of the event handler (its <code>this</code>) will be bound to <code>oListener</code> * if specified, otherwise it will be bound to this <code>sap.ui.core.routing.TargetCache</code> itself. * * @param {object} * [oData] An application-specific payload object that will be passed to the event handler * along with the event object when firing the event * @param {function} * fnFunction The function to be called, when the event occurs * @param {object} * [oListener] Context object to call the event handler with. Defaults to this * <code>sap.ui.core.routing.TargetCache</code> itself * * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ attachCreated : function(oData, fnFunction, oListener) { return this.attachEvent("created", oData, fnFunction, oListener); }, /** * Detaches event handler <code>fnFunction</code> from the {@link #event:created created} event of this * <code>sap.ui.core.routing.TargetCache</code>. * * The passed function and listener object must match the ones used for event registration. * * @param {function} fnFunction The function to be called, when the event occurs * @param {object} [oListener] Context object on which the given function had to be called * @returns {this} Reference to <code>this</code> in order to allow method chaining * @public */ detachCreated : function(fnFunction, oListener) { return this.detachEvent("created", fnFunction, oListener); }, /** * Fires event {@link #event:created created} to attached listeners. * * @param {object} [oParameters] Parameters to pass along with the event * @returns {this} Reference to <code>this</code> in order to allow method chaining * @protected */ fireCreated : function(oParameters) { return this.fireEvent("created", oParameters); }, /* * Privates */ _get : function (oOptions, sType, bGlobalId, oInfo, bNoCreate) { var oObject; switch (sType) { case "View": oObject = this._getView(oOptions, bGlobalId, bNoCreate); break; case "Component": oObject = this._getComponent(oOptions, bGlobalId, oInfo, bNoCreate); break; default: throw Error("The given sType: " + sType + " isn't supported by TargetCache.getObject"); } return oObject; }, /** * Hook for retrieving views synchronous way since Targets and router are not doing this yet * @param {object} oOptions The options to determine the view * @param {boolean} bGlobalId True, if a global id should be generated * @returns {*} the view * @private */ _getView : function (oOptions, bGlobalId, bNoCreate) { if (!bGlobalId) { oOptions = this._createId(oOptions); } return this._getViewWithGlobalId(oOptions, false /* sync creation */, bNoCreate); }, _getComponent : function (oOptions, bGlobalId, oInfo, bNoCreate) { if (!bGlobalId) { oOptions = this._createId(oOptions); } return this._getComponentWithGlobalId(oOptions, oInfo, bNoCreate); }, _createId: function (oOptions) { if (this._oComponent && oOptions.id) { oOptions = Object.assign({}, oOptions, { id : this._oComponent.createId(oOptions.id) }); } return oOptions; }, /** * hook for the deprecated property viewId on the route, will not prefix the id with the component * * @name sap.ui.core.routing.TargetCache#_getViewWithGlobalId * @returns {*} * @private */ /** * @param {string} sName logs an error if it is empty or undefined * @param {string} sType whether it's a 'View' or 'Component' * @private */ _checkName : function (sName, sType) { if (!sName) { var sMessage = "A name for the " + sType.toLowerCase() + " has to be defined"; Log.error(sMessage, this); throw Error(sMessage); } } }); return TargetCache; });