Understanding templateShareable in SAPUI5

In one of my old tutorials I have explained how to implement custom controls in SAPUI5/OpenUI5. There, you have learned that properties, associations, and aggregations are major parts of controls. I also described the difference between associations and aggregations. Besides that, I covered some of the important lifecycle methods available for every control, i.e. init(), onAfterRendering(), and exit(). These lifecyce APIs are called by the runtime, for example whenever a new instance of a control is created its init() method is called. When a control instance is destroyed the exit() lifecycle API method is called. For associations and aggregations of a control the lifecycle is defined clearly. The API docs clearly state the following (see sap.ui.base.ManagedObject):

Aggregations

"Managed aggregations can store one or more references to other ManagedObjects. They are a mean to control the lifecycle of the aggregated objects: one ManagedObject can be aggregated by at most one parent ManagedObject at any time. When a ManagedObject is destroyed, all aggregated objects are destroyed as well and the object itself is removed from its parent. That is, aggregations won't contain destroyed objects or null/undefined."

Associations

"Managed associations also form a relationship between objects, but they don't define a lifecycle for the associated objects. They even can 'break' in the sense that an associated object might have been destroyed already although it is still referenced in an association. For the same reason, the internal storage for associations are not direct object references but only the IDs of the associated target objects."

With this piece of information, the lifecycle of associations and aggregations is clear. In other words, if a control is destroyed, then

  • its aggregations are destroyed as well
  • its associations are not destroyed automatically

Using data binding features of SAPUI5/OpenUI5 allows developers to bind data from models to properties or aggregations of a given control. Keep in mind that associations are not bindable, see the API docs of sap.ui.base.ManagedObject for more information. Binding aggregations is very common in UI5, just think about the list items of an instance of sap.m.List, or the rows of an sap.ui.table.Table instance. To bind aggregations you can simply call the bindAggregation() API method of the corresponding control (inherited from sap.ui.base.ManagedObject, see sap.ui.base.ManagedObject.bindAggregation(...) for details). However, you could also call the generated APIs for a specific aggregation directly, i.e. bindItems(…) for the items aggregations of the sap.m.List. Here is a small code snippet to illustrate aggregation binding in UI5:

Using Aggregation Binding in UI5 (Live Demo: Code below or JavaScript equivalent)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Understanding templateShareable 1 | nabisoft</title>
        <script src="https://openui5.hana.ondemand.com/1.36.12/resources/sap-ui-core.js"
            id="sap-ui-bootstrap"
            data-sap-ui-theme="sap_bluecrystal"
            data-sap-ui-libs="sap.m"
            data-sap-ui-bindingSyntax="complex"
            data-sap-ui-compatVersion="edge"
            data-sap-ui-preload="async"></script>
 
        <!-- XMLView -->
        <script id="myXmlView" type="ui5/xmlview">
            <mvc:View
                xmlns="sap.m"
                xmlns:core="sap.ui.core"
                xmlns:mvc="sap.ui.core.mvc">

                <VBox items="{/employees}">
                    <items>
                        <Text text="{firstName} {lastName}" />
                    </items>
                </VBox>
               
            </mvc:View>
        </script>
 
        <script>
            sap.ui.getCore().attachInit(function () {
                "use strict";
                 
                sap.ui.define([
                  "sap/ui/model/json/JSONModel"
                ], function(JSONModel) {
                  "use strict";

                  var oModel = new JSONModel({ 
                    employees : [
                        {firstName:"John1", lastName : "Doe1", kids : [{firstName:"Michael1"}, {firstName:"Maria1"}] },
                        {firstName:"John2", lastName : "Doe2", kids : [{firstName:"Michael2"}, {firstName:"Maria2"}] },
                        {firstName:"John3", lastName : "Doe3", kids : [{firstName:"Michael3"}, {firstName:"Maria3"}] },
                        {firstName:"John4", lastName : "Doe4", kids : [{firstName:"Michael4"}, {firstName:"Maria4"}] },
                        {firstName:"John5", lastName : "Doe5", kids : [{firstName:"Michael5"}, {firstName:"Maria5"}] }
                    ]}
                  );
                  sap.ui.getCore().setModel(oModel);  

                  sap.ui.xmlview({
                      viewContent : jQuery("#myXmlView").html()
                  }).placeAt("content");

                }); 
            });
        </script>
 
    </head>
 
    <body class="sapUiBody">
        <div id="content"></div>
    </body>
