title: sapui5/openui5 TreeTable with custom control column throwing duplicate id error when expanding a node
body: <p>I have a <code>sap.ui.table.TreeTable</code> with several columns, one of which contains a custom control defined as a template for the column. </p>
  
  <p>The TreeTable renders correctly, and the column with the custom control also renders and functions correctly ... that is until I expand an unexpanded node.</p>
  
  <p>When I expand a node the Renderer throws an error<br>
  <code>Error: adding element with duplicate id '__link0-col1-row0'</code>,<br>
  which is the column associated with the custom control. </p>
  
  <p>I can workaround the problem by adding <strong>attachToggleOpenState</strong> event handler which destroys this columns template and adds it again. Expensive but it works!</p>
  
  <pre class="lang-js prettyprint-override"><code>this.oTable = new sap.ui.table.TreeTable("myTable", {
      columns : [ new sap.ui.table.Column( {
          label : "Query",
          template : new sparqlish.control.queryClause({
              clausePath : {
                  path : "viewModel&gt;path"
              }
          }),
          visible : true,
          width : "500px"
      }), ],
  }).attachToggleOpenState(function(oEvent) {
      // TODO workaround as expanding Tree causes duplicate id
      var queryColumn = oEvent.getSource().getColumns()[0];
      queryColumn.destroyTemplate();
      queryColumn.setTemplate(new sparqlish.control.queryClause({
          clausePath : {
              path : "viewModel&gt;path"
          }
      }))
  });
  </code></pre>
  
  <p>sparqlish.control.queryClause is the custom control.</p>
  
  <p>I am using openui5 version 1.28.15</p>
  
  <p>Any other, less expensive, suggestions?</p>
  
  <p>The custom control takes this form, however this control invokes other controls and so on.</p>
  
  <pre class="lang-js prettyprint-override"><code>jQuery.sap.require("sparqlish.control.conceptClause");
  jQuery.sap.require("sparqlish.control.propertyClause");
  jQuery.sap.require("sparqlish.control.conjunctionPropertyClause");
  sap.ui.core.Control.extend("sparqlish.control.queryClause", {
      metadata : {
          properties : {
              clausePath : {
                  type : "string"
              }
          },
          events : {},
          aggregations : {
              _conceptClause : {
                  type : "sparqlish.control.conceptClause",
                  multiple : false
              },
              _propertyClause : {
                  type : "sparqlish.control.propertyClause",
                  multiple : false
              },
              _conjunctionPropertyClause : {
                  type : "sparqlish.control.conjunctionPropertyClause",
                  multiple : false
              }
          }
      },
      init : function() {
          var self = this;
          self.setAggregation("_conceptClause", new sparqlish.control.conceptClause());
          self.setAggregation("_propertyClause", new sparqlish.control.propertyClause());
          self.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause());
      },
      renderer : function(oRm, oControl) {
          if (oControl.getClausePath() != undefined) {
              // TODO no point if path not yet defined
              oRm.write("&lt;div ");
              oRm.writeControlData(oControl);
              oRm.writeClasses();
              oRm.write("&gt;");
              var currentModel = oControl.getModel("queryModel");
              // Set binding context, rather than just the binding path etc, as this seems essential for satisfactory binding of
              // aggregations
              oControl.setBindingContext(new sap.ui.model.Context(oQueryModel, oControl.getClausePath()), "queryModel")
              var currentCtx = oControl.getBindingContext("queryModel");
              var currentContext = oControl.getModel("queryModel").getProperty("", currentCtx);
              if (currentContext != undefined) {
                  var sClass = currentContext._class;
                  if (sClass == "Query") {
                      oControl.setAggregation("_conceptClause", new sparqlish.control.conceptClause().setBindingContext(oControl.getBindingContext("queryModel")));
                      oRm.renderControl(oControl.getAggregation("_conceptClause"));
                  } else if (sClass == "Clause") {
                      oControl.setAggregation("_propertyClause", new sparqlish.control.propertyClause().setBindingContext(oControl.getBindingContext("queryModel")));
                      oRm.renderControl(oControl.getAggregation("_propertyClause"));
                  } else if (sClass == "ConjunctionClause") {
                      oControl.setAggregation("_conjunctionPropertyClause", new sparqlish.control.conjunctionPropertyClause().setBindingContext(oControl
                              .getBindingContext("queryModel")));
                      oRm.renderControl(oControl.getAggregation("_conjunctionPropertyClause"));
                  } else {
                      jQuery.sap.log.fatal("Incorrect class of provided query clause");
                  }
              }
              oRm.write("&lt;/div&gt;");
          } else {
              jQuery.sap.log.fatal("clausePath not defined");
          }
      }
  });
  </code></pre>
  
  <p>An example of one of the dependent controls is below. </p>
  
  <pre class="lang-js prettyprint-override"><code>jQuery.sap.require("sparqlish.control.conceptMenu");
  jQuery.sap.require("sparqlish.control.conceptFilters");
  jQuery.sap.require("sparqlish.control.addClause");
  sap.ui.core.Control.extend("sparqlish.control.conceptClause", {
      metadata : {
          properties : {
              concept : "object"
          },
          events : {},
          aggregations : {
              _concept : {
                  type : "sparqlish.control.conceptMenu",
                  multiple : false
              },
              _conceptFilters : {
                  type : "sparqlish.control.conceptFilters",
                  multiple : false
              },
              _addClause : {
                  type : "sparqlish.control.addClause",
                  multiple : false
              }
          }
      },
      init : function() {
          var self = this;
          var conceptSelect =function(oEvent){
  
                  var currentModel = self.getModel("queryModel");
                  var currentContext = self.getBindingContext("queryModel");
                  var currentModelData = currentModel.getProperty("", currentContext);
                  currentModelData.conceptFilters = [];
                  currentModelData.clauses = {};
                  var sConcept = oEvent.getParameter("concept");
                  var oMetaModel = self.getModel("metaModel");
                  var oConcept=oMetaModel.getODataEntitySet(sConcept);
                  self.setConcept( oConcept);
                  self.oEntityTypeModel = new sap.ui.model.json.JSONModel();
                  self.oEntityTypeModel.setData(oMetaModel.getODataEntityType(oConcept.entityType));
                  self.setModel(self.oEntityTypeModel, "entityTypeModel");
                  self.getAggregation("_conceptFilters").setModel(self.oEntityTypeModel, "entityTypeModel");
  
                  self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
                  currentModel.refresh();
                  self.rerender();    
          };
          self.setAggregation("_concept", new sparqlish.control.conceptMenu({
              selected : function(oEvent) {
                  self.getAggregation("_conceptFilters").getAggregation("_extendFilter").setVisible(true);
              },
              changed : conceptSelect
          }).bindElement("queryModel&gt;"));
          self.setAggregation("_conceptFilters", new sparqlish.control.conceptFilters().bindElement("queryModel&gt;"));
          self.setAggregation("_addClause", new sparqlish.control.addClause({
              pressed : function(oEvent) {
                  alert("concept");
              }
          }).bindElement("queryModel&gt;"));
      },
  
      renderer : function(oRm, oControl) {
          oRm.addClass("conceptClause");
          oRm.write("&lt;div ");
          oRm.writeControlData(oControl);
          oRm.writeClasses();
          oRm.write("&gt;");
          oRm.write(sap.ui.getCore().getModel("i18nModel").getProperty("conceptClauseFind"));
          oRm.renderControl(oControl.getAggregation("_concept"));
          oRm.renderControl(oControl.getAggregation("_conceptFilters"));
          oRm.write("&amp;nbsp;");
          oRm.renderControl(oControl.getAggregation("_addClause"));
          oRm.write("&lt;/div&gt;");
      }
  });
  </code></pre>