1. #1
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Vote Rating
    0
    Answers
    4
    BionicGeek is on a distinguished road

      0  

    Default Answered: Using GXT Charts with AutoBean populated ListStores?

    Answered: Using GXT Charts with AutoBean populated ListStores?


    Is it possible to create GXT3 charts which are backed by AutoBean-populated ListStores? In my testing I cannot seem to get the charts to generate (not even display on the screen) if the ListStore upon which it is based is a ListStore of type <AutoBean>. For example:

    Code:
    // (assume MyAutoBean is an interface and that I've used the 
    // JsonReader to  marshall some JSON into a collection of 
    // AutoBeans in a ListStore named 'myListStore' which is of type ListStore<MyAutoBean>).
    
    Chart<MyAutoBean> newChart = new Chart<MyAutoBean>();
    newChart.setStore(myListStore);
    
    // the rest of the chart configuration code left out for brevity
    When I attempt to render a chart such as the example, I see no result (no blank chart, nothing). I can confirm the rest of the UI code is fine and have verified that other components display in the view, but the charts will not generate.

    I did notice that the examples all use concrete objects when populating their ListStores so I am curious if the charts require concrete instances of objects.

    Note: grids happily work with AutoBean shim-populated ListStores and the examples demonstrate this capability. I have no issue using these types of ListStores with grids but when attempting to use the same ListStore (or a copy of the same ListStore) as what I am using in the grid, the chart never renders.

  2. I have uncovered and (mostly) corrected the issue I encountered.

    The first part was that while the ListStore was still being populated I was allowing the button click (and ultimately the rendering of the chart) to occur before the population process completed. As Colin stated previously, if the ListStore is empty, the chart will not render (i.e. you will not see a blank chart with axis lines and no data). Using the bindStore(...) method accounts for this scenario as a change to the store (e.g. the loader populating it completes) will force a redraw of the chart. (I suppose an alternative would be to disallow rendering of the chart prior to confirming that the ListStore is fully populated).

    The second part was a display formatting issue. While I was using the example code and adding the chart to a simple container, for some reason formatting the size of only the container (or, as in the example, adding it to a BorderLayoutContainer and specifying its BorderLayoutData) was not sufficient to display the chart. Upon inspection of the page I found that the chart images were present but not visible because the chart itself had not been properly formatted and sized in the container.

    I'm not sure if this is an optimal solution but I found that manually setting the chart height and width to 100% prior to adding it to the SimpleContainer made the chart visible when rendered. If there is a better solution or an alternative approach I would welcome a suggestion.

    UPDATE

    Quoting from a later post from Colin:

    It should *not* be required to resize the chart to make it visible. None of the examples do this. To say more, I'd need to know how the rest of the layout is working - being added to a SimpleContainer should mean that it gets a height and width, assuming that container is being given a size from its parent, and so on. The chart defaults to a very small size, assuming that you didn't want to draw it, so it must be given a size. It is somewhat like the Grid in that it doesn't make sense to let it decide for itself how big it should be.
    I am guessing that the way I have the objects laid out is impacting how the formatting and sizing of the chart occurs.

    I'm marking this answer as correct and including the quote about formatting in the event someone comes across this thread and encounters the same issues and does not read through the entire thread.

    Thanks to Colin for the clarifications and quick responses.

  3. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Posts
    1,934
    Vote Rating
    55
    Answers
    73
    Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough

      0  

    Default


    Two ideas for fixing this, and both are making the missing step of informing the Chart that data has changed.

    The issue here is that Charts behave slightly differently than Grids or Trees - they don't automatically listen to the ListStore for changes that might happen. This is done primarily to give developers more latitude when deciding when to make redraws occur. As such, you need to either:

    a) Tell the Chart to redraw itself explicitly when the data has changed:
    Code:
    chart.redrawChart();
    This should be done when you are ready to have the chart show the new items.
    b) Tell the Chart to listen to any changes in the ListStore, and automatically update accordingly:
    Code:
    //newChart.setStore(myListStore);
    newChart.bindStore(myListStore);
    I find that I almost always want bindStore instead of setStore, but if you do need to switch from bind back to set, you'll need to add redrawChart calls anywhere data is changed. I haven't found that to be a problem yet, but it is important to keep in mind. Charts are more expensive to redraw than, say, Grids, especially if you enable animations and shadows.

  4. #3
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Vote Rating
    0
    Answers
    4
    BionicGeek is on a distinguished road

      0  

    Default


    Thanks but I have tried the bindStore(myListStore) already to no avail (though that is a good suggestion how to keep the chart up to date if/when the data changes). The issue for me isn't that the chart isn't updating to account for new data - it never renders in the first place. I've tried creating a new ListStore<MyAutoBean> and binding that to the chart on the assumption that the chart would at least render, but so far that has not worked either. I based my theory (and thus the question) about charts not rendering when bound to AutoBean typed ListStores.

    Is it possible charts will not render even their generic elements (e.g. axis lines, legends, etc.) if there is no data in the ListStore?

    Incidentally - I've been prototyping this using code that is based on and virtually identical to the bar chart from the Dashboard example. The differences are that I removed the listeners for the time being as all I'm interested in is a display of the bar chart. The AutoBean I'm using has only two values, a String (for the Category Axis) and an Integer (for the bar series). The ListStore (setup, load, etc.) is virtually identical to the Json Grid example.

  5. #4
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Posts
    1,934
    Vote Rating
    55
    Answers
    73
    Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough

      0  

    Default


    Without data, there is nothing that can be drawn on the axis or legend, so yes, that is expected behavior. There must be at least two (okay, technically one, but a single item is almost as useless as zero) items for a series to make sense, therefore for the axis and legend to draw meaningful items.

    If you used bindStore, after items are loaded into the store (verify this by adding your other store handlers perhaps? Maybe draw a message box with the total count of items in the store, or log some messages?), the chart will automatically invoke redrawChart() and rebuild all axes/series/legends with the given items.

    I have at least two local projects using autobeans and charts, and have been pretty happy with them - everything seems to behave as expected. Can you provide a simple example that shows your case not working? I might try to build up a quick JSON + loader + ListStore + Chart example based on the JSON grid example and some random chart if you are unable, but, as I mentioned, I expect this to work on the first try, so your code might be easier to work with to find the problem.

  6. #5
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Vote Rating
    0
    Answers
    4
    BionicGeek is on a distinguished road

      0  

    Default


    Thanks again for the quick response. Here is the code I'm using to generate the chart. Any thoughts or insight would be greatly appreciated.

    AutoBean
    Code:
    //AutoBean
    public interface Bead {
      String getColor();
      void setColor(String color);
      String getCount();
      void setCount(String count);
    }
    // count attribute defined as a String is intentional
    PropertyAccess
    Code:
    public interface BeadPropertyAccess extends PropertyAccess<Bead> {
      ValueProvider<Bead, String> name();
    }
    Chart Generation
    Code:
    private BeadPropertyAccess props = GWT.create(BeadPropertyAccess.class);
    
    @UiField
    SimpleContainer chartContainer; // the chart is generated by a button click and added to this container
    
    public void generateChart(ListStore<Bead> beadStore) {
      Window.alert("Store Size = " + beadStore.size()); // result is always > 2
      Chart<Bead> beadChart = new Chart<Bead>();
      beadChart.bindStore(beadStore);
      beadChart.setShadowChart(true);
      
      CategoryAxis<Bead, String> catAxis = new CategoryAxis<Bead, String>();
      catAxis.setPosition(Position.BOTTOM);
      catAxis.setField(props.name());
      TextSprite rotation = new TextSprite();
      rotation.setRotation(270);
      catAxis.setLabelConfig(rotation);
      catAxis.setLabelProvider(new LabelProvider<String>() {
        @Override
        public String getLabel(String item) {
          if (item.length > 8) { return item.substring(0, 8) + "..."; }
          else { return item; }
          }
      });
      beadChart.addAxis(catAxis);
    
      BarSeries<Bead> bar = new BarSeries<Bead>();
      bar.setYAxisPosition(Position.LEFT);
      bar.addYField(new ValueProvider<Bead, Integer>() {
        @Override
        public Integer getValue(Bead object) {
          if (null != object && null != object.getCount()) {
            return Integer.valueOf(object.getCount()); // doesn't test for empty String but they will not be in this case
          }
          return 0;
        }
        @Override
        public void setValue(Bead object, Integer value) {
          object.setCount(value.toString());
        }
        @Override
        public String getPath() {
          return "count";
        }
      });
      SeriesLabelConfig<Bead> barLabelConfig = new SeriesLabelConfig<Bead>();
      rotation = rotation.copy();
      rotation.setTextAnchor(TextAnchor.END);
      rotation.setTextBaseline(TextBaseline.MIDDLE);
      barLabelConfig.setSpriteConfig(rotation);
      bar.setLabelConfig(barLabelConfig);
      bar.addColor(new RGB(148, 174, 10));
      bar.setColumn(true);
      beadChart.addSeries(bar);
    
      chartContainer.add(beadChart);
    
    }
    In testing I can confirm that adding some basic component (e.g. a text box) to the chartContainer on button click works as expected, so I don't believe there are issues with the container or the view itself. I also considered that perhaps there was some issue with the ListStore being passed in, but as you suggested (and note added in the code) I verified the count of objects in the Store before attempting to generate the chart.

    I have tried adding a beadChart.redrawChart() at the end of the method as well to see if I can force the redraw. I've also tried using the setStore(...) method in place of the bindStore(...), with the same result (no chart).

  7. #6
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Vote Rating
    0
    Answers
    4
    BionicGeek is on a distinguished road

      0  

    Default


    I have uncovered and (mostly) corrected the issue I encountered.

    The first part was that while the ListStore was still being populated I was allowing the button click (and ultimately the rendering of the chart) to occur before the population process completed. As Colin stated previously, if the ListStore is empty, the chart will not render (i.e. you will not see a blank chart with axis lines and no data). Using the bindStore(...) method accounts for this scenario as a change to the store (e.g. the loader populating it completes) will force a redraw of the chart. (I suppose an alternative would be to disallow rendering of the chart prior to confirming that the ListStore is fully populated).

    The second part was a display formatting issue. While I was using the example code and adding the chart to a simple container, for some reason formatting the size of only the container (or, as in the example, adding it to a BorderLayoutContainer and specifying its BorderLayoutData) was not sufficient to display the chart. Upon inspection of the page I found that the chart images were present but not visible because the chart itself had not been properly formatted and sized in the container.

    I'm not sure if this is an optimal solution but I found that manually setting the chart height and width to 100% prior to adding it to the SimpleContainer made the chart visible when rendered. If there is a better solution or an alternative approach I would welcome a suggestion.

    UPDATE

    Quoting from a later post from Colin:

    It should *not* be required to resize the chart to make it visible. None of the examples do this. To say more, I'd need to know how the rest of the layout is working - being added to a SimpleContainer should mean that it gets a height and width, assuming that container is being given a size from its parent, and so on. The chart defaults to a very small size, assuming that you didn't want to draw it, so it must be given a size. It is somewhat like the Grid in that it doesn't make sense to let it decide for itself how big it should be.
    I am guessing that the way I have the objects laid out is impacting how the formatting and sizing of the chart occurs.

    I'm marking this answer as correct and including the quote about formatting in the event someone comes across this thread and encounters the same issues and does not read through the entire thread.

    Thanks to Colin for the clarifications and quick responses.

  8. #7
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Posts
    1,934
    Vote Rating
    55
    Answers
    73
    Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough

      0  

    Default


    Code:
      public void generateChart(ListStore<Bead> beadStore) {
      Window.alert("Store Size = " + beadStore.size()); // result is always > 2
      Chart<Bead> beadChart = new Chart<Bead>();
      beadChart.bindStore(beadStore);
    From this alone, you already have data when the chart gets created, so no binding is needed. Invoking bindStore will add event handlers to the liststore, but those won't go off, since the store is apparently pre-populated.

    It all looks reasonable, but I can't run it with what is there - items have to be read in, etc.

    The one main thing that worries me is the lack of a numeric axis - I see the bar series, and the category axis (i.e. group by name), but no numeric axis to show how high the items should be, how they get labeled, etc. Is this deliberate?

  9. #8
    Sencha User
    Join Date
    May 2012
    Posts
    19
    Vote Rating
    0
    Answers
    4
    BionicGeek is on a distinguished road

      0  

    Default


    See my previous, found a bit of a solution. However, to answer your question about whether or not the lack of NumericAxis is deliberate - it is. I modeled it on the Dashboard example:

    http://www.sencha.com/examples/#ExamplePlace:dashboard

    To address the store issue - yes I would need to include some of my other code used for reading in the data to the store, but suffice to say you were correct about the charts working just fine with an AutoBean populated ListStore. The root cause of the issue I encountered turned out to be something separate from the data in the store.

  10. #9
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Posts
    1,934
    Vote Rating
    55
    Answers
    73
    Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough Colin Alworth is a jewel in the rough

      0  

    Default


    I had started by previous post before I saw yours, so making a new quick one to try to address your comments.

    It should *not* be required to resize the chart to make it visible. None of the examples do this. To say more, I'd need to know how the rest of the layout is working - being added to a SimpleContainer should mean that it gets a height and width, assuming that container is being given a size from its parent, and so on. The chart defaults to a very small size, assuming that you didn't want to draw it, so it must be given a size. It is somewhat like the Grid in that it doesn't make sense to let it decide for itself how big it should be.

Tags for this Thread