</html>

The example above creates a simple sap.m.VBox with some data and it uses an sap.m.Text as the template. The template defines how each item in the VBox should look like. The question of the questions is the following: What happens to the template if our VBox instance is destroyed? Or in other words: How does the lifecycle of a control instance used as template for data binding look like? To illustrate this, let's add a simple button, which checks the state of the template inside its press event handler.

Checking the state of the template (Live Demo: Code below or JavaScript equivalent)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Understanding templateShareable 2 | nabisoft</title>
        <script src="https://openui5.hana.ondemand.com/1.36.12/resources/sap-ui-core.js"
            id="sap-ui-bootstrap"
            data-sap-ui-theme="sap_bluecrystal"
            data-sap-ui-libs="sap.m"
            data-sap-ui-bindingSyntax="complex"
            data-sap-ui-compatVersion="edge"
            data-sap-ui-preload="async"></script>
 
        <!-- XMLView -->
        <script id="myXmlView" type="ui5/xmlview">
            <mvc:View
                xmlns="sap.m"
                xmlns:core="sap.ui.core"
                xmlns:mvc="sap.ui.core.mvc">

                <VBox id="myVBox" items="{/employees}">
                    <items>
                        <Text text="{firstName} {lastName}" />
                    </items>
                </VBox>
               
            </mvc:View>
        </script>
 
        <script>
            sap.ui.getCore().attachInit(function () {
                "use strict";
                 
                sap.ui.define([
                  "sap/ui/model/json/JSONModel"
                ], function(JSONModel) {
                  "use strict";

                  var oModel = new JSONModel({ 
                    employees : [
                        {firstName:"John1", lastName : "Doe1", kids : [{firstName:"Michael1"}, {firstName:"Maria1"}] },
                        {firstName:"John2", lastName : "Doe2", kids : [{firstName:"Michael2"}, {firstName:"Maria2"}] },
                        {firstName:"John3", lastName : "Doe3", kids : [{firstName:"Michael3"}, {firstName:"Maria3"}] },
                        {firstName:"John4", lastName : "Doe4", kids : [{firstName:"Michael4"}, {firstName:"Maria4"}] },
                        {firstName:"John5", lastName : "Doe5", kids : [{firstName:"Michael5"}, {firstName:"Maria5"}] }
                    ]}
                  );
                  sap.ui.getCore().setModel(oModel);  

                  var oView = sap.ui.xmlview({
                      viewContent : jQuery("#myXmlView").html()
                  });
                  oView.placeAt("content");
                  
                  var oBtn = new sap.m.Button({
                      text : "check binding",
                      press : function () {
                          var oVBox, oBindingInfo, oTemplate;
                          
                          oVBox = oView.byId("myVBox");
                          oBindingInfo = oVBox.getBindingInfo("items");
                          console.log(oBindingInfo);	// note ==> templateShareable: 1
                          oTemplate = oBindingInfo.template;
                          console.log(oTemplate);
                          
                          oVBox.destroy();
                          //oVBox.placeAt("content");	//==> ERROR: After an element has been destroyed, it can no longer be used in the UI!
                          console.log("oVBox.bIsDestroyed = " + oVBox.bIsDestroyed);
                          
                          console.log("oTemplate.bIsDestroyed = " + oTemplate.bIsDestroyed);	// There is no better API for this, i.e. getter
                          oTemplate.unbindText();
                          oTemplate.setText("Hard coded text");
                          oTemplate.placeAt("content");		// does not cause an error, because it's not destroyed!
                          
                          //conclusion: we destroyed the VBox, but its template is not destroyed
                          
                      }
                  });
                  oBtn.placeAt("contentButton");

                }); 
            });
        </script>
 
    </head>
 
    <body class="sapUiBody">
        <div id="content"></div>
        <div id="contentButton"></div>
    </body>
