title: How to prevent SAPUI5 lifecycles from running twice?
body: <p>I have a custom control that contains a scatter plot chart. When a button is pressed, it opens a modal/dialog control that displays the chart (see image below). The only problem is that the chart is displayed twice! Upon opening the dev tools, it appears that all of the lifecycles are rendered twice. I have no idea why this happens but I need it to <strong>render only once</strong>. </p>
  
  <p>Here is my main controller:</p>
  
  <p><strong>main.controller.js</strong></p>
  
  <pre><code>jQuery.sap.registerModulePath("vizConcept.ScatterPlot", "controls/ScatterPlot");
  jQuery.sap.require("vizConcept.ScatterPlot");
  jQuery.sap.registerModulePath("vizConcept.ScatterPlotItem", "controls/ScatterPlot");
  jQuery.sap.require("vizConcept.ScatterPlotItem");
  
  
  sap.ui.define([
          'jquery.sap.global',
          'vizConcept/controller/BaseController',
          'sap/ui/model/json/JSONModel',
          'vizConcept/model/viewControls',
          'sap/m/Button',
          'sap/m/Dialog',
      ],
  
      function (jQuery, BaseController, JSONModel, viewControls, Button, Dialog) {
  
          "use strict";
  
          var controls;
  
          var mainController = BaseController.extend("vizConcept.controller.Main", {
  
              onInit: function(oEvent) {
  
                  // Access/expose the defined model(s) configured in the Component.js or Manifest.json within the controller.
                  this.getView().setModel(this.getOwnerComponent().getModel("products"), "products");
                  var oModel = this.getView().getModel("products");
                  this.getView().setModel(oModel);
                  //
                  console.log('onInit');
                  var sUrl = "#" + this.getOwnerComponent().getRouter().getURL("page2");
  
          //IIFE renders our chart, will need to move this in a modular way        
              }, 
  
              onAfterRendering: function () {
                  this._rebindAll();
              },
  
          _rebindAll : function() {
  
              var oModel = new sap.ui.model.json.JSONModel({
                "buckets": [
          {
              "quarter": "Q2 2013",
              "values": [
                   {  "name":     "Segment A",
                      "value":    "3228",
                      "value2":   "12"    
                   },
                   {
                      "name": "Segment B",
                      "value":    "11752",
                      "value2":   "37"
                   },
                   {
                      "name": "Segment C",
                      "value":    "492",
                      "value2":   "3"
                   },
                   {
                      "name": "Segment D",
                      "value":    "654",
                      "value2":   "6"
                   },
                   {
                      "name": "Segment E",
                      "value":    "39165",
                      "value2":   "167"
                   },
                   {
                      "name": "Segment F",
                      "value":    "4745",
                      "value2":   "9"
                   }
              ]   
          },
          {
              "quarter":  "Q3 2013",
              "values":   [
                  {   "name":     "Segment A",
                      "value":    "6806",
                      "value2":   "24"
                   },
                   {
                      "name": "Segment B",
                      "value":    "11372",
                      "value2":   "51"
                   },
                   {
                      "name": "Segment C",
                      "value":    "2306",
                      "value2":   "10"
                   },
                   {
                      "name": "Segment D",
                      "value":    "1492",
                      "value2":   "7"
                   },
                   {
                      "name": "Segment E",
                      "value":    "33944",
                      "value2":   "170"
  
                   },
                   {
                      "name": "Segment F",
                      "value":    "6498",
                      "value2":   "16"
                   }
              ]
          },
          {
              "quarter":  "Q4 2013",
              "values":   [
                  {   "name":     "Segment A",
                      "value":    "11228",
                      "value2":   "23"    
                   },
                   {
                      "name": "Segment B",
                      "value":    "31324",
                      "value2":   "83"
                   },
                   {
                      "name": "Segment C",
                      "value":    "1291",
                      "value2":   "17"
                   },
                   {
                      "name": "Segment D",
                      "value":    "4350",
                      "value2":   "17"
                   },
                   {
                      "name": "Segment E",
                      "value":    "52265",
                      "value2":   "155"
                   },
                   {
                      "name": "Segment F",
                      "value":    "23785",
                      "value2":   "66"
                   }
              ]
          },
          {
              "quarter":  "Q1 2014",
              "values":   [
                  {   "name":     "Segment A",
                      "value":    "3708",
                      "value2":   "18"
                   },
                   {
                      "name": "Segment B",
                      "value":    "20176",
                      "value2":   "74"
                   },
                   {
                      "name": "Segment C",
                      "value":    "5169",
                      "value2":   "61"
                   },
                   {
                      "name": "Segment D",
                      "value":    "31322",
                      "value2":   "76"
                   },
                   {
                      "name": "Segment E",
                      "value":    "49069",
                      "value2":   "191"
                   },
                   {
                      "name": "Segment F",
                      "value":    "8928",
                      "value2":   "19"
                   }
              ]
          },
          {
              "quarter":  "Q2 2014",
              "values":   [
                  {   "name":     "Segment A",
                      "value":    "2950",
                      "value2":   "26"    
                   },
                   {
                      "name": "Segment B",
                      "value":    "6807",
                      "value2":   "54"
                   },
                   {
                      "name": "Segment C",
                      "value":    "3789",
                      "value2":   "110"
                   },
                   {
                      "name": "Segment D",
                      "value":    "12867",
                      "value2":   "91"
                   },
                   {
                      "name": "Segment E",
                      "value":    "21411",
                      "value2":   "128"
                   },
                   {
                      "name": "Segment F",
                      "value":    "18478",
                      "value2":   "21"
                   }
              ]
          }
  
      ]
              });
  
              var oScatterPlotHolder = this.byId("RegionScatterPlotHolder");
              var oScatterPlotItem = new vizConcept.ScatterPlotItem({quarter:"{quarter}", values:"{values}"});
              /* new  chart */
              var oScatterPlot = new vizConcept.ScatterPlot({
                  items: {path : "/buckets", template : oScatterPlotItem}
              });
  
              //var oModel = sap.ui.getCore().getModel("growth-regions-scatter");
  
              oScatterPlot.setModel(oModel);
              oScatterPlotHolder.addItem(oScatterPlot);
  
              $(function() {
                var dlg = new sap.m.Dialog({
                  id: 'vizModal',    
                  title: 'Scatter Plot Example Viz',
                  width : "1800px",  
                  height : "600px",  
                  content : [oScatterPlotHolder],
                  beginButton: new Button({
                                      text: 'Close',
                                      press: function () {
                                          dlg.close();
                                      }
                                  })  
                });
  
                (new sap.m.Button({
                      text: 'open',
                      type: 'Accept',
                      press: function() {
                        dlg.open();
                        oScatterPlotHolder.invalidate();
                      }
                    })).placeAt('content');
                  });
  
          },    
  
          onToPage2 : function () {
              this.getOwnerComponent().getRouter().navTo("page2");
          },
  
  
          });
  
          return mainController;
      });
  </code></pre>
  
  <p><strong>ScatterPlot.js (custom control)</strong></p>
  
  <pre><code>jQuery.sap.require("sap/ui/thirdparty/d3");
  jQuery.sap.declare("vizConcept.ScatterPlot");
  
  sap.ui.core.Element.extend("vizConcept.ScatterPlotItem", { metadata : {
      properties : {
          "quarter" : {type : "string", group : "Misc", defaultValue : null},
          "values" : {type : "object", group : "Misc", defaultValue : null}   
      }
  }});    
  sap.ui.core.Control.extend("vizConcept.ScatterPlot", {
      metadata : {
          properties: {
              "title": {type : "string", group : "Misc", defaultValue : "ScatterPlot Title"}
          },
          aggregations : {
              "items" : { type: "vizConcept.ScatterPlotItem", multiple : true, singularName : "item"}
          },
          defaultAggregation : "items",
          events: {
              "onPress" : {},
              "onChange":{}       
          }           
      },
  
  
      init : function() {
          console.log("vizConcept.ScatterPlot.init()");
          this.sParentId = "";
      },
  
  
      createScatterPlot : function() {
          /*
           * Called from renderer
           */
          console.log("vizConcept.ScatterPlot.createScatterPlot()");
          var oScatterPlotLayout = new sap.m.VBox({alignItems:sap.m.FlexAlignItems.Center,justifyContent:sap.m.FlexJustifyContent.Center});
          var oScatterPlotFlexBox = new sap.m.FlexBox({height:"auto",alignItems:sap.m.FlexAlignItems.Center});
          /* ATTENTION: Important
           * This is where the magic happens: we need a handle for our SVG to attach to. We can get this using .getIdForLabel()
           * Check this in the 'Elements' section of the Chrome Devtools: 
           * By creating the layout and the Flexbox, we create elements specific for this control, and SAPUI5 takes care of 
           * ID naming. With this ID, we can append an SVG tag inside the FlexBox
           */
          this.sParentId=oScatterPlotFlexBox.getIdForLabel();
          oScatterPlotLayout.addItem(oScatterPlotFlexBox);
  
          return oScatterPlotLayout;
  
      },
  
  
      /**
       * The renderer render calls all the functions which are necessary to create the control,
       * then it call the renderer of the vertical layout 
       * @param oRm {RenderManager}
       * @param oControl {Control}
       */
      renderer : function(oRm, oControl) {
          var layout = oControl.createScatterPlot();
  
          oRm.write("&lt;div");
          oRm.writeControlData(layout); // writes the Control ID and enables event handling - important!
          oRm.writeClasses(); // there is no class to write, but this enables 
          // support for ColorBoxContainer.addStyleClass(...)
  
          oRm.write("&gt;");
          oRm.renderControl(layout);
          oRm.addClass('verticalAlignment');
  
          oRm.write("&lt;/div&gt;");
  
      },
  
      onAfterRendering: function(){
          console.log("vizConcept.ScatterPlot.onAfterRendering()");
          //console.log(this.sParentId);
          var cItems = this.getItems();
          var data = [];
          for (var i=0;i&lt;cItems.length;i++){
              var oEntry = {};
              for (var j in cItems[i].mProperties) {
                  oEntry[j]=cItems[i].mProperties[j];
              }   
              data.push(oEntry);
          }
          //console.log("Data:");
          //console.log(data);
  
          /*
           * ATTENTION: See .createScatterPlot()
           * Here we're picking up a handle to the "parent" FlexBox with the ID we got in .createScatterPlot()
           * Now simply .append SVG elements as desired
           * EVERYTHING BELOW THIS IS PURE D3.js
           */
  
          var margin = {
                  top: 15,
                  right: 15,
                  bottom: 30,
                  left: 40
              },
              width = 600 - margin.left - margin.right,
                  height = 300 - margin.top - margin.bottom;
  
              // Our X scale
              var x = d3.scale.linear()
                  .range([0, width-80]);
  
              // Our Y scale
              var y = d3.scale.linear()
                  .range([height, 0]);
  
              // Our color bands
              var color = d3.scale.ordinal()
                  .range(["#004460", "#0070A0", "#008BC6", "#009DE0", "#45B5E5", "8CCDE9", "#DAEBF2"]);    //"#00A6ED",   
  
              // Use our X scale to set a bottom axis
              var xAxis = d3.svg.axis()
                  .scale(x)
                  .orient("bottom");
  
              // Smae for our left axis
              var yAxis = d3.svg.axis()
                  .scale(y)
                  .orient("left")
                  .tickFormat(d3.format(".2s"));
  
              var tip = d3.select("body").append("div")
                  .attr("class", "sctooltip")
                  .style("position", "absolute")
                  .style("text-align", "center")
                  .style("width", "80px")
                  .style("height", "42px")
                  .style("padding", "2px")
                  .style("font", "11px sans-serif")
                  .style("background", "#F0F0FF")
                  .style("border", "0px")
                  .style("border-radius", "8px")
                  .style("pointer-events", "none")
                  .style("opacity", 0);
  
          var vis = d3.select("#" + this.sParentId);
          var svg = vis.append("svg")
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)
          .style("background-color","white")
          .style("font", "12px sans-serif")
          .append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
          x.domain([0, d3.max(data, function (d) {
  
              var max = d3.max(d.values, function (dd){
                  return(+dd.value);
              })
               return max;
          })]);
  
          // Our Y domain is from zero to our highest total
          y.domain([0, d3.max(data, function (d) {
  
              var max = d3.max(d.values, function (dd){
                  return(+dd.value2);
              })
              return max;
          })]);
  
          var totalval = 0;
          var totalval2 = 0;
          data.forEach(function (d) {
              var quarter = d.quarter;
              d.values.forEach(function (dd){
                  dd.quarter = quarter;
                  totalval += +dd.value;
                  totalval2 += +dd.value2;
              });
  
          });
  
          var average = totalval/totalval2;
          var line_data = [{"x": 0, "y": 0},{"x": (y.domain()[1]*average), "y": y.domain()[1]}];
  
          var avgline = d3.svg.line()
              .x(function(d){ return x(d.x); })
              .y(function(d){ return y(d.y); })
              .interpolate("linear");
  
  
          svg.append("g")
              .attr("class", "x axis")
              .style("fill", "none")
              .style("stroke", "grey")
              .style("shape-rendering", "crispEdges")
              .attr("transform", "translate(0," + height + ")")
              .call(xAxis);
  
          svg.append("g")
              .attr("class", "y-axis")
              .style("fill", "none")
              .style("stroke", "grey")
              .style("shape-rendering", "crispEdges")
              .call(yAxis);
  
          //average line
          svg.append("path")
              .attr("class", "avgline")
              .style("stroke", "#000")
              .style("stroke-width", "1px")
              .style("stroke-dasharray", ("4, 4"))
              .attr("d", avgline(line_data));
  
          /*
              .append("text")
              .attr("transform", "rotate(-90)")
              .attr("y", 6)
              .attr("dy", ".71em")
              .style("text-anchor", "end")
              .text("XXXXX");
          */
          var plot = svg.selectAll(".quarter")
              .data(data)
              .enter().append("g");
  
          plot.selectAll("dot")
              .data(function (d) {
              return d.values;
          })
              .enter().append("circle")
              .attr("class", "dot")
              .attr("r", 5)
              .attr("cx", function (d){
                  return x(d.value);
              })
              .attr("cy", function (d) {
              return y(d.value2);
          })
              .style("stroke", "#004460")
              .style("fill", function (d) {
              return color(d.name);
          })
              .style("opacity", .9)
              .style("visibility", function(d){
                  if(+d.value != 0){
                      return "visible";
                  }else{
                      return "hidden";
                  }
              })
              .style("pointer-events", "visible")
              .on("mouseover", function(d){
                      tip.transition()
                          .duration(200)
                          .style("opacity", .8);
                      tip.html(d.name + "&lt;br/&gt;" + d.quarter + "&lt;br /&gt;" + "Avg. " +(+d.value/+d.value2).toFixed(2))
                          .style("left", (d3.event.pageX-40) + "px")
                          .style("top", (d3.event.pageY-50) + "px");
  
                  })
                  .on("mouseout", function(d){
                      tip.transition()
                          .duration(500)
                          .style("opacity", 0);
                  });;
  
          var legend = svg.selectAll(".legend")
          .data(color.domain())
          .enter().append("g")
          .attr("class", "legend")
          .attr("transform", function (d, i) {
          return "translate(0," + i * 16 + ")";
          });
  
          legend.append("rect")
          .attr("x", width - 12)
          .attr("width", 12)
          .attr("height", 12)
          .style("fill", color);
  
          legend.append("text")
          .attr("x", width - 24)
          .attr("y", 6)
          .attr("dy", ".35em")
          .style("text-anchor", "end")
          .style("font", "11px sans-serif")
          .text(function (d) {
          return d;
          }); 
  
          var avglabel = svg.append("g")
              .attr("transform", "translate(" + (width-40) + ",140)");
          avglabel.append("text")
              .style("text-anchor", "middle")
              .text("Average: " + average.toFixed(2));
  
      }
  });
  </code></pre>
  
  <p><a href="https://i.stack.imgur.com/1Vc7H.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/1Vc7H.png" alt=""></a>
  Here is an example of what is the outcome of the double rendering</p>
  
  <p><strong>Edit 1 (Here is my element dom tree)</strong>
  <a href="https://i.stack.imgur.com/oBlCJ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/oBlCJ.png" alt="enter image description here"></a></p>