title: Data binding error with a custom d3 control in SAP UI5
body: <p>I'm working with a custom d3 control in SAP UI5. I found a template on how to do that here: <a href="http://scn.sap.com/community/developer-center/front-end/blog/2014/07/17/custom-sapui5-visualization-controls-with-d3js" rel="nofollow">http://scn.sap.com/community/developer-center/front-end/blog/2014/07/17/custom-sapui5-visualization-controls-with-d3js</a></p>
  
  <p>So now I'm just trying to make this work. My main problem is that my control doesn't consume the data from the model. Here is the Errors:</p>
  
  <ul>
  <li>Error: Invalid value for  attribute x="NaN"</li>
  <li>Error: Invalid negative value for  attribute height="-12"</li>
  <li>Error: Invalid value for  attribute x="NaN"</li>
  </ul>
  
  <p>I decided that this is the problem with an asynchronous request, so I added this: <code>gModel.loadData("json/performance-comparison.json", null, false);</code> but this is stil not working.</p>
  
  <p>Thanks for any ideas.</p>
  
  <p>Here is the code: </p>
  
  <p><strong>HorizontalBarChart.js</strong></p>
  
  <pre><code>  jQuery.sap.require("sap/ui/thirdparty/d3");
  jQuery.sap.declare("control.HorizontalBarChart");
  
  sap.ui.core.Element.extend("control.HorizontalBarChartItem", { metadata : {
      properties : {
          "region" : {type : "string", group : "Misc", defaultValue : null},
          "budget" : {type : "string", group : "Misc", defaultValue : null},
          "bw" : {type : "string", group : "Misc", defaultValue : null},
          "forecast" : {type : "string", group : "Misc", defaultValue : null} 
      }
  }});    
  sap.ui.core.Control.extend("control.HorizontalBarChart", {
      metadata : {
          properties: {
              "title": {type : "string", group : "Misc", defaultValue : "PerformanceToTargetComparison Chart Title"}
          },
          aggregations : {
              "items" : { type: "control.HorizontalBarChartItem", multiple : false, singularName : "item"}
          }
          ,
          defaultAggregation : "items",
          events: {
              "onPress" : {},
              "onChange":{}       
          }           
      },
  
  
      init : function() {
          console.log("sap.jaysdk.PerformanceToTargetComparison.init()");
          this.sParentId = "";
      },
  
  
      createComparison : function() {
          /*
           * Called from renderer
           */
          //console.log("Creating Chart");
          /*
          var chartLayout = new sap.ui.commons.Panel();
          this.sParentId = chartLayout.getIdForLabel();
          console.log(this.sParentId);
  
          return chartLayout;
          */
  
          var oChartLayout = new sap.m.VBox({alignItems:sap.m.FlexAlignItems.Center,justifyContent:sap.m.FlexJustifyContent.Center});
          var oChartFlexBox = new sap.m.FlexBox({height:"180px",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=oChartFlexBox.getIdForLabel();
          oChartLayout.addItem(oChartFlexBox);
  
          return oChartLayout;
  
      },
  
  
      /**
       * 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.createComparison();
  
          //layout.addStyleClass('pointer');
  
          // instead of "this" in the renderer function
          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("sap.jaysdk.PerformanceToTargetComparison.onAfterRendering()");
          var cItems = this.getItems();
          //console.log(cItems);
          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 .createComparison()
           * Here we're picking up a handle to the "parent" FlexBox with the ID we got in .createComparison()
           * Now simply .append SVG elements as desired
           * EVERYTHING BELOW THIS IS PURE D3.js
           */
  
          var vis = d3.select("#" + this.sParentId);
  
          var colors = {"pos_bw": "green",
                    "pos_forecast": "darkgreen",
                    "neg_bw": "darkorange",
                    "neg_forecast": "red"};
  
          var numElements = data.length;
          for (var i=0; i&lt;data.length;i++){
              data[i].bw = data[i].bw/data[i].budget;
              data[i].forecast = data[i].forecast/data[i].budget;
              data[i].budget = 1;
          }
          console.log("data: ");
          console.log(data);
  
          var maxval = d3.max(data, function(d){
              return Math.max(d.budget, d.bw, d.forecast);});
          if(maxval&lt;1.6){
              maxval=1.6;
          }
  
          var minval = d3.min(data, function(d){
              return Math.min(d.budget, d.bw, d.forecast);});
  
          var width = 600;
          var offset = 75;
  
  
          var svg = vis.append("svg").style("background-color","white").attr("width", width).attr("height", numElements*30);
          var xScale = d3.scale.linear().domain([minval, maxval]).range([0, width-(offset+5)]);
          //console.log(minval + " - 0 - " + maxval)
          //console.log(xScale(minval) + " - " + xScale(1) + " - " + xScale(maxval));
  
          var chart;
          // 200% line
          if(maxval&gt;2){
              chart= svg.append("g").attr("transform", "translate(" + offset + ",10)");
              chart.append("rect")
                  .attr("x", xScale(2))
                  .attr("Y", 0)
                  .attr("width", 1)
                  .attr("height", (numElements-1)*25 + 13)
                  .style("fill", "darkgrey");
              chart.append("text").text("200%")
                  .attr("text-anchor", "middle")
                  .attr("x", xScale(2))
                  .attr("y", numElements*25+4)
                  .attr("font-family", "sans-serif")
                  .attr("font-size", "12px")
                  .attr("fill", "darkgrey");
          }
  
          // 300% line
          if(maxval&gt;3.1){
              chart= svg.append("g").attr("transform", "translate(" + offset + ",10)");
              chart.append("rect")
                  .attr("x", xScale(3))
                  .attr("Y", 0)
                  .attr("width", 1)
                  .attr("height", (numElements-1)*25 + 13)
                  .style("fill", "darkgrey");
              chart.append("text").text("300%")
                  .attr("text-anchor", "middle")
                  .attr("x", xScale(3))
                  .attr("y", numElements*25+4)
                  .attr("font-family", "sans-serif")
                  .attr("font-size", "12px")
                  .attr("fill", "darkgrey");
          }
  
          // 150% line
          if(maxval&gt;1.5 &amp;&amp; maxval&lt;3.1){
              chart= svg.append("g").attr("transform", "translate(" + offset + ",10)");
              chart.append("rect")
                  .attr("x", xScale(1.5))
                  .attr("Y", 0)
                  .attr("width", 1)
                  .attr("height", (numElements-1)*25 + 13)
                  .style("fill", "darkgrey");
              chart.append("text").text("150%")
                  .attr("text-anchor", "middle")
                  .attr("x", xScale(1.5))
                  .attr("y", numElements*25+4)
                  .attr("font-family", "sans-serif")
                  .attr("font-size", "12px")
                  .attr("fill", "darkgrey");
          }
  
          // 50% line
          if(minval&lt;0.5){
              chart= svg.append("g").attr("transform", "translate(" + offset + ",10)");
              chart.append("rect")
                  .attr("x", xScale(0.5))
                  .attr("Y", 0)
                  .attr("width", 1)
                  .attr("height", (numElements-1)*25 + 13)
                  .style("fill", "darkgrey");
              chart.append("text").text("50%")
                  .attr("text-anchor", "middle")
                  .attr("x", xScale(0.5))
                  .attr("y", numElements*25+4)
                  .attr("font-family", "sans-serif")
                  .attr("font-size", "12px")
                  .attr("fill", "darkgrey");
          }
  
          // Booked/Won Percentage
          chart = svg.append("g").attr("transform", "translate(" + offset + ",10)");
          chart.selectAll(".pttc_bw")
              .data(data)
              .enter()
              .append("rect")
              .attr("class", "pttc_bw")
              .attr("x", function(d) {
                  if(d.bw&lt;1){
                      return xScale(d.bw);
                  }else{
                      return xScale(1);
                  }
              })
              .attr("y", function(d, i){ return 25*i+2;})
              .attr("height", 4)
              .attr("width", function(d){ 
                  if(d.bw&lt;1){
                      return xScale(1) - xScale(d.bw);
                  }else{
                      return xScale(d.bw)-xScale(1);
                  }
              })
              .style("fill", function(d){ if(d.bw&lt;1){ return colors.neg_bw;} else {return colors.pos_bw;}});
  
          // Forecast Percentage
          chart = svg.append("g").attr("transform", "translate(" + offset + ",10)");
          chart.selectAll(".pttc_forecast")
          .data(data)
          .enter().append("rect")
          .attr("class", "pttc_forecast")
          .attr("x", function(d) {
              if(d.forecast&lt;1){
                  return xScale(d.forecast);
              }else{
                  return xScale(1);
              }
          })
          .attr("y", function(d, i){ return 25*i+7;})
          .attr("height", 4)
          .attr("width", function(d){ 
              if(d.forecast&lt;1){
                  return xScale(1) - xScale(d.forecast);
              }else{
                  return xScale(d.forecast) - xScale(1);
              }
          })
          .style("fill", function(d){ if(d.forecast&lt;1){ return colors.neg_forecast;} else {return colors.pos_forecast;}});
  
          // Labels
          chart = svg.selectAll(".labels")
          chart.data(data)
              .enter().append("text")
              .attr("class", "labels")
              .attr("text-anchor", "end")
              .attr("x", offset-6)
              .attr("y", function(d, i){ return 25*i + 22})
              .text(function(d){ return d.region;})
              .attr("font-family", "sans-serif")
              .attr("font-size", "16px")
              .attr("font-weight", "bold")
              .attr("fill", "darkgrey");
  
          // 100% line
          chart = svg.append("g").attr("transform", "translate(" + offset + ",10)");
          chart.append("rect")
              .attr("x", xScale(1))
              .attr("Y", 0)
              .attr("width", 1)
              .attr("height", (numElements-1)*25 + 13)
              .style("fill", "grey");
          chart.append("text").text("100%")
              .attr("text-anchor", "middle")
              .attr("x", xScale(1))
              .attr("y", numElements*25+4)
              .attr("font-family", "sans-serif")
              .attr("font-size", "12px")
              .attr("fill", "grey");
  
  
      }
  
  
  });
  </code></pre>
  
  <p><strong>firstXmlView.controller.js</strong></p>
  
  <pre><code>jQuery.sap.registerModulePath("control.HorizontalBarChart", "control/HorizontalBarChart");
  jQuery.sap.require("control.HorizontalBarChart");
  jQuery.sap.registerModulePath("control.HorizontalBarChartItem", "control/HorizontalBarChart");
  jQuery.sap.require("control.HorizontalBarChartItem");
  
  
  
  sap.ui.controller("ui.firstXmlView", {
  
  /**
  * Called when a controller is instantiated and its View controls (if available) are already created.
  * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
  * @memberOf ui.firstXmlView
  */
      onInit: function() {
  
          var gModel = new sap.ui.model.json.JSONModel("json/performance-comparison.json");
          gModel.loadData("json/performance-comparison.json", null, false);
          sap.ui.getCore().setModel(gModel, "performance");
  
          var oChartHolder = this.byId("ChartHolder");
          var oChartItem = new control.HorizontalBarChartItem({region:"{region}", budget:"{budget}", bw:"{bw_ytd}", forecast:"{forecast}"});
  
  
          var oChart = new control.HorizontalBarChart({
                 items: {path : "/regions", template : oChartItem}
          });
  
          var oModel = sap.ui.getCore().getModel("performance");
          oChart.setModel(oModel);
  
          oChartHolder.addItem(oChart);
      },
  
  /**
  * Similar to onAfterRendering, but this hook is invoked before the controller's View is re-rendered
  * (NOT before the first rendering! onInit() is used for that one!).
  * @memberOf ui.firstXmlView
  */
  //  onBeforeRendering: function() {
  //  },
  
  /**
  * Called when the View has been rendered (so its HTML is part of the document). Post-rendering manipulations of the HTML could be done here.
  * This hook is the same one that SAPUI5 controls get after being rendered.
  * @memberOf ui.firstXmlView
  */
  //  onAfterRendering: function() {
  //
  //  },
  
  /**
  * Called when the Controller is destroyed. Use this one to free resources and finalize activities.
  * @memberOf ui.firstXmlView
  */
  //  onExit: function() {
  //
  //  }
  
  });
  </code></pre>
  
  <p><strong>index.html</strong></p>
  
  <pre><code>   &lt;!DOCTYPE HTML&gt;
  &lt;html&gt;
  &lt;head&gt;
      &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
      &lt;meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/&gt;
  
  
          &lt;!-- SAP UI5 Bootstrap --&gt;
           &lt;script src="/sap/ui5/1/resources/sap-ui-core.js"
                    id="sap-ui-bootstrap"
                    data-sap-ui-theme="sap_bluecrystal"
                    data-sap-ui-libs="sap.ui.commons, sap.ui.table, sap.ui.core, sap.m" &gt;
           &lt;/script&gt;
           &lt;!-- SAP UI5 Bootstrap --&gt;     
           &lt;!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required --&gt;
  
  
           &lt;!-- D3 Charts Load --&gt;
           &lt;script src="js/d3.min.js"&gt;&lt;/script&gt;
           &lt;script src="js/d3.Stacked.Chart.js"&gt;&lt;/script&gt;
           &lt;script src="js/d3.Pie.Chart.js"&gt;&lt;/script&gt;
           &lt;script src="js/Data.js"&gt;&lt;/script&gt;
           &lt;!-- D3 Charts Load --&gt;
  
           &lt;!-- Loading Custom Control --&gt;
           &lt;script&gt;
           sap.ui.localResources('control'); 
           jQuery.sap.require("control.HorizontalBarChart");
           jQuery.sap.require("control.pieChart");
           jQuery.sap.require("control.AutoCompleteValueHolder"); 
           jQuery.sap.require("control.root");
           jQuery.sap.require("control.stackedChart");
           jQuery.sap.require("control.stackChartItem");
           &lt;/script&gt;
           &lt;!-- Loading Custom Control --&gt;
  
  
           &lt;!-- Header --&gt;
      &lt;script type="text/javascript"&gt;
  /*
          var oAppHeader = new sap.ui.commons.ApplicationHeader("appHeader"); 
        //  oAppHeader.setLogoSrc("http://global.sap.com/global/images/SAPLogo.gif");
        // oAppHeader.setLogoText("SAP - Link Prediction PoC");
  
          oAppHeader.setDisplayWelcome(true);
          oAppHeader.setUserName("");
  
          oAppHeader.setDisplayLogoff(true);
          oAppHeader.placeAt("header");
  */
      &lt;/script&gt;
      &lt;!-- Header --&gt;
  
        &lt;!-- Stacked Chart --&gt;
      &lt;script type="text/javascript"&gt;
      /*
          var oPanelWheel = new sap.ui.commons.Panel();
  
              oPanelWheel.setTitle(new sap.ui.commons.Title({text: "Stacked Chart"}));
              oPanelWheel.setWidth("1000px");
              oPanelWheel.setCollapsed(false);
  
          var stackedChart = new sap.ui.core.HTML({
                  content: "&lt;div id='content' align='center'&gt;&lt;/div&gt;",
                  preferDOM: false,
                  afterRendering:  function(Event){
  
                      stackBuilding();
                  }   
          });
          //oPanelWheel.placeAt("content");
          stackedChart.placeAt("content");
          */
      &lt;/script&gt;
  
      &lt;!-- Stacked Chart --&gt;
  
      &lt;script&gt;
              sap.ui.localResources("ui");
              var view = sap.ui.view({ //id:"idui1", 
                  viewName:"ui.firstXmlView", type:sap.ui.core.mvc.ViewType.XML});
  
  
              view.placeAt("content");
              //var view = new sap.m.Shell({ 
              //app : new sap.ui.core.ComponentContainer({ 
              //  name : "sap.jaysdk" 
              //})
          //})
      &lt;/script&gt;
  
  
  
  &lt;/head&gt;
  &lt;body class="sapUiBody" role="application"&gt; 
      &lt;div id="header"&gt;&lt;/div&gt;
      &lt;div id="drawChart"&gt;&lt;/div&gt;
      &lt;div id="mainPage"&gt;&lt;/div&gt;
      &lt;div id="content"&gt;&lt;/div&gt;
      &lt;div id="test"&gt;&lt;/div&gt;
      &lt;div id="footer"&gt;&lt;/div&gt;
  &lt;/body&gt;
  </code></pre>
  
  <p></p>
  
  <p><strong>firstXMLView.view.xml</strong></p>
  
  <pre><code>&lt;core:View xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns="sap.m"
      controllerName="ui.firstXmlView" xmlns:html="http://www.w3.org/1999/xhtml"&gt;
  &lt;Page title="working on d3"&gt;
      &lt;content&gt;
      &lt;Panel&gt;
      &lt;headerToolbar&gt;
            &lt;Toolbar&gt;
                   &lt;Label text="YTD Booked/Won and Forecast to Budget (Percentage)" /&gt;
            &lt;/Toolbar&gt;
     &lt;/headerToolbar&gt;
     &lt;FlexBox id="ChartHolder" alignItems="Start" justifyContent="Center"&gt;
      &lt;/FlexBox&gt;
      &lt;/Panel&gt;
      &lt;/content&gt;
  &lt;/Page&gt;
  </code></pre>
  
  <p></p>
  
  <p><strong>performance-comparison.json</strong></p>
  
  <pre><code>{
      "regions":  [
          {
              "region":   "France",
              "budget":   "10123",
              "bw_ytd":   "12798",
              "forecast": "14885"
          },
          {
              "region":   "Germany",
              "budget":   "11903",
              "bw_ytd":   "5000",
              "forecast": "6500"
          },
          {
              "region":   "Japan",
              "budget":   "7000",
              "bw_ytd":   "8900",
              "forecast": "10500"
          },
          {
              "region":   "Mexico",
              "budget":   "5000",
              "bw_ytd":   "9000",
              "forecast": "10000"
          },
          {
              "region":   "UK",
              "budget":   "12500",
              "bw_ytd":   "14000",
              "forecast": "19634"
          },
          {
              "region":   "US",
              "budget":   "40000",
              "bw_ytd":   "65000",
              "forecast": "82000"
          }
      ]
  }
  </code></pre>