</html>

When the button is pressed our event handler is called. In the handler we retrieve a reference to the VBox on the view in order to get the BindingInfo of its items aggregation. The BindingInfo is basically what we have defined in the XMLView for the binding of the items aggregation of our VBox. It's worth to note that the BindingInfo has a property templateShareable which is set to the value 1. So although we did not set this templateShareable it's there. Furthermore, the BindingInfo has a property template, which is the template that we have specified in the XMLView. In line 66 we finally do something that the lifecycle management of UI5 does in certain cases as well: we call oVBox.destroy() to destroy the VBox. After an element has been destroyed, it can no longer be used in the UI! That means calling oVBox.placeAt("content"); would cause an error.

We just destroyed the VBox, but how does that affect the template that we used for the VBox? Let's have a look at oTemplate to find out more. First of all, it seems that destroyed controls have a property bIsDestroyed with the boolean value true (unfortunately, there is no getter). In our case oTemplate.bIsDestroyed is not available, and this is the first hint for us that the template was not destroyed by the runtime. Next we want to prove that this assumption is true by setting the text property of the template (an instance of an sap.m.Text) and placing it on the view afterwards. As you can see it works perfectly, and there is not error at all! We have now verified that although we destroyed the VBox its template was not destroyed automatically by the runtime!

This fact is related to the templateShareable property that we found on the BindingInfo. In our case its value was 1 which stands for MAYBE_SHAREABLE_OR_NOT (see the code of sap.ui.base.ManagedObject on Github). Although we have not set the value the runtime made a guess. In case a template is marked as shareable (templateShareable=true) or as "Maybe Shareable Or Not" it will not be destroyed by the UI5 runtime. It also means that the developer has to take care of destroying the template(s) in order to release unneeded resources. However, this could lead to memory leaks in case developers forget to call destroy() manually on the templates. By the way – the default value for templateShareable is true (see sap.ui.base.ManagedObject.html.bindAggregation) and this is equl to the "old" behavior (before templateShareable was introduced). When setting templateShareable:false the "new" behavior if turned on, which means that the runtime will take care of destroying the template for us.

templateShareable was introduced after the UI5 team detected that there is actually no clear definition for the lifecycle of templates used for data binding. When calling bindAggregation(...) you always have to pass a template. But what happens to the old template if you are "re-binding"? In other words, what happens to the old template if the binding is updated by calling bindAggregation(...) again? The answer is simple: the template is not destroyed per default! The runtime can't just destroy() the old template because applications often keep a reference to the template in a private variable to reuse the template later. Therefore, the UI5 team has introduced templateShareable. The value false basically tells the runtime that the lifecycle of the parent control can be used for the template as well, i.e. if the parent is destroyed its template can be destroyed as well. In such cases the developer does not have to think of destroying the template. When templateShareable is set to true (default + old behavior) the runtime does not care about the lifecycle of the template and the developer has to handle it instead. However, many developers don't maintain templateShareable and it wasn't even available in older versions of UI5. In case it's not set the runtime tries to detect the most probable value for it by using some kind of a smart heuristic. If the runtime thinks that the template is MAYBE_SHAREABLE_OR_NOT during operations that imply shareable templates (i.e. when the BindingInfo is cloned) you will see the famous error message in your console:

A shared template must be marked with templateShareable:true in the binding info

templateShareable error message in Chrome's console
templateShareable error message in Chrome's console

This error message tells you that you should check you code and handle the lifecycle of the template. In fact, internally when you see this message you can be sure that you have a memory leak because internally templateShareable is set to true right after this message! Check sap.ui.base:ManagedObject on github in case you want a prove for this.

