UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

819 lines (740 loc) 33.1 kB
/*! * OpenUI5 * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.ui.core.mvc.XMLView. sap.ui.define([ 'sap/ui/thirdparty/jquery', './View', "./XMLViewRenderer", "sap/base/util/deepExtend", 'sap/ui/base/ManagedObject', 'sap/ui/core/XMLTemplateProcessor', 'sap/ui/core/library', 'sap/ui/core/Control', 'sap/ui/core/RenderManager', 'sap/ui/core/cache/CacheManager', 'sap/ui/model/resource/ResourceModel', 'sap/ui/util/XMLHelper', 'sap/base/strings/hash', 'sap/base/Log', 'sap/base/util/LoaderExtensions', "sap/ui/performance/trace/Interaction" ], function( jQuery, View, XMLViewRenderer, deepExtend, ManagedObject, XMLTemplateProcessor, library, Control, RenderManager, Cache, ResourceModel, XMLHelper, hash, Log, LoaderExtensions, Interaction ) { "use strict"; // actual constants var RenderPrefixes = RenderManager.RenderPrefixes, ViewType = library.mvc.ViewType, sXMLViewCacheError = "XMLViewCacheError", notCacheRelevant = {}; /** * Constructor for a new <code>XMLView</code>. * * <strong>Note:</strong> Application code shouldn't call the constructor directly, but rather use the factory * {@link sap.ui.core.mvc.XMLView.create XMLView.create} or {@link sap.ui.core.mvc.View.create View.create} * with type {@link sap.ui.core.mvc.ViewType.XML XML}. The factory provides more features than the * constructor (e.g. caching and preprocessing) and simplifies asynchronous loading of a view. * Future features might only be available when using the factory. * * @param {string} [sId] ID for the new view, generated automatically if no ID is given * @param {object} [mSettings] Initial settings for the new view * * @class * A View defined using XML and, optionally, pure HTML markup. * * <strong>Note:</strong><br> * Be aware that modifications of the content aggregation of this control are not supported due to technical reasons. * This includes calls to all content modifying methods like <code>addContent></code> etc., but also the implicit * removal of controls contained by the content aggregation. For example the destruction of a Control via the <code> * destroy</code> method. All functions can be called but may not work properly or lead to unexpected side effects. * * <strong>Note:</strong><br> * On root level, you can only define content for the default aggregation, e.g. without adding the <code>&lt;content&gt;</code> tag. If * you want to specify content for another aggregation of a view like <code>dependents</code>, place it in a child * control's dependents aggregation or add it by using {@link sap.ui.core.mvc.XMLView#addDependent}. * * @extends sap.ui.core.mvc.View * @version 1.87.1 * * @public * @alias sap.ui.core.mvc.XMLView * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel */ var XMLView = View.extend("sap.ui.core.mvc.XMLView", /** @lends sap.ui.core.mvc.XMLView.prototype */ { metadata : { library : "sap.ui.core", specialSettings : { /** * If an XMLView instance is used to represent an HTML subtree of another XMLView, * then that other XMLView is provided with this setting to be able to delegate * View functionality (createId, getController) to that 'real' view. */ containingView : { type: 'sap.ui.core.mvc.XMLView', visibility: 'hidden' }, /** * If an XMLView instance is used to represent an HTML subtree of another XMLView, * that subtree is provided with this setting. */ xmlNode : { type: 'Element', visibility: 'hidden' }, /** * Configuration for the XMLView caching. */ cache : 'Object', /** * The processing mode of the XMLView. * The processing mode "sequential" is implicitly activated for the following type of async views: * a) root views in the manifest * b) XMLViews created with the (XML)View.create factory * c) XMLViews used via routing * Additionally, all declarative nested async subviews are also processed asynchronously. */ processingMode: { type: "string", visibility: "hidden" } }, designtime: "sap/ui/core/designtime/mvc/XMLView.designtime" }, renderer: XMLViewRenderer }); /** * Instantiates an XMLView of the given name and with the given ID. * * The <code>vView</code> can either be the name of the module that contains the view definition * or it can be a configuration object with properties <code>viewName</code>, <code>viewContent</code> * and a <code>controller</code> property (more properties are described in the parameters section below). * * If a <code>viewName</code> is given, it behaves the same as when <code>vView</code> is a string: * the named resource will be loaded and parsed as XML. Alternatively, an already loaded view definition * can be provided as <code>viewContent</code>, either as XML string or as an already parsed XML document. * Exactly one of <code>viewName</code> and <code>viewContent</code> must be given, if none or both are given, * an error will be reported. The <code>controller</code> property is optional and can hold a controller instance. * When given, it overrides the controller class defined in the view definition. * * When property <code>async</code> is set to true, the view definition and the controller class (and its * dependencies) will be loaded asynchronously. Any controls used in the view might be loaded sync or * async, depending on the processingMode. Even when * the view definition is provided as string or XML Document, controller or controls might be loaded * asynchronously. In any case a view instance will be returned synchronously by this factory API, but its * content (control tree) might appear only later. Also see {@link sap.ui.core.mvc.View#loaded}. * * <strong>Note</strong>: If an XML document is given, it might be modified during view construction. * * <strong>Note</strong>: On root level, you can only define content for the default aggregation, e.g. * without adding the <code>&lt;content&gt;</code> tag. If you want to specify content for another aggregation * of a view like <code>dependents</code>, place it in a child control's dependents aggregation or add it by * using {@link sap.ui.core.mvc.XMLView#addDependent}. * * <strong>Note</strong>: If you enable caching, you need to take care of the invalidation via keys. Automatic * invalidation takes only place if the UI5 version or the component descriptor (manifest.json) change. This is * still an experimental feature and may experience slight changes of the invalidation parameters or the cache * key format. * * Like with any other control, <code>sId</code> is optional and an ID will be created automatically. * * @param {string} [sId] ID of the newly created view * @param {string | object} vView Name of the view or a view configuration object as described above * @param {string} [vView.viewName] Name of the view resource in module name notation (without suffix) * @param {string|Document} [vView.viewContent] XML string or XML document that defines the view * @param {boolean} [vView.async] Defines how the view source is loaded and rendered later on * @param {object} [vView.cache] Cache configuration, only for <code>async</code> views; caching gets active * when this object is provided with vView.cache.keys array; keys are used to store data in the cache and for * invalidation of the cache * @param {Array<(string|Promise<string>)>} [vView.cache.keys] Array with strings or Promises resolving with strings * @param {object} [vView.preprocessors] Preprocessors configuration, see {@link sap.ui.core.mvc.View} * @param {sap.ui.core.mvc.Controller} [vView.controller] Controller instance to be used for this view * @public * @static * @deprecated since 1.56: Use {@link sap.ui.core.mvc.XMLView.create XMLView.create} instead * @returns {sap.ui.core.mvc.XMLView} the created XMLView instance * @ui5-global-only */ sap.ui.xmlview = function(sId, vView) { return sap.ui.view(sId, vView, ViewType.XML); }; /** * Instantiates an XMLView from the given configuration options. * * If a <code>viewName</code> is given, it must be a dot-separated name of an XML view resource (without * the mandatory suffix ".view.xml"). The resource will be loaded asynchronously via the module system * (preload caches might apply) and will be parsed as XML. Alternatively, an already loaded view <code>definition</code> * can be provided, either as XML string or as an already parsed XML document. Exactly one of <code>viewName</code> * or <code>definition</code> must be given, if none or both are given, an error will be reported. * * The <code>controller</code> property is optional and can hold a controller instance. When given, it overrides * the controller class defined in the view definition. * * <strong>Note</strong>: On root level, you can only define content for the default aggregation, e.g. without * adding the <code>&lt;content&gt;</code> tag. If you want to specify content for another aggregation of a view * like <code>dependents</code>, place it in a child control's <code>dependents</code> aggregation or add it * by using {@link sap.ui.core.mvc.XMLView#addDependent}. * * <strong>Note</strong>: If you enable caching, you need to take care of the invalidation via keys. Automatic * invalidation takes only place if the UI5 version or the component descriptor (manifest.json) change. This is * still an experimental feature and may experience slight changes of the invalidation parameters or the cache * key format. * * @param {object} oOptions - An object containing the view configuration options. * @param {string} [oOptions.id] - Specifies an ID for the View instance. If no ID is given, an ID will be generated. * @param {string} [oOptions.viewName] - Corresponds to an XML module that can be loaded via the module system * (oOptions.viewName + suffix ".view.xml") * @param {string|Document} [oOptions.definition] - XML string or XML document that defines the view. * Exactly one of <code>viewName</code> or <code>definition</code> must be given. * @param {sap.ui.core.mvc.Controller} [oOptions.controller] - Controller instance to be used for this view. * The given controller instance overrides the controller defined in the view definition. * Sharing one controller instance between multiple views is not possible. * @param {object} [oOptions.cache] - Cache configuration; caching gets active when this object is provided * with vView.cache.keys array; keys are used to store data in the cache and for invalidation * of the cache. * @param {Array<(string|Promise<string>)>} [oOptions.cache.keys] - Array with strings or Promises resolving with strings * @param {object} [oOptions.preprocessors] Preprocessors configuration, see {@link sap.ui.core.mvc.View} * <strong>Note</strong>: These preprocessors are only available to this instance. * For global or on-demand availability use {@link sap.ui.core.mvc.XMLView.registerPreprocessor}. * @public * @static * @returns {Promise<sap.ui.core.mvc.XMLView>} A Promise that resolves with the view instance or rejects with any thrown error. */ XMLView.create = function (oOptions) { var mParameters = deepExtend({}, oOptions); // mapping renamed parameters mParameters.viewContent = mParameters.definition; // defaults for the async API mParameters.async = true; mParameters.type = ViewType.XML; // for now the processing mode is always set to default, might be changeable later, e.g. "parallel" mParameters.processingMode = mParameters.processingMode || "sequential"; return View.create(mParameters); }; /** * The type of the view used for the <code>sap.ui.view</code> factory * function. This property is used by the parsers to define the specific * view type. * @private */ XMLView._sType = ViewType.XML; /** * Flag for feature detection of asynchronous loading/rendering * @public * @since 1.30 */ XMLView.asyncSupport = true; /** * Flag indicating whether to use the cache * @private * @experimental * @since 1.44 */ XMLView._bUseCache = sap.ui.getCore().getConfiguration().getViewCache() && Cache._isSupportedEnvironment(); function validatexContent(xContent) { if (xContent.parseError.errorCode !== 0) { var oParseError = xContent.parseError; throw new Error( "The following problem occurred: XML parse Error for " + oParseError.url + " code: " + oParseError.errorCode + " reason: " + oParseError.reason + " src: " + oParseError.srcText + " line: " + oParseError.line + " linepos: " + oParseError.linepos + " filepos: " + oParseError.filepos ); } } function validateViewSettings(oView, mSettings) { if (!mSettings) { throw new Error("mSettings must be given"); } else if (mSettings.viewName && mSettings.viewContent) { throw new Error("View name and view content are given. There is no point in doing this, so please decide."); } else if ((mSettings.viewName || mSettings.viewContent) && mSettings.xmlNode) { throw new Error("View name/content AND an XML node are given. There is no point in doing this, so please decide."); } else if (!(mSettings.viewName || mSettings.viewContent) && !mSettings.xmlNode) { throw new Error("Neither view name/content nor an XML node is given. One of them is required."); } else if (mSettings.cache && !(mSettings.cache.keys && mSettings.cache.keys.length)) { throw new Error("No cache keys provided. At least one is required."); } } function getxContent(oView, mSettings) { // keep the content as a pseudo property to make cloning work but without supporting mutation // TODO model this as a property as soon as write-once-during-init properties become available oView.mProperties["viewContent"] = mSettings.viewContent; var xContent = XMLHelper.parse(mSettings.viewContent); validatexContent(xContent); return xContent.documentElement; } /** * * @param oView * @param mSettings * @returns {undefined|Promise} will return a Promise if ResourceModel is instantiated asynchronously, otherwise undefined */ function setResourceModel(oView, mSettings) { if ((oView._resourceBundleName || oView._resourceBundleUrl) && (!mSettings.models || !mSettings.models[oView._resourceBundleAlias])) { var oModel = new ResourceModel({ bundleName: oView._resourceBundleName, bundleUrl: oView._resourceBundleUrl, bundleLocale: oView._resourceBundleLocale, async: mSettings.async }); var vBundle = oModel.getResourceBundle(); // if ResourceBundle was created with async flag vBundle will be a Promise if (vBundle instanceof Promise) { return vBundle.then(function() { oView.setModel(oModel, oView._resourceBundleAlias); }); } oView.setModel(oModel, oView._resourceBundleAlias); } } function setAfterRenderingNotifier(oView) { // Delegate for after rendering notification before onAfterRendering of child controls oView.oAfterRenderingNotifier = new XMLAfterRenderingNotifier(); oView.oAfterRenderingNotifier.addDelegate({ onAfterRendering: function() { oView.onAfterRenderingBeforeChildren(); } }); } function getRootComponent(oSrcElement) { var Component = sap.ui.require("sap/ui/core/Component"), oComponent; while (oSrcElement && Component) { var oCandidateComponent = Component.getOwnerComponentFor(oSrcElement); if (oCandidateComponent) { oSrcElement = oComponent = oCandidateComponent; } else { if (oSrcElement instanceof Component) { oComponent = oSrcElement; } oSrcElement = oSrcElement.getParent && oSrcElement.getParent(); } } return oComponent; } function getCacheInput(oView, mCacheSettings) { var oRootComponent = getRootComponent(oView), sManifest = oRootComponent ? JSON.stringify(oRootComponent.getManifest()) : null, aFutureKeyParts = []; aFutureKeyParts = aFutureKeyParts.concat( getCacheKeyPrefixes(oView, oRootComponent), getVersionInfo(), getCacheKeyProviders(oView), mCacheSettings.keys ); return validateCacheKey(oView, aFutureKeyParts).then(function(sKey) { return { key: sKey + "(" + hash(sManifest || "") + ")", componentManifest: sManifest, additionalData: mCacheSettings.additionalData }; }); } function isValidKey(sKey) { return sKey; } function validateCacheKey(oView, aFutureKeyParts) { return Promise.all(aFutureKeyParts).then(function(aKeys) { aKeys = aKeys.filter(function(oElement) { return oElement !== notCacheRelevant; }); if (aKeys.every(isValidKey)) { return aKeys.join('_'); } else { var e = new Error("Provided cache keys may not be empty or undefined."); e.name = sXMLViewCacheError; return Promise.reject(e); } }); } function getCacheKeyPrefixes(oView, oRootComponent) { var sComponentName = oRootComponent && oRootComponent.getMetadata().getName(); return [ sComponentName || window.location.host + window.location.pathname, oView.getId(), sap.ui.getCore().getConfiguration().getLanguageTag() ].concat(oRootComponent && oRootComponent.getActiveTerminologies() || []); } function getCacheKeyProviders(oView) { var mPreprocessors = oView.getPreprocessors(), oPreprocessorInfo = oView.getPreprocessorInfo(/*bSync =*/false), aFutureCacheKeys = []; function pushFutureKey(o) { aFutureCacheKeys.push(o.preprocessor .then(function(oPreprocessorImpl) { if (oPreprocessorImpl.getCacheKey) { return oPreprocessorImpl.getCacheKey(oPreprocessorInfo); } else { /* We cannot check for the getCacheKey function synchronous, but we later need * to differentiate whether the result of getCacheKey returns an invalid result * (null/undefined) or the function simply does not exist. * Therefore we use the 'notCacheRelevant' token to mark preProcessors that does * not provide a getCacheKey function and so are not relevant for caching. * See validateCacheKey function. */ return notCacheRelevant; } }) ); } for (var sType in mPreprocessors) { mPreprocessors[sType].forEach(pushFutureKey); } return aFutureCacheKeys; } function getVersionInfo() { return sap.ui.getVersionInfo({async:true}).then(function(oInfo) { var sTimestamp = ""; if (!oInfo.libraries) { sTimestamp = sap.ui.buildinfo.buildtime; } else { oInfo.libraries.forEach(function(oLibrary) { sTimestamp += oLibrary.buildTimestamp; }); } return sTimestamp; }).catch(function(error) { // Do not populate the cache if the version info could not be retrieved. Log.warning("sap.ui.getVersionInfo could not be retrieved", "sap.ui.core.mvc.XMLView"); Log.debug(error); return ""; }); } function writeCache(mCacheInput, xContent) { // we don't want to write the key into the cache var sKey = mCacheInput.key; delete mCacheInput.key; mCacheInput.xml = XMLHelper.serialize(xContent); return Cache.set(sKey, mCacheInput); } function readCache(mCacheInput) { return Cache.get(mCacheInput.key).then(function(mCacheOutput) { // double check manifest to eliminate issues with hash collisions if (mCacheOutput && mCacheOutput.componentManifest == mCacheInput.componentManifest) { mCacheOutput.xml = XMLHelper.parse(mCacheOutput.xml, "application/xml").documentElement; if (mCacheOutput.additionalData) { // extend the additionalData which was passed into cache configuration dynamically deepExtend(mCacheInput.additionalData, mCacheOutput.additionalData); } return mCacheOutput; } }); } /** * This function initialized the view settings. * * @param {object} mSettings with view settings * @returns {Promise|null} will be returned if running in async mode */ XMLView.prototype.initViewSettings = function(mSettings) { var that = this, _xContent; function processView(xContent) { that._xContent = xContent; if (View._supportInfo) { View._supportInfo({context: that._xContent, env: {caller:"view", viewinfo: deepExtend({}, that), settings: deepExtend({}, mSettings || {}), type: "xmlview"}}); } // extract the properties of the view from the XML element if ( !that.isSubView() ) { // for a real XMLView, we need to parse the attributes of the root node var mSettingsFromXML = {}; // enrich mSettingsFromXML XMLTemplateProcessor.parseViewAttributes(xContent, that, mSettingsFromXML); if (!mSettings.async) { // extend mSettings which get applied implicitly during view constructor Object.assign(mSettings, mSettingsFromXML); } else { // apply the settings from the loaded view source via an explicit call that.applySettings(mSettingsFromXML); } } else { // when used as fragment: prevent connection to controller, only top level XMLView must connect delete mSettings.controller; } // vSetResourceModel is a promise if ResourceModel is created async var vSetResourceModel = setResourceModel(that, mSettings); if (vSetResourceModel instanceof Promise) { return vSetResourceModel.then(function() { setAfterRenderingNotifier(that); }); } setAfterRenderingNotifier(that); } function runViewxmlPreprocessor(xContent, bAsync) { if (that.hasPreprocessor("viewxml")) { // for the viewxml preprocessor fully qualified ids are provided on the xml source return XMLTemplateProcessor.enrichTemplateIdsPromise(xContent, that, bAsync).then(function() { return that.runPreprocessor("viewxml", xContent, !bAsync); }); } return xContent; } function runPreprocessorsAsync(xContent) { var fnDone = Interaction.notifyAsyncStep("VIEW PREPROCESSING"); return that.runPreprocessor("xml", xContent).then(function(xContent) { return runViewxmlPreprocessor(xContent, /*bAsync=*/true); }) .finally(fnDone); } function loadResourceAsync(sResourceName) { return LoaderExtensions.loadResource(sResourceName, {async: true}).then(function(oData) { return oData.documentElement; // result is the document node }); } function processResource(sResourceName, mCacheInput) { return loadResourceAsync(sResourceName).then(runPreprocessorsAsync).then(function(xContent) { if (mCacheInput) { writeCache(mCacheInput, xContent); } return xContent; }); } function processCache(sResourceName, mCacheSettings) { return getCacheInput(that, mCacheSettings).then(function(mCacheInput) { return readCache(mCacheInput).then(function(mCacheOutput) { if (!mCacheOutput) { return processResource(sResourceName, mCacheInput); } else { return mCacheOutput.xml; } }); }).catch(function(error) { if (error.name === sXMLViewCacheError) { // no sufficient cache keys, processing can continue Log.debug(error.message, error.name, "sap.ui.core.mvc.XMLView"); Log.debug("Processing the View without caching.", "sap.ui.core.mvc.XMLView"); return processResource(sResourceName); } else { // an unknown error occured and should be exposed return Promise.reject(error); } }); } this._oContainingView = mSettings.containingView || this; this._sProcessingMode = mSettings.processingMode; if (this.oAsyncState) { // suppress rendering of preserve content this.oAsyncState.suppressPreserve = true; } validateViewSettings(this, mSettings); // either template name or XML node is given if (mSettings.viewName) { var sResourceName = mSettings.viewName.replace(/\./g, "/") + ".view.xml"; if (mSettings.async) { // in async mode we need to return here as processing takes place in Promise callbacks if (mSettings.cache && XMLView._bUseCache) { return processCache(sResourceName, mSettings.cache).then(processView); } else { return loadResourceAsync(sResourceName).then(runPreprocessorsAsync).then(processView); } } else { _xContent = LoaderExtensions.loadResource(sResourceName).documentElement; } } else if (mSettings.viewContent) { if (mSettings.viewContent.nodeType === window.Node.DOCUMENT_NODE) { // Check for XML Document _xContent = mSettings.viewContent.documentElement; } else { _xContent = getxContent(this, mSettings); } } else if (mSettings.xmlNode) { _xContent = mSettings.xmlNode; } if (mSettings.async) { // a normal Promise: return runPreprocessorsAsync(_xContent).then(processView); } else { // a SyncPromise _xContent = this.runPreprocessor("xml", _xContent, true); _xContent = runViewxmlPreprocessor(_xContent, false); // if the _xContent is a SyncPromise we have to extract the _xContent // and make sure we throw any occurring errors further if (_xContent && typeof _xContent.getResult === 'function') { if (_xContent.isRejected()) { // sync promises store the error within the result if they are rejected throw _xContent.getResult(); } _xContent = _xContent.getResult(); } processView(_xContent); } }; XMLView.prototype.onBeforeRendering = function() { // make sure to preserve the content if not preserved yet var oDomRef = this.getDomRef(); if (oDomRef && !RenderManager.isPreservedContent(oDomRef)) { RenderManager.preserveContent(oDomRef, /* bPreserveRoot= */ true); } View.prototype.onBeforeRendering.apply(this, arguments); }; XMLView.prototype.exit = function() { if (this.oAfterRenderingNotifier) { this.oAfterRenderingNotifier.destroy(); } View.prototype.exit.apply(this, arguments); }; XMLView.prototype.onControllerConnected = function(oController) { var that = this; // unset any preprocessors (e.g. from an enclosing JSON view) // create a function, which scopes the instance creation of a class with the corresponding owner ID // XMLView special logic for asynchronous template parsing, when component loading is async but // instance creation is sync. function fnRunWithPreprocessor(fn) { return ManagedObject.runWithPreprocessors(fn, { settings: that._fnSettingsPreprocessor }); } // parse the XML tree if (!this.oAsyncState) { this._aParsedContent = fnRunWithPreprocessor(XMLTemplateProcessor.parseTemplate.bind(null, this._xContent, this)); } else { var fnDone = Interaction.notifyAsyncStep("VIEW PROCESSING"); return XMLTemplateProcessor.parseTemplatePromise(this._xContent, this, true, { fnRunWithPreprocessor: fnRunWithPreprocessor }).then(function(aParsedContent) { that._aParsedContent = aParsedContent; // allow rendering of preserve content delete that.oAsyncState.suppressPreserve; }).finally(fnDone); } }; XMLView.prototype.getControllerName = function() { return this._controllerName; }; XMLView.prototype.isSubView = function() { return this._oContainingView != this; }; /** * If the HTML doesn't contain own content, it tries to reproduce existing content * This is executed before the onAfterRendering of the child controls, to ensure that * the HTML is already at its final position, before additional operations are executed. */ XMLView.prototype.onAfterRenderingBeforeChildren = function() { if ( this._$oldContent.length !== 0 ) { // Log.debug("after rendering for " + this); // move DOM of children into correct place in preserved DOM var aChildren = this.getAggregation("content"); if ( aChildren ) { for (var i = 0; i < aChildren.length; i++) { // Get current DOM of the child or the invisible placeholder for it. // For children that do DOM preservation on their own, use the temporary DOM, // they'll move their old DOM themselves var oNewChildDOM = document.getElementById(RenderPrefixes.Temporary + aChildren[i].getId()) || aChildren[i].getDomRef() || document.getElementById(RenderPrefixes.Invisible + aChildren[i].getId()); // if such DOM exists, replace the placeholder in the view's DOM with it if ( oNewChildDOM ) { jQuery(document.getElementById(RenderPrefixes.Dummy + aChildren[i].getId())).replaceWith(oNewChildDOM); } // otherwise keep the dummy placeholder } } // move preserved DOM into place // Log.debug("moving preserved dom into place for " + this); jQuery(document.getElementById(RenderPrefixes.Temporary + this.getId())).replaceWith(this._$oldContent); } this._$oldContent = undefined; }; XMLView.prototype._onChildRerenderedEmpty = function(oControl, oElement) { // when the render manager notifies us about an empty child rendering, we replace the old DOM with a dummy jQuery(oElement).replaceWith('<div id="' + RenderPrefixes.Dummy + oControl.getId() + '" class="sapUiHidden"></div>'); return true; // indicates that we have taken care }; /** * Register a preprocessor for all views of a specific type. * * The preprocessor can be registered for several stages of view initialization, for xml views these are * either the plain "xml" or the already initialized "controls" , see {@link sap.ui.core.mvc.XMLView.PreprocessorType}. * For each type one preprocessor is executed. If there is a preprocessor passed to or activated at the * view instance already, that one is used. When several preprocessors are registered for one hook, it has to be made * sure, that they do not conflict when being processed serially. * * It can be either a module name as string of an implementation of {@link sap.ui.core.mvc.View.Preprocessor} or a * function with a signature according to {@link sap.ui.core.mvc.View.Preprocessor.process}. * * <strong>Note</strong>: Preprocessors work only in async views and will be ignored when the view is instantiated * in sync mode by default, as this could have unexpected side effects. You may override this behaviour by setting the * bSyncSupport flag to true. * * @public * @static * @param {string|sap.ui.core.mvc.XMLView.PreprocessorType} sType * the type of content to be processed * @param {string|function} vPreprocessor * module path of the preprocessor implementation or a preprocessor function * @param {boolean} bSyncSupport * declares if the vPreprocessor ensures safe sync processing. This means the preprocessor will be executed * also for sync views. Please be aware that any kind of async processing (like Promises, XHR, etc) may * break the view initialization and lead to unexpected results. * @param {boolean} [bOnDemand] * ondemand preprocessor which enables developers to quickly activate the preprocessor for a view, * by setting <code>preprocessors : { xml }</code>, for example. * @param {object} [mSettings] * optional configuration for preprocessor */ XMLView.registerPreprocessor = function(sType, vPreprocessor, bSyncSupport, bOnDemand, mSettings) { sType = sType.toUpperCase(); if (XMLView.PreprocessorType[sType]) { View.registerPreprocessor(XMLView.PreprocessorType[sType], vPreprocessor, this.getMetadata().getClass()._sType, bSyncSupport, bOnDemand, mSettings); } else { Log.error("Preprocessor could not be registered due to unknown sType \"" + sType + "\"", this.getMetadata().getName()); } }; /** * Specifies the available preprocessor types for XMLViews * * @see sap.ui.core.mvc.XMLView * @see sap.ui.core.mvc.View.Preprocessor * @enum {string} * @public */ XMLView.PreprocessorType = { /** * This preprocessor receives the plain xml source of the view and should also return a valid * xml ready for view creation * @public */ XML : "xml", /** * This preprocessor receives a valid xml source for View creation without any template tags but with control * declarations. These include their full IDs by which they can also be queried during runtime. * @public */ VIEWXML : "viewxml", /** * This preprocessor receives the control tree produced through the view source * @public */ CONTROLS : "controls" }; /** * Dummy control for after rendering notification before onAfterRendering of * child controls of the XMLView is called * @extends sap.ui.core.Control * @alias sap.ui.core.mvc.XMLAfterRenderingNotifier * @private */ var XMLAfterRenderingNotifier = Control.extend("sap.ui.core.mvc.XMLAfterRenderingNotifier", { metadata: { library: "sap.ui.core" }, renderer: { apiVersion: 2, render: function(oRM, oControl) { oRM.text(""); // onAfterRendering is only called if control produces output } } }); // Register OpenUI5 default preprocessor for templating XMLView.registerPreprocessor("xml", "sap.ui.core.util.XMLPreprocessor", true, true); return XMLView; });