UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

440 lines (396 loc) 16.6 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/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; });