In apps it's not always obvious why you actually get the error messsage mentioned above. Have a look at the following code:

Causing the templateShareable error message (Live Demo: Code below or JavaScript equivalent)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Understanding templateShareable 3 | nabisoft</title>
        <script src="https://openui5.hana.ondemand.com/1.36.12/resources/sap-ui-core.js"
            id="sap-ui-bootstrap"
            data-sap-ui-theme="sap_bluecrystal"
            data-sap-ui-libs="sap.m"
            data-sap-ui-bindingSyntax="complex"
            data-sap-ui-compatVersion="edge"
            data-sap-ui-preload="async"></script>
 
        <!-- XMLView -->
        <script id="myXmlView" type="ui5/xmlview">
            <mvc:View
                xmlns="sap.m"
                xmlns:core="sap.ui.core"
                xmlns:mvc="sap.ui.core.mvc">

                <VBox items="{/employees}">
                    <items>
                        <HBox items="{kids}">
                            <items>
                                <Text text="{firstName}," />
                            </items>
                        </HBox>				
                    </items>
                </VBox>
               
            </mvc:View>
        </script>
 
        <script>
            sap.ui.getCore().attachInit(function () {
                "use strict";
                 
                sap.ui.define([
                  "sap/ui/model/json/JSONModel"
                ], function(JSONModel) {
                  "use strict";

                  var oModel = new JSONModel({ 
                    employees : [
                        {firstName:"John1", lastName : "Doe1", kids : [{firstName:"Michael1"}, {firstName:"Maria1"}] },
                        {firstName:"John2", lastName : "Doe2", kids : [{firstName:"Michael2"}, {firstName:"Maria2"}] },
                        {firstName:"John3", lastName : "Doe3", kids : [{firstName:"Michael3"}, {firstName:"Maria3"}] },
                        {firstName:"John4", lastName : "Doe4", kids : [{firstName:"Michael4"}, {firstName:"Maria4"}] },
                        {firstName:"John5", lastName : "Doe5", kids : [{firstName:"Michael5"}, {firstName:"Maria5"}] }
                    ]}
                  );
                  sap.ui.getCore().setModel(oModel);  

                  sap.ui.xmlview({
                      viewContent : jQuery("#myXmlView").html()
                  }).placeAt("content");

                }); 
            });
        </script>
 
    </head>
 
    <body class="sapUiBody">
        <div id="content"></div>
    </body>
</html>

As you can see we use VBox that uses an HBox as its template. The HBox itself uses a simple sap.m.Text as its template. This example will cause the templateShareable error message being printed to the console. Can you tell why we get the error message and how to get rid of it? Obviously, UI5 was not smart enough to guess that in this case we actually would prefer to have templateShareable=false. To fix the error message you need to set templateShareable to either false of true in the BindingInfo (see code below). In case you set it to true make sure you also handle the lifecycle of the template (I leave this task to you).

Fixing the templateShareable error message (Live Demo: Code below or JavaScript equivalent)
<mvc:View
    xmlns="sap.m"
    xmlns:core="sap.ui.core"
    xmlns:mvc="sap.ui.core.mvc">
    
    <VBox items="{/employees}">
        <items>
            <HBox items="{path:'kids', templateShareable:false}"> <!-- if using true make sure to destroy manually -->
                <items>
                    <Text text="{firstName}," />
                </items>
            </HBox>
        </items>
    </VBox>           

</mvc:View>

Conclusion

