SAP UI5中的自定义d3控件的数据绑定错误

时间:2015-04-24 14:06:34

标签: d3.js sapui5

我正在使用SAP UI5中的自定义d3控件。我在这里找到了一个关于如何做到这一点的模板:http://scn.sap.com/community/developer-center/front-end/blog/2014/07/17/custom-sapui5-visualization-controls-with-d3js

所以现在我只想努力做到这一点。我的主要问题是我的控件不会消耗模型中的数据。这是错误:

  • 错误:属性x =“NaN”
  • 的值无效
  • 错误:属性height =“ - 12”
  • 的负值无效
  • 错误:属性x =“NaN”
  • 的值无效

我认为这是异步请求的问题,所以我添加了这个:gModel.loadData("json/performance-comparison.json", null, false);但这仍然没有用。

感谢任何想法。

以下是代码:

Horizo​​ntalBarChart.js

  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("<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(">");
        oRm.renderControl(layout);
        oRm.addClass('verticalAlignment');

        oRm.write("</div>");

    },

    onAfterRendering: function(){
        console.log("sap.jaysdk.PerformanceToTargetComparison.onAfterRendering()");
        var cItems = this.getItems();
        //console.log(cItems);
        var data = [];
        for (var i=0;i<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<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<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>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>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>1.5 && maxval<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<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<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<1){
                    return xScale(1) - xScale(d.bw);
                }else{
                    return xScale(d.bw)-xScale(1);
                }
            })
            .style("fill", function(d){ if(d.bw<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<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<1){
                return xScale(1) - xScale(d.forecast);
            }else{
                return xScale(d.forecast) - xScale(1);
            }
        })
        .style("fill", function(d){ if(d.forecast<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");


    }


});

firstXmlView.controller.js

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() {
//
//  }

});

的index.html

   <!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>


        <!-- SAP UI5 Bootstrap -->
         <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" >
         </script>
         <!-- SAP UI5 Bootstrap -->     
         <!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->


         <!-- D3 Charts Load -->
         <script src="js/d3.min.js"></script>
         <script src="js/d3.Stacked.Chart.js"></script>
         <script src="js/d3.Pie.Chart.js"></script>
         <script src="js/Data.js"></script>
         <!-- D3 Charts Load -->

         <!-- Loading Custom Control -->
         <script>
         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");
         </script>
         <!-- Loading Custom Control -->


         <!-- Header -->
    <script type="text/javascript">
/*
        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");
*/
    </script>
    <!-- Header -->

      <!-- Stacked Chart -->
    <script type="text/javascript">
    /*
        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: "<div id='content' align='center'></div>",
                preferDOM: false,
                afterRendering:  function(Event){

                    stackBuilding();
                }   
        });
        //oPanelWheel.placeAt("content");
        stackedChart.placeAt("content");
        */
    </script>

    <!-- Stacked Chart -->

    <script>
            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" 
            //})
        //})
    </script>



</head>
<body class="sapUiBody" role="application"> 
    <div id="header"></div>
    <div id="drawChart"></div>
    <div id="mainPage"></div>
    <div id="content"></div>
    <div id="test"></div>
    <div id="footer"></div>
</body>

firstXMLView.view.xml

<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">
<Page title="working on d3">
    <content>
    <Panel>
    <headerToolbar>
          <Toolbar>
                 <Label text="YTD Booked/Won and Forecast to Budget (Percentage)" />
          </Toolbar>
   </headerToolbar>
   <FlexBox id="ChartHolder" alignItems="Start" justifyContent="Center">
    </FlexBox>
    </Panel>
    </content>
</Page>

性能comparison.json

{
    "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"
        }
    ]
}

1 个答案:

答案 0 :(得分:0)

我不建议您仅仅因此而进行同步通话。

使用数据绑定时需要考虑的事项:

  • 你需要一个模型(json,xml,odata,等等)
  • 您的模型必须在控件上使用或使用getCore()。setModel()
  • 您必须为控件定义路径

但是,请记住:

例如,如果在定义数据绑定时使用格式化程序函数,则应事先加载模型以避免出现错误(“NaN”)。如果您创建了绑定路径+模型但未加载,即使没有数据,也会调用格式化程序函数。换句话说,函数的形式参数将是未定义的。

在特定时刻,您的模型可能已创建,但不一定意味着您的客户端上已加载数据 - 这就是为什么良好的数据绑定技术是在加载模型时定义绑定路径。如果您查看任何模型类,您将找到使您能够在加载数据或请求失败时定义回调的方法(请参阅attachRequestCompleted或attachRequestFailed方法)。

使用这种技术,您可以保留异步调用并在数据就绪时定义数据绑定。