@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
341 lines (296 loc) • 12.9 kB
JavaScript
/*!
* 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;
});