I hope this little explanation helps you to better understand what's behind templateShareable. Now you should know what to do when you see the error message on the console.

  • Start using templateShareable explicitly as a best practice, especially for complex bindings
  • Review your older apps and think about them to use templateShareable explicitly if possible
  • If you keep a reference to a template in a private variable for later reuse set templateShareable to true
  • If templateShareable is true make sure to manually handle the lifecycle of your template (i.e. destroy the template in controller's onExit)
  • If you see the templateShareable error message check your code and
    • set templateShareable explicitly
    • handle the lifecycle of the template manually if you set templateShareable=true
Comments
  • If you like this tutorial help us to maintain it:
  • And don't forget to let others know about it:
Great post!
posted by Ivan Niolaev
Tue Nov 20 11:13:20 UTC 2018
First off, thanks alot for this wonderful post! I found it accidentally and started reading, the moment i realise it wan not exactly the topic i was looking for - i was half way through and willing to finish reading any way. Great job, once again!

Tho i still have a questoin regarding those multi-layered aggregations and their templates. Be4 i read this post i thought that if i write this:

<VBox items="{/employees}">
    <items>
        <HBox items="{kids}">
            <items>
                <Text text="{firstName}" />
            </items>
        </HBox>
    </items>
</VBox>

Then sap xml parser will understand, that it should use the same control as a template for every instance of <HBox items="{kids}">. Turns out it didn't even bother to try to guess it. Then what is the best way to achieve this behavior, which seems optimal to me, atleast way better than destroying and recreating identical templates on every aggregation update.

BR, Ivan.
Thanks and a question.
posted by Onur
Thu Sep 27 07:17:08 UTC 2018
Good job! Does anyone know how to apply this hotfix when objects are created as js?
( for example: oSelect.bindAggregation("items", "/ModelPath", oItem); )
Great Blog! Deep and accurate information.
posted by Vivek
Tue Sep 18 14:12:41 UTC 2018
Very nicely written post. Thanks for research and posting.
Template Shareable Issue
posted by Sushant Ranade
Fri Aug 18 11:33:29 UTC 2017
Hi Nabi,

Thanks a lot for this informative blog. It proved really helpful for understanding the templateShareable issue. Hope to see more blogs from you in the future.

Your Avid Reader,
Sushant
RE: Same issue different error in the console
posted by Nabi
Mon Aug 01 13:45:06 UTC 2016
Hi George,

The error messages mentioned above in this tutorial are based on SAPUI5 1.36.12. If you run the examples from above with UI5 1.38.5 you will see exactly the same error messages you are getting in your own apps ("During a clone operation,..."). This basically means the messages are much more meaningful with your version of UI5. In other words, the reason is still the same, see the tutorial. I suggest our clients to set templateShareable explicitely because I don't always trust the auto detection for templateShareable... However, today SAP cannot just set "templateShareable=false" as the default because this would break especially older apps that implicitely use "templateShareable=true" but have never explicitely set templateShareable to true. Maybe in future there will be the default value (false) we all want for our new apps, or maybe there will be some config option (https://openui5.hana.ondemand.com/#docs/guide/91f2d03b6f4d1014b6dd926db0e91070.html) to set the default explicitely.

Best, Nabi
Same issue different error in the console
posted by George Modrogan
Mon Aug 01 12:17:15 UTC 2016
Hi Nabi,

Great post by the way and useful.

I noticed a message error today. I have a table, and inside the table row template I use a ColumnMicroChart with a template for Columns "ColumnMicroChartData"
This is the message that I get:
"During a clone operation, a template was found that neither was marked with 'templateShareable:true' nor 'templateShareable:false'. The framework won't destroy the template. This could cause errors (e.g. duplicate IDs) or memory leaks (The template is used in aggregation 'columns' of object '__chart0').For more information, see documentation under 'Aggregation Binding'. -  "

I looked over the Aggregation Binding documentation and it's pretty interesting to notice this statement: "Caution
This is very error-prone, therefore we don't recommend to leave the parameter undefined! Always set the parameter explicitly to true or false."

I set "templateShareable=false" and works great, the error is gone. 

Do you recommend to have "templateShareable=false" for all the bindings in the application? Or the framework knows to set this on "false" in simple binding scenarios? It's annoying a bit to maintain this new property for all the binding. This should be the default value

Cheers,
George