@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
621 lines (528 loc) • 18.8 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.
*/
// 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;
});