views:

26

answers:

1

Hello,

I am having trouble binding a LineChart to XML data via HTTPService object.

Here is my code:

<mx:Panel  
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:ui="flex.utils.ui.*" 
    xmlns:charts="flex.utils.ui.charts.*" 
    layout="vertical" horizontalAlign="center" verticalGap="2" paddingBottom="0" paddingTop="10" xmlns:local="*" 
    creationComplete="getChartDataSrv.send();"
    >

    <mx:HTTPService id="getChartDataSrv" result="onResult(event)"  url="http://localhost:8060/BusinessServiceLayer/DataService.asmx/GetDeviceDatasetsByDateGranular?xxxxxxxx" />

    <mx:Script>
        <![CDATA[
            import flex.utils.ui.events.CheckBoxLegendItemChangedEvent;
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
            import mx.controls.Alert;

            [Bindable]
            private var deviceData:ArrayCollection;

            private var deviceData2:ArrayCollection;

            private function legendItemChanged(event:CheckBoxLegendItemChangedEvent):void {
                var txt:String = "legendItemChanged: " + event.legendItem.label +
                    " [" + (event.legendItemSelected ? "selected]" : "not selected]");
                trace(txt);
                changeLabel.text = txt;

                // update the vertical axis minimum/maximum values to ignore the hidden series
                if (updateMinMaxCB.selected) {
                    updateVerticalAxisMinMax();
                }
            }

            private function updateVerticalAxisMinMax():void {
                var max:Number = 0;
                var min:Number = 0;
                var count:int = 0;
                for each (var series:LineSeries in linechart.series) {
                    if (series.visible) {
                        // contains LineSeriesItem objects
                        var items:Array = series.items;
                        // Get the min and max y values for each item from the yNumber property 
                        var minMax:Array = getMinMax(items, "yNumber");
                        min = Math.min(min, minMax[0]);
                        max = Math.max(max, minMax[1]);
                        count++;
                    }
                }
                if (count > 0) {
                    // change the min/max to the next interval
                    max = Math.ceil(max / vAxis.interval) * vAxis.interval;
                    min = Math.floor(min / vAxis.interval) * vAxis.interval;
                    //set the new minimum/maxmimum values
                    vAxis.maximum = max;
                    vAxis.minimum = min;
                }
            }

            private function getMinMax(items:Array, field:String = null):Array {
                var min:Number = 0;
                var max:Number = 0;
                for each (var item:* in items) {
                    if (item) {
                        var num:Number = NaN;
                        if (field && item.hasOwnProperty(field)) {
                            num = Number(item[field]);
                        } else {
                            num = Number(item);
                        }
                        if (!isNaN(num)) {
                            max = Math.max(max, num);
                            min = Math.min(min, num);
                        }
                    }
                } 
                return [ min, max ];
            }

            private function updateMinMaxCBClicked(event:Event):void {
                if (updateMinMaxCB.selected) {
                    updateVerticalAxisMinMax();
                } else {
                    // reset the minimum & maximum values so that the axis will calculate them properly
                    vAxis.minimum = NaN;
                    vAxis.maximum = NaN;
                }
            }

            private function swapDataProvider():void {
                if (changeDataBtn.selected) {
                    linechart.dataProvider = deviceData2;
                } else {
                    linechart.dataProvider = deviceData;
                }
                // validate first, then update the vertical axis min/max values
                linechart.validateNow();
                if (updateMinMaxCB.selected) {
                    updateVerticalAxisMinMax();
                }
            }

            private function onResult(evt:ResultEvent):void {
                linechart.dataProvider = evt.result.ArrayOfDAIX.DAIX;
                linechart.invalidateDisplayList();
            }

        ]]>
    </mx:Script>

    <mx:Stroke id = "s1" color="haloGreen"  weight="2"/>
    <mx:Stroke id = "s2" color="haloOrange" weight="2"/>
    <mx:Stroke id = "s3" color="haloBlue" weight="2"/>
    <mx:Stroke id = "s4" color="red" weight="2"/>
    <mx:Stroke id = "s5" color="yellow" weight="2"/>

    <mx:Label text="LineChart with CheckBox Legend" color="0xffffff" 
              fontSize="14" textDecoration="underline" fontWeight="bold"/>

    <mx:VBox enabled="true" paddingTop="10" paddingRight="5" paddingBottom="10" paddingLeft="5" 
             backgroundColor="white" width="100%" height="100%" horizontalAlign="center">
        <mx:LineChart id="linechart" color="#FDFDFD" width="100%" height="100%" 
                      showDataTips="true" dataProvider="{getChartDataSrv.lastResult.ArrayOfDAIX.DAIX}">
            <mx:horizontalAxis>
                <mx:CategoryAxis categoryField="w"/>
            </mx:horizontalAxis>
            <mx:verticalAxis>
                <mx:LinearAxis id="vAxis"/>
            </mx:verticalAxis>
            <mx:series>             
                <mx:LineSeries yField="v1" form="curve" displayName="Column 1" lineStroke="{s1}"/>
                <mx:LineSeries yField="v2" form="curve" displayName="Column 2" lineStroke="{s2}"/>
                <mx:LineSeries yField="v3" form="curve" displayName="Column 3" lineStroke="{s3}"/>
                <mx:LineSeries yField="v4" form="curve" displayName="Column 4" lineStroke="{s4}"/>
                <mx:LineSeries yField="v5" form="curve" displayName="Column 5" lineStroke="{s5}"/>
            </mx:series>
        </mx:LineChart>

        <ui:TitledBorderBox title="Legend" borderColor="#666666" id="legendBox">
            <charts:CheckBoxLegend dataProvider="{linechart}" color="white" direction="vertical" 
                                   toggleChartSeries="true" change="legendItemChanged(event)" id="legend"/>
        </ui:TitledBorderBox>

    </mx:VBox>

    <mx:Label id="changeLabel" color="#ffffff"/>
    <mx:HBox horizontalAlign="left">
        <mx:CheckBox id="cb" selected="true" label="{cb.selected ? 'Select None' : 'Select All'}" 
                     click="legend.selectAllOrNone(cb.selected)"/>
        <mx:Spacer width="10" height="10"/>
        <mx:CheckBox label="Update Vertical Axis Min/Max?" id="updateMinMaxCB"
                     toolTip="Check this box to update the vertical axis minimum and maximum values when the legend items change"
                     change="updateMinMaxCBClicked(event)"/>
    </mx:HBox>
    <mx:Button label="Change Data" toggle="true" id="changeDataBtn" click="swapDataProvider()"
               toolTip="Change the data provider to a different set of values"/>

