UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

621 lines (528 loc) 18.8 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 base class sap.ui.core.tmpl.Template for all templates sap.ui.define([ 'sap/ui/base/ManagedObject', 'sap/ui/base/BindingInfo', 'sap/ui/base/BindingParser', 'sap/ui/core/Control', 'sap/ui/core/RenderManager', 'sap/base/util/ObjectPath', 'sap/base/Log', 'sap/base/assert', 'sap/ui/thirdparty/jquery', 'sap/ui/core/tmpl/TemplateControl', './_parsePath' ], function( ManagedObject, BindingInfo, BindingParser, Control, RenderManager, ObjectPath, Log, assert, jQuery, TemplateControl, parsePath ) { "use strict"; /** * Creates and initializes a new template with the given <code>sId</code> and * settings. * * The set of allowed entries in the <code>mSettings</code> object depends on * the concrete subclass and is described there. * * @param {string} * [sId] optional id for the new template; generated automatically if * no non-empty id is given Note: this can be omitted, no matter * whether <code>mSettings</code> will be given or not! * @param {object} * [mSettings] optional map/JSON-object with initial settings for the * new component instance * @public * * @class Base Class for Template. * @extends sap.ui.base.ManagedObject * @abstract * @author SAP SE * @version 1.111.5 * @alias sap.ui.core.tmpl.Template * @since 1.15 * @deprecated since 1.56, use an {@link sap.ui.core.mvc.XMLView XMLView} or a {@link topic:e6bb33d076dc4f23be50c082c271b9f0 Typed View} instead. */ var Template = ManagedObject.extend("sap.ui.core.tmpl.Template", /** @lends sap.ui.core.tmpl.Template.prototype */ { constructor : function(sId, mSettings) { ManagedObject.apply(this, arguments); }, metadata : { stereotype : "template", "abstract" : true, library : "sap.ui.core", properties : { /** * The Template definition as a String. */ "content" : {type : "string", group : "Data", defaultValue : null} }, publicMethods : [ // methods "declareControl", /* protected */ "createControl", /* protected */ "placeAt", "createMetadata", "createRenderer" ] } }); var mTemplates = {}; /** * @private */ Template.prototype.register = function() { var sId = this.getId(), oOldTemplate = mTemplates[sId], sMsg; if ( oOldTemplate && this !== oOldTemplate ) { sMsg = "adding template with duplicate id '" + sId + "'"; Log.error(sMsg); throw new Error("Error: " + sMsg); } mTemplates[sId] = this; }; /** * @private */ Template.prototype.deregister = function() { delete mTemplates[this.getId()]; }; /** * Returns the registered template for the given ID, if any. * @param {string} sId * @return {sap.ui.core.tmpl.Template} the template for the given ID * @public */ Template.byId = function(sId) { return mTemplates[sId]; }; /** * Templates don't have a facade and therefore return themselves as their interface. * * @returns {this} <code>this</code> as there's no facade for templates * @see sap.ui.base.Object#getInterface * @public */ Template.prototype.getInterface = function() { return this; }; /** * registry for supported template types * @private */ Template._mSupportedTypes = {}; /** * Registers a new Template type which is used to compile the template. * * @param {string} sType type of the template * @param {string} sClass the class of the specifc Template element * * @static */ Template.registerType = function(sType, sClass) { Template._mSupportedTypes[sType] = sClass; }; /** * Unregisters a Template type which is used to compile the template. * * @param {string} sType type of the template * * @static */ Template.unregisterType = function(sType) { delete Template._mSupportedTypes[sType]; }; /** * parses the given path and extracts the model and path * * @param {string} sPath the path * @return {object} the model and the path * * @protected * @static */ Template.parsePath = parsePath; /* * overridden to prevent instantiation of Template * @name sap.ui.core.tmpl.Template#init * @function */ Template.prototype.init = function(mSettings, oScope) { if (this.getMetadata().getName() === "sap.ui.core.tmpl.Template") { throw new Error("The class 'sap.ui.core.tmpl.Template' is abstract and must not be instantiated!"); } // check for complex binding syntax if (BindingInfo.parse === BindingParser.complexParser) { /* * we disable the complex binding parser for Templates * TODO: reconsider a better solution later * @name sap.ui.core.tmpl.Template#extractBindingInfo * @function */ Template.prototype.extractBindingInfo = function(oValue, bIgnoreObjects, oScope) { BindingInfo.parse = BindingParser.simpleParser; var oReturnValue = Control.prototype.extractBindingInfo.apply(this, arguments); BindingInfo.parse = BindingParser.complexParser; return oReturnValue; }; } }; /** * Declares a new control based on this template and returns the created * class / constructor function. The class is based on the information coming * from the abstract functions <code>createMetadata</code> and * <code>createRenderer</code>. * * @param {string} sControl the fully qualified name of the control * @return {function} the created class / constructor function * @public */ Template.prototype.declareControl = function(sControl) { assert(!!sControl, "A fully qualified name must be specified!"); if (sControl) { // create the new control type var oMetadata = this.createMetadata(), fnRenderer = this.createRenderer(), that = this; TemplateControl.extend(sControl, { // the new control metadata metadata: oMetadata, // set the reference to the template init: function() { TemplateControl.prototype.init.apply(this, arguments); // link to the template this.setTemplate(that); }, // add the custom renderer function renderer: { renderTemplate: fnRenderer, hasControlData: oMetadata._hasControlData } }); // returns the constructor function return ObjectPath.get(sControl || ""); } }; /** * Creates an anonymous TemplateControl for the Template. * * @param {string} sId the control ID * @param {object} [oContext] the context for the renderer/templating * @param {sap.ui.core.mvc.View} oView * @return {sap.ui.core.tmpl.TemplateControl} the created control instance * @public */ Template.prototype.createControl = function(sId, oContext, oView) { // create the anonymous control instance var oControl = new TemplateControl({ id: sId, template: this, context: oContext }); // for anonymous controls the renderer functions is added to the control instance oControl.setTemplateRenderer(this.createRenderer(oView)); // return the control return oControl; }; /** * Creates an anonymous TemplateControl for the Template and places the control * into the specified DOM element. * * @param {string|Element|sap.ui.core.Control} oRef the id or the DOM reference where to render the template * @param {object} [oContext] The context to use to evaluate the Template. It will be applied as value for the context property of the created control. * @param {string|int} [vPosition] Describes the position where the control should be put into the container * @param {boolean} bInline * @return {sap.ui.core.tmpl.TemplateControl} the created control instance * @public */ Template.prototype.placeAt = function(oRef, oContext, vPosition, bInline) { // parameter fallback if (typeof oContext === "string" || typeof oContext === "number") { vPosition = oContext; oContext = undefined; } // if the oRef is an ID or DomRef and the template should be rendered // inline we lookup the context from DOM element and mark the template // as an inline template to avoid additional elements around the template. var sId; if (!(oRef instanceof Control) && bInline) { // lookup the DOM element in which to place the template var $this = typeof oRef === "string" ? jQuery(document.getElementById(oRef)) : jQuery(oRef); // the DOM element must exist if ($this.length > 0) { // reuse the id for the template control sId = $this.attr("id"); oRef = $this.get(0); // by default the context coming from sap.ui.template method will be used // but it can be also defined on the root DOM element for inline templates // in case of inline templates we mark them var sContext = $this.attr("data-context"); oContext = oContext || sContext && JSON.parse(sContext); // mark the template as inline template (to avoid extra DOM for the TemplateControl) // for inline templates the UIArea and the TemplateControl are the same DOM element RenderManager.markInlineTemplate($this); } } // create the control (ID will be generated if not inline) var oControl = this.createControl(sId, oContext); // render the control into the specified domref oControl.placeAt(oRef, vPosition); // return the control return oControl; }; /** * Returns the metadata object for the new Control class. * This function needs to be implemented by sub classes of the Template. * * @return {object} the metadata object of the new control class * @abstract */ Template.prototype.createMetadata = function() { Log.error("The function createMetadata is an abstract function which needs to be implemented by subclasses."); }; /** * Returns the renderer function for the new Control class. * This function needs to be implemented by sub classes of the Template. * * @return {any} the renderer function for the new Control class. * @abstract */ Template.prototype.createRenderer = function() { Log.error("The function createRenderer is an abstract function which needs to be implemented by subclasses."); }; /** * Creates a Template for the given ID, DOM reference or a configuration object. * * If no parameter is defined, this function makes a lookup of DOM elements * which are specifying a type attribute. If the value of this type attribute * matches a registered type then the content of this DOM element will be * used to create a new <code>Template</code> instance. * * If you want to lookup all kind of existing and known templates and parse them * directly you can simply call: * <pre> * sap.ui.template(); * </pre> * * To parse a concrete DOM element you can do so by using this function in the * following way: * <pre> * sap.ui.template("theTemplateId"); * </pre> * * Or you can pass the reference to a DOM element and use this DOM element as * a source for the template: * <pre> * sap.ui.template(oDomRef); * </pre> * * The last option to use this function is to pass the information via a * configuration object. This configuration object can be used to pass a * context for the templating framework when compiling the template: * <pre> * var oTemplateById = sap.ui.template({ * id: "theTemplateId", * context: { ... } * }); * * var oTemplateByDomRef = sap.ui.template({ * domref: oDomRef, * context: { ... } * }); * </pre> * * It can also be used to load a template from another file: * <pre> * var oTemplate = sap.ui.template({ * id: "myTemplate", * src: "myTemplate.tmpl" * }); * * var oTemplateWithContext = sap.ui.template({ * id: "myTemplate", * src: "myTemplate.tmpl", * context: { ... } * }); * </pre> * * @param {string|Element|object} [oTemplate] the ID or the DOM reference to the template to lookup or a configuration object containing the src, type and eventually the ID of the Template. * @param {string} oTemplate.id - the ID of the Template / the ID of the DOM element containing the source of the Template</li> * @param {Element} oTemplate.domref - the DOM element containing the source of the Template</li> * @param {string} [oTemplate.type] - the type of the Template</li> * @param {object} [oTemplate.context] the context for the renderer/templating * @param {string} [oTemplate.src] - the URL to lookup the template</li> (<i>experimental!</i>) * @param {string} oTemplate.control - the fully qualified name of the control to declare</li> (<i>experimental!</i>) * @return {sap.ui.core.tmpl.Template | sap.ui.core.tmpl.Template[]} the created Template instance * or in case of usage without parameters any array of templates is returned * @deprecated since 1.56, use an {@link sap.ui.core.mvc.XMLView XMLView} or {@link sap.ui.core.mvc.JSView JSView} instead. * @public * @static * @ui5-global-only */ sap.ui.template = function(oTemplate) { var fnLogDeprecation = function(sName) { Log.warning("The usage of Template Views is deprecated since 1.56. Please make use of the asynchronous create functions of the different view classes, e.g. XMLView.create()","Deprecation", null, function() { return { type: "sap.ui.template", name: sName }; }); }; // when no oTemplate is defined we need to lookup the elements in the document // and retrieve elements which have a type attribute which contains a value // of the supported types: if (!oTemplate) { // lookup all kind of DOM elements for having a type which is supported var aTemplates = []; jQuery.each(Template._mSupportedTypes, function(sType, sClass) { // @legacy-relevant: jQuery usage in deprecated code jQuery("script[type='" + sType + "'], [data-type='" + sType + "']").each(function(iIndex, oElement) { aTemplates.push(sap.ui.template({ id: oElement.id, domref: oElement, type: sType, _class: sClass /* helper to save lookup time in the supported types */ })); }); }); return aTemplates; } else { // check the settings for being a string or a DOM element if (typeof oTemplate === "string") { return sap.ui.template({ id: oTemplate }); } else if (oTemplate && oTemplate.tagName && oTemplate.nodeName && oTemplate.ownerDocument && oTemplate.nodeType === 1) { // instanceof HTMLElement only works for modern browsers! return sap.ui.template({ id: oTemplate.id, domref: oTemplate }); } // apply the default values oTemplate = jQuery.extend({ // @legacy-relevant: jQuery usage in deprecated code type: Template.DEFAULT_TEMPLATE }, oTemplate); fnLogDeprecation(oTemplate.id); // in case of specifying a src attribute for the configuration object // we load the template from a remote resource var sId, sType, sControl, sContent, sController = false, bLoadTemplate = typeof oTemplate.src === "string", bInline = false; if (bLoadTemplate) { // load the template from the specified URL jQuery.ajax({ // @legacy-relevant: jQuery usage in deprecated code url: oTemplate.src, dataType: "text", async: false, success: function(data) { // apply the content as template content // set the id, type and control if defined in the object sId = oTemplate.id; sType = oTemplate.type; sControl = oTemplate.control; sContent = data; //Check for inline template information var rTmplInfo = /^<!--\sUI5:Template\stype=([a-z\/\-]*)\s(?:controller=([A-Za-z.]*)\s)?-->/, aTmplInfo = sContent.match(rTmplInfo); if (aTmplInfo) { sType = aTmplInfo[1]; if (aTmplInfo.length == 3) { sController = aTmplInfo[2]; } sContent = sContent.substr(aTmplInfo[0].length); } }, error: function() { throw new Error("The template could not be loaded from " + oTemplate.src + "!"); } }); } else { // retrieve the required properties var oElement = oTemplate.domref || ((oTemplate.id ? window.document.getElementById(oTemplate.id) : null)), $element = jQuery(oElement); bInline = false; // lookup the missing properties sId = oTemplate.id || oElement && oElement.id; sType = $element.attr("type") || oTemplate.type; sControl = $element.attr("data-control") || oTemplate.control; // lookup if the template for the current id and check this element for // beeing a subclass of sap.ui.core.tmpl.Template and return the existing // instance if found if (sId) { var theTemplate = sap.ui.getCore().getTemplate(sId); // eslint-disable-next-line no-unsafe-negation if (!theTemplate instanceof Template) { throw new Error("Object for id \"" + sId + "\" is no sap.ui.core.tmpl.Template!"); } else { if (theTemplate) { return theTemplate; } } } // the element to parse must exist if ($element.length === 0) { throw new Error("DOM element for the Template with the id \"" + sId + "\" not found!"); } // retrieve the content sContent = $element.html(); // check the preconditions for rendering and set the render property // if the DOM ref is part of the documents body var sTagName = oElement.tagName.toLowerCase(); if (sTagName !== "script") { bInline = $element.parents("body").length === 1; } } // if not class is given we fallback to the type attribute on the // template defintion. var sClass = oTemplate._class; if (!sClass) { sClass = Template._mSupportedTypes[sType]; if (!sClass) { //sType = sap.ui.core.tmpl.Template.DEFAULT_TEMPLATE; throw new Error("The type \"" + sType + "\" is not supported."); } } // require and instantiate the proper template var fnClass = sap.ui.requireSync(sClass.replace(/\./g, "/")); // legacy-relevant: Template is deprecated since 1.56 and XMLView should be used instead fnClass = fnClass || ObjectPath.get(sClass || ""); // create a new instance of the template var oInstance = new fnClass({ id: sId, content: sContent }); // declare the control if specified if (sControl) { oInstance.declareControl(sControl); } // render inline templates immediately if (sController) { oInstance._sControllerName = sController; } // render inline templates immediately if (bInline) { oInstance.placeAt(sId, oTemplate.context, undefined, true); } // return the template instance return oInstance; } }; // define and register the default template Template.DEFAULT_TEMPLATE = "text/x-handlebars-template"; Template.registerType(Template.DEFAULT_TEMPLATE, "sap.ui.core.tmpl.HandlebarsTemplate"); return Template; });