Search code examples
flashapache-flexlistbuilderitemrenderer

Cannot get data out of the dataprovider in a flex application


I am building an app for a website that will filter through the shops carrying some products/brands and show the results in a List component. I got all the way up to loading the data, putting brands/countries/cities in comboboxes so a user can filter in which country and city he wants to look and for which brand. No problem till now. My problem is that while the dataprovider works as it should when hardcoding some values to the labels that i use for the item renderer of each entry, i cannot get the real data from the data provider.. you will see in the code below, that in the VGroup component (which i use for each shop in the list) there are 4 labels, where 3 of them are hard coded, just to see it works. One of the labels though is trying to get the real data from the xml file and thats where i stop. In all the tutorials i ve seen (and documentation too) everybody just hits "data.XYZ" where XYZ is the name of the attribute or node to get. Doing the same in my case just returns an error of "1120: Access of undefined property data.". What am i doing wrong? Its been a couple of hours i am iterating over hundreds of examples for flex, both new and old versions and i cant find out what to do. Its really nerve breaking to stick to this last small thing when all the rest are in! Have a look at the code and let me know if i did something the wrong way or i missed something! posting the code below. There is also an xml file but its loaded correctly and the different xmlLists DO have the correct nodes stored, so i do not think it needs to be posted, unless you think otherwise. The xml nodes for each shop are "title", "tel","mail","city" and i am stuck trying to get the title. The function that matters the most is the filterShops one and then of course the whole list and itemrenderer block.

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               width="815" height="578" minWidth="955" minHeight="600"
               creationComplete="sendData()">

    <fx:Style source="styles.css"/>

    <fx:Declarations>
        <s:HTTPService id="shopsService" url="data/places.xml"     result="shopsService_return(event)" resultFormat="e4x"/>
    </fx:Declarations>

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.collections.XMLListCollection;
            import mx.events.FlexEvent;
            import mx.rpc.events.ResultEvent;

            import spark.events.IndexChangeEvent;

            private var placesXML:XMLList;

            [Bindable] private var countriesList:XMLListCollection;
            [Bindable] private var brandsList:XMLListCollection;
            [Bindable] private var citiesList:XMLListCollection;
            [Bindable] private var shopsList:XMLListCollection;
            [Bindable] public var gridList:XMLListCollection;

            private var selectedCountry:int = 0;
            private var selectedCountryId:int = 0;
            private var selectedBrand:int = 0;
            private var selectedCity:int = 0;
            private var selectedBrandId:int = 0;
            private var selectedCityId:int = 0;

            private function sendData():void {
                shopsService.send();
            }

            private function shopsService_return(event:ResultEvent):void {
                placesXML = XMLList(event.result);
                brandsList = new XMLListCollection(placesXML.brands.brand);
                citiesList = new     XMLListCollection(placesXML.countries.country[0].cities.city);
                shopsList = new XMLListCollection(placesXML.shops.shop);
                gridList = new XMLListCollection();
            }

            private function onBrandboxChange(event:IndexChangeEvent):void {
                selectedBrand = event.target.selectedIndex;
                selectedBrandId = placesXML.brands.brand[selectedBrand].attribute("id");
                filterShops(selectedCountryId.toString(),selectedBrandId.toString(),selectedCityId.toString());
            }

            private function onCityboxChange(event:IndexChangeEvent):void {
                selectedCity = event.target.selectedIndex;
                selectedCityId = placesXML.countries.country[selectedCountry].cities.city[selectedCity].attribute("id");
                filterShops(selectedCountryId.toString(),selectedBrandId.toString(),selectedCityId.toString());
            }

            private function englandClick(event:MouseEvent):void
            {
                citiesList = new XMLListCollection(placesXML.countries.country[0].cities.city);
                selectedCountry = 0;
                selectedCountryId = 1;
            }

            private function scotlandClick(event:MouseEvent):void
            {
                citiesList = new XMLListCollection(placesXML.countries.country[1].cities.city);
                selectedCountry = 1;
                selectedCountryId = 2;
            }

            private function filterShops(countryId:String,brandId:String,cityId:String):void {
                var xmlCountryId:String;
                var indexOfBrandId:int;
                var xmlCityId:String;
                //outputLabel.text = countryId+"-"+brandId+"-"+cityId;

                for (var i:int = 0; i < shopsList.length; i++) {
                    xmlCountryId = shopsList[i].attribute("country");
                    xmlCityId = shopsList[i].attribute("city");

                    if (xmlCountryId == countryId && xmlCityId == cityId) {
                        indexOfBrandId = shopsList[i].attribute("brands").toString().split(",").indexOf(brandId);
                        if (indexOfBrandId > -1) gridList.addItem(shopsList[i]);
                    }
                    outputLabel.text = shopsList[0].title;
                }
            }

        ]]>
    </fx:Script>

    <s:List id="outputWindow" x="10" y="230" width="795" height="338" borderVisible="false"
        dataProvider="{gridList}">
        <s:layout>
            <s:TileLayout
            requestedColumnCount="4"
            requestedRowCount="2"
            horizontalGap="2"
            verticalGap="2"/>
        </s:layout>
        <s:itemRenderer>
            <fx:Component>
                <s:VGroup width="180" height="90">
                    <s:Label styleName="item-title" text="{data.title}" />
                    <s:Label color="#868686" fontFamily="pfHighwayRegular" fontSize="12" paddingBottom="-2"
                         paddingTop="-2" styleName="item-text" text="Tel: 0120 230 3777"/>
                    <s:Label styleName="item-text" text="Mail: [email protected]"/>
                    <s:Label styleName="item-link" text="Web: www.2012ltd.co.uk"/>
                </s:VGroup>
            </fx:Component>
        </s:itemRenderer>
    </s:List>
    <s:Button x="6" y="8" label="England" click="englandClick(event)"/>
    <s:Button x="125" y="8" label="Scotland" click="scotlandClick(event)" />
    <s:Button x="261" y="10" label="Wales"/>
    <s:Button x="400" y="10" label="Ireland"/>
    <s:ComboBox x="505" y="56" id="brandBox" dataProvider="{brandsList}" change="onBrandboxChange(event)" />
    <s:ComboBox x="659" y="56" id="cityBox" dataProvider="{citiesList}" change="onCityboxChange(event)" />
    <s:Label id="outputLabel" x="10" y="119" text="Label"/>
</s:Application>

Solution

  • So take your component out of being an inline definition and define it in another file, that will make this slightly less confusing, when you do so add a implements to to the VGroup and implement IDropInListItemRenderer and IDataRenderer. This means you'll have a data property and a listData property within the renderer, the data will have one element of the list assigned to it for each renderer used (or re-used) the listData will have information about the main list and the index of the data in the dataProvider etc.