</mx:Panel>

Originally I had written a static array of data, to get this example working. It worked fine, until I made the HTTPService object and appropriate WebService calls.

When I debug this code, I can see that my HTTPService object is returning the array correctly, it has 96 items, which is desired.

The problem is, the LineChart graph is already rendered to the browser, and I cannot get my data into my LineChart. The static array worked fine but I am at a loss on why it will not work with my ResultEvent and HTTPService object.

Why can I not bind my Chart within my ResultEvent, or directly, with the result object? Even if I explicitly set the dataprovider of "linechart" (in the ResultEvent method) it does not update the LineChart data.

Also, if I use "linechart.invalidateDisplayList();", it too does not populate or redraw my LineChart. Is that because it's contained within a Panel?

Any ideas? Also, take note that this is all happening within a Panel and not within an Application. I cannot use Application tags on this component, so I am fairly confused about this. Why does the LineChart render first, and then my HTTPService produce the data the Chart will not use?

A: 

I figured out my problem and it has very much to do with case sensitive column mappings in the LineSeries container. Apparently, my static array I originally used had lower case names. My new array, being produced from the HTTPService/WebService invoke, was upper case.

once I matched them up, the LineChart worked fine.

I am very surprised that FLEX never griped or complained that I was referencing "yField" objects that did not exist. It seems to gripe about everything else, except important things like this. Who woulda thunk?

This code fixed my problem (see uppercase yFields):

        <mx:LineChart id="linechart" color="#FDFDFD" width="100%" height="100%" 
                  showDataTips="true" dataProvider="{getChartDataSrv.lastResult.ArrayOfDAIX.DAIX}">
        <mx:horizontalAxis>
            <mx:CategoryAxis categoryField="W"/>
        </mx:horizontalAxis>
        <mx:verticalAxis>
            <mx:LinearAxis id="vAxis"/>
        </mx:verticalAxis>
        <mx:series>             
            <mx:LineSeries yField="V1" form="curve" displayName="Column 1" lineStroke="{s1}"/>
            <mx:LineSeries yField="V2" form="curve" displayName="Column 2" lineStroke="{s2}"/>
            <mx:LineSeries yField="V3" form="curve" displayName="Column 3" lineStroke="{s3}"/>
            <mx:LineSeries yField="V4" form="curve" displayName="Column 4" lineStroke="{s4}"/>
            <mx:LineSeries yField="V5" form="curve" displayName="Column 5" lineStroke="{s5}"/>
        </mx:series>
    </mx:LineChart>
Devtron