@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
440 lines (396 loc) • 16.6 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/core/Control',
'sap/ui/base/EventProvider',
'sap/ui/core/mvc/View',
'sap/ui/core/routing/async/Target',
'sap/ui/core/routing/sync/Target',
"sap/base/util/UriParameters",
"sap/base/Log"
],
function(
Control,
EventProvider,
View,
asyncTarget,
syncTarget,
UriParameters,
Log
) {
"use strict";
/**
* This class resolves the property binding of the 'title' option.
*
* @class
* @param {object} mSettings configuration object for the TitleProvider
* @param {object} mSettings.target Target for which the TitleProvider is created
* @private
* @extends sap.ui.core.Control
*/
var TitleProvider = Control.extend("sap.ui.core.routing.Target.TitleProvider", /** @lends sap.ui.core.routing.TitleProvider.prototype */ {
metadata: {
library: "sap.ui.core",
properties: {
/**
* The title text provided by this class
*/
title: {
type: "string",
group: "Data",
defaultValue: null
}
}
},
constructor: function(mSettings) {
this._oTarget = mSettings.target;
delete mSettings.target;
Control.prototype.constructor.call(this, mSettings);
},
setTitle: function(sTitle) {
// Setting title property should not trigger two way change in model
this.setProperty("title", sTitle, true);
if (this._oTarget._bIsDisplayed && sTitle) {
this._oTarget.fireTitleChanged({
name: this._oTarget._oOptions._name,
title: sTitle
});
}
}
});
/**
* <b>Don't call this constructor directly</b>, use {@link sap.ui.core.routing.Targets} instead, it will create instances of a Target.<br/>
* If you are using the mobile library, please use the {@link sap.m.routing.Targets} constructor, please read the documentation there.<br/>
*
* @class
* Provides a convenient way for placing views into the correct containers of your application.
*
* The main benefit of Targets is lazy loading: you do not have to create the views until you really need them.
* @param {object} oOptions all of the parameters defined in {@link sap.m.routing.Targets#constructor} are accepted here, except for children you need to specify the parent.
* @param {sap.ui.core.routing.TargetCache} oCache All views required by this target will get created by the views instance using {@link sap.ui.core.routing.Views#getView}
* @param {sap.ui.core.routing.Target} [oParent] the parent of this target. Will also get displayed, if you display this target. In the config you have the fill the children property {@link sap.m.routing.Targets#constructor}
* @public
* @since 1.28.1
* @extends sap.ui.base.EventProvider
* @alias sap.ui.core.routing.Target
*/
var Target = EventProvider.extend("sap.ui.core.routing.Target", /** @lends sap.ui.core.routing.Target.prototype */ {
constructor : function(oOptions, oCache) {
var sErrorMessage;
// temporarily: for checking the url param
function checkUrl() {
if (UriParameters.fromQuery(window.location.search).get("sap-ui-xx-asyncRouting") === "true") {
Log.warning("Activation of async view loading in routing via url parameter is only temporarily supported and may be removed soon", "Target");
return true;
}
return false;
}
// Set the default value to sync
if (oOptions._async === undefined) {
// temporarily: set the default value depending on the url parameter "sap-ui-xx-asyncRouting"
oOptions._async = checkUrl();
}
if (oOptions.type === "Component" && !oOptions._async) {
sErrorMessage = "sap.ui.core.routing.Target doesn't support loading component in synchronous mode, please switch routing to async";
Log.error(sErrorMessage);
throw new Error(sErrorMessage);
}
this._updateOptions(oOptions);
this._oCache = oCache;
EventProvider.apply(this, arguments);
if (this._oOptions.title) {
this._oTitleProvider = new TitleProvider({
target: this
});
}
// branch by abstraction
var TargetStub = this._oOptions._async ? asyncTarget : syncTarget;
for (var fn in TargetStub) {
this[fn] = TargetStub[fn];
}
this._bIsDisplayed = false;
this._bIsLoaded = false;
},
/**
* Destroys the target, will be called by {@link sap.m.routing.Targets} don't call this directly.
*
* @protected
* @returns { sap.ui.core.routing.Target } this for chaining.
*/
destroy : function () {
this._oParent = null;
this._oOptions = null;
this._oCache = null;
if (this._oTitleProvider) {
this._oTitleProvider.destroy();
}
this._oTitleProvider = null;
EventProvider.prototype.destroy.apply(this, arguments);
this.bIsDestroyed = true;
return this;
},
/**
* Creates a view and puts it in an aggregation of a control that has been defined in the {@link sap.ui.core.routing.Target#constructor}.
*
* @name sap.ui.core.routing.Target#display
* @function
* @param {*} [vData] an object that will be passed to the display event in the data property. If the target has parents, the data will also be passed to them.
* @return {Promise} resolves with {name: *, view: *, control: *} if the target can be successfully displayed otherwise it resolves with {name: *, error: *}
* @public
*/
/**
* Suspends the object which is loaded by the target.
*
* Currently this function stops the router of the component when the object which is loaded by this target
* is an instance of UIComponent. This is done only when the target is already loaded. When the target is
* not loaded yet or still being loaded, the router of the component isn't stopped.
*
* @return {sap.ui.core.routing.Target} The 'this' to chain the call
* @name sap.ui.core.routing.Target#suspend
* @function
* @public
*/
/**
* Will be fired when a target is displayed
*
* Could be triggered by calling the display function or by the @link sap.ui.core.routing.Router when a target is referenced in a matching route.
*
* @name sap.ui.core.routing.Target#display
* @event
* @param {object} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {object} oEvent.getParameters.view The view that got displayed.
* @param {object} oEvent.getParameters.control The control that now contains the view in the controlAggregation
* @param {object} oEvent.getParameters.config The options object passed to the constructor {@link sap.ui.core.routing.Target#constructor}
* @param {object} oEvent.getParameters.data The data passed into the {@link sap.ui.core.routing.Target#display} function
* @param {object} oEvent.getParameters.routeRelevant=false Whether the target is relevant to the matched route or not
* @public
*/
/**
* Attaches event handler <code>fnFunction</code> to the {@link #event:display display} event of this
* <code>sap.ui.core.routing.Target</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.Target</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.Target</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
attachDisplay : function(oData, fnFunction, oListener) {
return this.attachEvent(this.M_EVENTS.DISPLAY, oData, fnFunction, oListener);
},
/**
* Detaches event handler <code>fnFunction</code> from the {@link #event:display display} event of this
* <code>sap.ui.core.routing.Target</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
*/
detachDisplay : function(fnFunction, oListener) {
return this.detachEvent(this.M_EVENTS.DISPLAY, 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
*/
fireDisplay : function(oParameters) {
var sTitle = this._oTitleProvider && this._oTitleProvider.getTitle();
if (sTitle) {
this.fireTitleChanged({
name: this._oOptions._name,
title: sTitle
});
}
this._bIsDisplayed = true;
oParameters = oParameters || {};
oParameters.config = this._oRawOptions;
return this.fireEvent(this.M_EVENTS.DISPLAY, oParameters);
},
/**
* Will be fired when the title of this <code>Target</code> has been changed.
*
* @name sap.ui.core.routing.Target#titleChanged
* @event
* @param {object} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.title The name of this target
* @param {string} oEvent.getParameters.title The current displayed title
* @private
*/
/**
* Attaches event handler <code>fnFunction</code> to the {@link #event:titleChanged titleChanged} event of this
* <code>sap.ui.core.routing.Target</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.Target</code> itself.
*
* When the first event handler is registered later than the last title change, it's still called with the last changed title because
* when title is set with static text, the event is fired synchronously with the instantiation of this Target and the event handler can't
* be registered before the event is fired.
*
* @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.Target</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @private
*/
attachTitleChanged : function(oData, fnFunction, oListener) {
var bHasListener = this.hasListeners("titleChanged"),
sTitle = this._oTitleProvider && this._oTitleProvider.getTitle();
this.attachEvent(this.M_EVENTS.TITLE_CHANGED, oData, fnFunction, oListener);
// in case the title is changed before the first event listener is attached, we need to notify, too
if (!bHasListener && sTitle && this._bIsDisplayed) {
this.fireTitleChanged({
name: this._oOptions._name,
title: sTitle
});
}
return this;
},
/**
* Detaches event handler <code>fnFunction</code> from the {@link #event:titleChanged titleChanged} event of this
* <code>sap.ui.core.routing.Target</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
* @private
*/
detachTitleChanged : function(fnFunction, oListener) {
return this.detachEvent(this.M_EVENTS.TITLE_CHANGED, fnFunction, oListener);
},
// private
fireTitleChanged : function(oParameters) {
return this.fireEvent(this.M_EVENTS.TITLE_CHANGED, oParameters);
},
_getEffectiveObjectName : function (sName) {
var sPath = this._oOptions.path;
if (sPath) {
sName = sPath + "." + sName;
}
return sName;
},
_updateOptions: function (oOrigOptions) {
var oOptions = Object.assign({}, oOrigOptions);
// convert the legacy syntax to the new one
// if "viewName" is set, it's converted to "type" and "name"
// meanwhile, the "viewPath" is also set to "path" and the
// "viewId" is also set to "id"
if (oOptions.viewName) {
// if the target's name is given under the "name" property,
// copy it to "_name" before overwritting it with the "viewName"
if (oOptions.name) {
oOptions._name = oOptions.name;
}
oOptions.type = "View";
oOptions.name = oOptions.viewName;
// sync Target still only works with the legacy options
if (oOptions._async) {
delete oOptions.viewName;
}
if (oOptions.viewPath) {
oOptions.path = oOptions.viewPath;
// sync Target still only works with the legacy options
if (oOptions._async) {
delete oOptions.viewPath;
}
}
if (oOptions.viewId) {
oOptions.id = oOptions.viewId;
// sync Target still only works with the legacy options
if (oOptions._async) {
delete oOptions.viewId;
}
}
}
this._oOptions = oOptions;
this._oRawOptions = oOrigOptions;
},
_bindTitleInTitleProvider : function(oView) {
if (this._oTitleProvider && oView instanceof View) {
this._oTitleProvider.applySettings({
title: this._oOptions.title
}, oView.getController());
}
},
_addTitleProviderAsDependent : function(oView) {
if (!this._oTitleProvider) {
return;
}
// Remove the title provider from the old parent manually before adding
// it to the new view because the internal removal from old parent
// currently causes rerendering of the old parent.
var oOldParent = this._oTitleProvider.getParent();
if (oOldParent) {
oOldParent.removeDependent(this._oTitleProvider);
}
if (oView instanceof View) {
oView.addDependent(this._oTitleProvider);
}
},
/**
* This function is called between the target view is loaded and the view is added to the container.
*
* This function can be used for applying modification on the view or the container to make the rerendering occur
* together with the later aggregation change.
*
* @protected
* @param {object} mArguments the object containing the arguments
* @param {sap.ui.core.Control} mArguments.container the container where the view will be added
* @param {sap.ui.core.Control} mArguments.view the view which will be added to the container
* @param {object} [mArguments.data] the data passed from {@link sap.ui.core.routing.Target#display} method
* @since 1.46.1
*/
_beforePlacingViewIntoContainer : function(mArguments) {},
/**
* Here the magic happens - recursion + placement + view creation needs to be refactored
*
* @name sap.ui.core.routing.Target#_place
* @param {object} [vData] an object that will be passed to the display event in the data property. If the
* target has parents, the data will also be passed to them.
* @param {Promise} oSequencePromise Promise chain for resolution in the correct order
* @return {Promise} resolves with {name: *, view: *, control: *} if the target can be successfully displayed otherwise it rejects with an error message
* @private
*/
/**
* Validates the target options, will also be called from the route but route will not log errors
*
* @name sap.ui.core.routing.Target#._isValid
* @param oParentInfo
* @returns {boolean|string} returns true if it's valid otherwise the error message
* @private
*/
M_EVENTS : {
DISPLAY : "display",
TITLE_CHANGED : "titleChanged"
}
});
return Target;
});