UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

403 lines (346 loc) 11.1 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.ui.core.tmpl.TemplateControl. sap.ui.define([ 'sap/ui/core/Control', 'sap/ui/core/DeclarativeSupport', 'sap/ui/core/UIArea', './DOMElement', "./TemplateControlRenderer", "./_parsePath", "sap/base/strings/capitalize", "sap/base/strings/hyphenate", "sap/base/Log", "sap/ui/thirdparty/jquery", 'sap/ui/core/library' ], function( Control, DeclarativeSupport, UIArea, DOMElement, TemplateControlRenderer, parsePath, capitalize, hyphenate, Log, jQuery ) { "use strict"; /** * Constructor for a new tmpl/TemplateControl. * * @param {string} [sId] id for the new control, generated automatically if no id is given * @param {object} [mSettings] initial settings for the new control * * @class * This is the base class for all template controls. Template controls are declared based on templates. * @extends sap.ui.core.Control * @version 1.111.5 * * @public * @since 1.15 * @deprecated since 1.56 * @alias sap.ui.core.tmpl.TemplateControl */ var TemplateControl = Control.extend("sap.ui.core.tmpl.TemplateControl", /** @lends sap.ui.core.tmpl.TemplateControl.prototype */ { metadata : { library : "sap.ui.core", properties : { /** * The context is a data object. It can be used for default template expressions. A change of the context object leads to a re-rendering whereas a change of a nested property of the context object doesn't. By default the context is an empty object. */ context : {type : "object", group : "Data", defaultValue : null} }, aggregations : { /** * The nested controls of the template control */ controls : {type : "sap.ui.core.Control", multiple : true, singularName : "control", visibility : "hidden"} }, associations : { /** * The template on which the template control is based on. */ template : {type : "sap.ui.core.tmpl.Template", multiple : false} }, events : { /** * Fired when the Template Control has been (re-)rendered and its HTML is present in the DOM. */ afterRendering : {}, /** * Fired before this Template Control is re-rendered. Use to unbind event handlers from HTML elements etc. */ beforeRendering : {} } }, renderer: TemplateControlRenderer }); /* * Initialization of the sap.ui.core.tmpl.TemplateControl */ TemplateControl.prototype.init = function() { // list of binding information to cleanup once the // control is destroyed or re-rendering happens this._aBindingInfos = []; }; /** * checks whether the control is inline or not * * @return {boolean} flag, whether to control is inline or not * @protected */ TemplateControl.prototype.isInline = function() { // in case of inline templates the ID of the UIArea root node is the same // like the ID for the current template control (this can only be the case // for inline templates) var bInline = false, oParent = this.getParent(); if (oParent instanceof UIArea && jQuery(oParent.getRootNode()).attr("id") === this.getId()) { bInline = true; } return bInline; }; /* * Overridden to remove the old content for inline templates and clean up the * old UIArea which is in the "nirvana" now */ TemplateControl.prototype.placeAt = function(oRef, oPosition) { // in case of placing an inline template somewhere else on the screen // we remove the content and destroy the UIArea's content // TODO: how to destroy the UIArea itself? var bInline = this.isInline(); var $this = this.$(), oUIArea = this.getUIArea(); Control.prototype.placeAt.apply(this, arguments); if (bInline && $this.length === 1) { $this.remove(); oUIArea.destroyContent(); } }; /** * Returns the instance specific renderer for an anonymous template control. * * @return {function} the instance specific renderer function * @protected */ TemplateControl.prototype.getTemplateRenderer = function() { return this.fnRenderer; }; /** * Sets the instance specific renderer for an anonymous template control. * * @param {function} fnRenderer the instance specific renderer function * @return {this} <code>this</code> to allow method chaining * @protected */ TemplateControl.prototype.setTemplateRenderer = function(fnRenderer) { this.fnRenderer = fnRenderer; return this; }; /** * cleanup of the controls and bindings * @private */ TemplateControl.prototype._cleanup = function() { // destroy the controls this.destroyAggregation("controls"); // cleanup the bindings this._aBindingInfos.forEach(function(oBindingInfo) { var oBinding = oBindingInfo.binding; if ( oBinding ) { oBinding.detachChange(oBindingInfo.changeHandler); oBinding.destroy(); } }); this._aBindingInfos = []; }; /** * compile the declarative markup * @private */ TemplateControl.prototype._compile = function() { var oTemplate = sap.ui.getCore().byId(this.getTemplate()), bDeclarativeSupport = oTemplate && oTemplate.getDeclarativeSupport(); if (bDeclarativeSupport) { var that = this; setTimeout(function() { DeclarativeSupport.compile(that.getDomRef()); }); } }; /* * cleanup of the controls and bindings */ TemplateControl.prototype.exit = function() { this._cleanup(); }; /* * cleanup of the controls and bindings */ TemplateControl.prototype.onBeforeRendering = function() { this.fireBeforeRendering(); this._cleanup(); }; /* * parse nested controls which have been added via markup */ TemplateControl.prototype.onAfterRendering = function() { this.fireAfterRendering(); }; /* * clone the fnRenderer */ TemplateControl.prototype.clone = function() { var oClone = Control.prototype.clone.apply(this, arguments); oClone.fnRenderer = this.fnRenderer; return oClone; }; /* * get notified when the model changes, ... */ TemplateControl.prototype.updateBindings = function(bUpdateAll, sModelName) { Control.prototype.updateBindings.apply(this, arguments); // invalidate once the element is rendered if (this.getDomRef()) { this.invalidate(); } }; /** * Creates a pseudo binding for a value to get notified once the value * changes to invalidate the control and trigger a re-rendering. * * @param {string} sPath the binding path * @param {string} sModelFunc the name of the model function * @return {any} the value of the path (typically an array) * @private */ TemplateControl.prototype.bind = function(sPath, sType) { // parse the path and create the binding var oPathInfo = parsePath(sPath), oModel = this.getModel(oPathInfo.model), sPath = oPathInfo.path, sModelFunc = sType ? "bind" + capitalize(sType) : "bindProperty", oBinding = oModel && oModel[sModelFunc](sPath), oBindingInfo = { binding: oBinding, path: oPathInfo.path, model: oPathInfo.model }; // attach a change handler (if the binding exists) if (oBinding) { oBindingInfo.changeHandler = function() { Log.debug("TemplateControl#" + this.getId() + ": " + sType + " binding changed for path \"" + sPath + "\""); this.invalidate(); }.bind(this); oBinding.attachChange(oBindingInfo.changeHandler); } // store the binding info for later cleanup this._aBindingInfos.push(oBindingInfo); // return the external formatted value for the property return oBinding; }; /** * Calculates the path by considering the binding context if the path * is a relative path otherwise the incoming path will be returned. * * @param {string} sPath the path * @return {string} the path including the binding context * @private */ TemplateControl.prototype.calculatePath = function(sPath, sType) { var oBindingContext = this.getBindingContext(), sBindingContextPath = oBindingContext && oBindingContext.getPath(); if (sPath && sBindingContextPath && !sPath.startsWith("/")) { if (!sBindingContextPath.endsWith("/")) { sBindingContextPath += "/"; } sPath = sBindingContextPath + sPath; } return sPath; }; /** * Creates a pseudo binding for a property to get notified once the property * changes to invalidate the control and trigger a re-rendering. * * @param {string} sPath the binding path * @return {any} the value of the path * @protected */ TemplateControl.prototype.bindProp = function(sPath) { var oBinding = this.bind(this.calculatePath(sPath), "property"); return oBinding && oBinding.getExternalValue(); }; /** * Creates a pseudo binding for an aggregation to get notified once the property * changes to invalidate the control and trigger a re-rendering. * * @param {string} sPath the binding path * @return {any} the value of the path * @protected */ TemplateControl.prototype.bindList = function(sPath) { var oBinding = this.bind(this.calculatePath(sPath), "list"), oModel = oBinding && oBinding.getModel(), sPath = oBinding && oBinding.getPath(); return oBinding && oModel.getProperty(sPath); }; /** * compiles (creates and registers) a new DOM element * * @param {object} mSettings the settings for the new DOM element * @param {string} [sParentPath] the parent path for the DOM element * @param {boolean} [bDoNotAdd] if true, then the control will not be * added to the _controls aggregation * @return {sap.ui.core.Control} new DOM element instance * * @protected */ TemplateControl.prototype.createDOMElement = function(mSettings, sParentPath, bDoNotAdd) { var oElement = new DOMElement(mSettings); if (sParentPath) { // set the context for the DOM element oElement.bindObject(sParentPath); } if (!bDoNotAdd) { this.addAggregation("controls", oElement); } return oElement; }; /** * compiles (creates and registers) a new control * * @param {object} mSettings the settings for the new control * @param {string} [sParentPath] the parent path for the control * @param {boolean} [bDoNotAdd] if true, then the control will not be * added to the _controls aggregation * @param {sap.ui.core.mvc.View} oView * @return {sap.ui.core.Control} new control instance * * @protected */ TemplateControl.prototype.createControl = function(mSettings, sParentPath, bDoNotAdd, oView) { // sap.ui.core.Element.create doesn't work because there is no type // conversion for the values done (would be the better approach) var mHTMLSettings = {}; jQuery.each(mSettings, function(sKey, oValue) { // @legacy-relevant: jQuery usage in deprecated code mHTMLSettings["data-" + hyphenate(sKey)] = oValue; }); var $control = jQuery("<div></div>", mHTMLSettings); var oControl = DeclarativeSupport._createControl($control.get(0), oView); if (sParentPath) { // set the context for the control oControl.bindObject(sParentPath); } if (!bDoNotAdd) { this.addAggregation("controls", oControl); } return oControl; }; return TemplateControl; });