[CLOSED] series renderer problems by store reload

  1. #1

    [CLOSED] series renderer problems by store reload

    Dear Ext.Net Team,

    Based on Chart renderer example, I created following example which when swiped to right/left refreshes the store and hence the chart/series. However some generated sprites remains and are not cleared from the surface. Any solution?

    @using Ext.Net
    @using Ext.Net.MVC
    
    @{
        ViewBag.Title = "Errorin_Chart";
        var X = Html.X();
    }
    
    
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>@ViewBag.Title</title>
           
            <script>
                var onSwipe = function(e) {                
                    switch (e.direction) {                
                    case "right":
                        App.isNextPressed.setValue("false");
                        break;                
                    case "left":
                        App.isNextPressed.setValue("true");                    
                        break;
                    default:
                    }
                    //console.log(App.Offset.value +' swiped ' + e.direction);
                    debugger;
                    App.ChartStore.reload();                
                }
    
                function renderData(sprite, config, rendererData, index) {
                    var store = rendererData.store,
                        storeItems = store.getData().items,
                        last = storeItems.length - 1,
                        record = storeItems[index],
                        diff = record && 1,
                        changes = {},
                        surface = sprite.getParent(),
                        isRtl = this.getSurface().getInherited().rtl,
                        textSprites,
                        textSprite,
                        rectSprite;
                    if (!record) {
                        return;
                    }
                    // This renderer function draws a red label if series #2 is greater than series #1.
                    // The label displays the difference between the values of series #1 and series #2.
                    //
                    // Note: The two renderer functions in this test case cannot be consolidated. The red labels
                    // are rendered here because they are positioned relatively to the series #2 columns.
                    if (diff > 0) {
                        changes.strokeStyle = 'tomato';
                        changes.fillStyle = 'mistyrose';
                        changes.opacity = 1.0;
    
                        textSprites = surface.myTextSprites;
                        if (!textSprites) {
                            textSprites = surface.myTextSprites = [];
                        }
                        textSprite = textSprites[index];
                        if (!textSprite) {
                            textSprite = textSprites[index] = surface.add({ type: 'text' });
                            rectSprite = textSprite.rectSprite = surface.add({ type: 'rect' });
                        } else {
                            rectSprite = textSprite.rectSprite;
                            textSprite.show();
                            rectSprite.show();
                        }
    
                        rectSprite.setAttributes({
                            x: config.x + (index == last ? -17 : (isRtl ? -50 : 16)),
                            y: config.y - 36,
                            width: 30 + (diff >= 10 ? (diff >= 100 ? (diff >= 1000 ? 18 : 12) : 6) : 0),
                            height: 18,
                            stroke: 'tomato',
                            fill: 'mistyrose',
                            lineWidth: 1,
                            radius: 4,
                            zIndex: 10000
                        });
    
                        textSprite.setAttributes({
                            text: "+ " + diff,
                            x: config.x + (index == last ? -11 : (isRtl ? -46 : 20)),
                            y: config.y - 23,
                            fill: 'red',
                            fontSize: 12,
                            zIndex: 10001,
                            scalingY: -1
                        });
                    } else {
                        changes.strokeStyle = 'dodgerblue';
                        changes.fillStyle = 'palegreen';
                        changes.opacity = 0.6;
    
                        textSprites = surface.myTextSprites;
                        if (textSprites) {
                            textSprite = textSprites[index];
                            if (textSprite) {
                                textSprite.rectSprite.hide();
                                textSprite.hide();
                            }
                        }
                    }
                    return changes;
                }
            </script>
        </head>
        <body>
            <h1>Buggy Chart</h1>      
            @(Html.X().ResourceManager().ShowWarningOnAjaxFailure(false))     
            
            @(X.CartesianChart()
                  .ID("testchart2")
                  .Height(400)
                  .InsetPadding(40).Flex(1)
                  .Listeners(l =>
                  {
                      //l.Swipe.Handler = "";
                      //l.Tap.Handler = "console.log('tapped');";
                      l.Swipe.Fn = "onSwipe";
                      
                  })
                  .Bin(
                      X.Hidden().ID("isNextPressed").Value("false"))
                  .Store(Html.X().Store().ID("ChartStore").Model(Html.X().Model().Fields(f =>
                  {
                      f.Add(Html.X().ModelField().Name("Intervall"));
                      f.Add(Html.X().ModelField().Name("Present"));
                  })).Proxy(Html.X().RestProxy().AppendAction(false).Reader(
                      Html.X().JsonReader().RootProperty("data")).API(urls => { urls.Read = Url.Action("GenerateDummyData", "Chart"); })
                      ).Parameters(pm => { pm.Add(new StoreParameter("isNextPressed", "App.isNextPressed.value", ParameterMode.Raw)); })
                  )              
                  .Axes(
                      Html.X().CategoryAxis()
                          .Position(Position.Bottom)
                          .Fields("Intervall")
                          .Label(Html.X().ChartLabel().RotationDegrees(-45))
                  )
                  .Series(
                      Html.X().BarSeries()
                          .XField("Intervall")
                          .StyleSpec(Html.X().Sprite()
                              .Opacity(2)
                              .FillStyle("#0000CD"))
                          .YField(new[] {"Present"})
                          .Titles(new[] {"Present"})
                          .Renderer(r => r.Fn = "renderData")              
                  ))
        </body>
    </html>
    The chart conroller:

     public StoreResult GenerateDummyData(bool isNextPressed)
            {
                //get the location
                //get the counts for location with granularity and offset
                var data = new List<object>();
                Random rnd = new Random();
    
                if (isNextPressed)
                {
                    //Hour aggregation
                    for (var i = 8; i < 14; i++)
                    {
                        data.Add(new { Intervall = i + ":00", Present = rnd.Next(5, 12)});
                    }
                    for (var i = 14; i < 18; i++)
                    {
                        data.Add(new { Intervall = i + ":00", Present = rnd.Next(5, 12)});
                    }
                }
    
                else
                {
                    //Weekly aggregation
                    for (var i = 1; i < 53; i++)
                    {
                        data.Add(new { Intervall = "W" + i, Present = rnd.Next(100, 150)});
                    }
                }
    
                return new StoreResult(data);
            }
    Last edited by fabricio.murta; Feb 25, 2017 at 4:07 AM. Reason: no user feedback for 7+ days
  2. #2
    Hello @mirwais!

    Thanks, the test case you provided was perfect, I could reproduce the issue here just fine!..

    The problem with the sprites is because they are not really a chart's element, but manually added from the renderer. So, the chart does not really know they are there when you redraw it with less columns.

    Some "reverse-renderer" should be run to wipe out left over custom added sprites. I'm actually impressed it moved the other sprites correctly in the reload animation!

    If you look at lines 101-104, code there suggests that, during an update and a column turns from red (diff > 0) to blue, it should by itself hide the bar's extra sprites.

    This is a little overkill, but safe. I suggest you dig down this and try after the load of the store, count how many labels you actually used and hide the remaining ones.

    Simple proof of concept solution is just bind to the store's BeforeLoad handler something like this:

    var beforeLoadHandler = function (store, operation) {
        var chart = App.testchart2,
            series = chart.series[0],
            surface = series.getSurface(),
            spriteList = surface.myTextSprites;
    
        if (spriteList && spriteList.length > 0) {
            spriteList.forEach(function (sprite) {
                if (sprite.rectSprite) {
                    sprite.rectSprite.hide();
                }
                sprite.hide();
            });
        }
    }
    Hope this helps!
    Fabrício Murta
    Developer & Support Expert
  3. #3
    Hello Fabricio,
    Thank you for the support, it's indeed an overkill but I can live with it, it is a special case.
    I am however curious about seriesrenderer: add to line 45 of js function

    console.log(index + ' field:' + sprite.getField()+ ' style:' + sprite.attr.fillStyle);
    You will see it will be called many times with the same sprites, why is that so?

    Additionally for better performance one can use: setAttributes(changes, bypassNormalization:true) but make sure changes have the correct attributes.
  4. #4
    Hello @mirwais, the answer for this recurrent call of the renderer is simple: animation. Renderer does what is says, it renders the entity with your custom behavior. If it has to render several times to perform animation, so that's what happens.

    Try adding to your CartesianChart() definition .Animation(false) and you'll see it is called only once. Usually it is a good idea the renderer functions to be the fastest and simplest the possible, because they are something that needs to run in real time during interactions with the component, be it the chart or a grid panel.

    In the case of a buffered grid panel, for example, the renderer is called as the user scrolls thru the grid or switch pages, so it is called once (or various times if animations or multiple steps layout building) every user interaction.

    Hope this helps clarifying the issue. If you need a complex renderer maybe you can experiment identifying the current animation frame, or end of animation, and make the renderer just return null when it is not really needed -- for example you can let the chart animation play and in the last call let it render the custom/complex entities on the chart. You'll see it animate normally and in the end the sprites will show up.
    Fabrício Murta
    Developer & Support Expert
  5. #5
    Hello @mirwais!

    Been some time already since we last replied to this thread and no feedback from your side. Do you still need help on this issue? We may mark this thread as closed if you don't reply in an additional 7+ days, but that won't prevent you from posting a feedback here whenever you feel fit.
    Fabrício Murta
    Developer & Support Expert

Similar Threads

  1. Replies: 1
    Last Post: Nov 07, 2013, 8:27 PM
  2. [CLOSED] Renderer on Tips of Series of Chart in code behind
    By reinout.mechant@imprss.be in forum 2.x Legacy Premium Help
    Replies: 5
    Last Post: Jun 21, 2013, 3:29 PM
  3. Replies: 1
    Last Post: Jun 18, 2013, 3:56 PM
  4. [OPEN] [#77] Chart legend problems with large amount of series
    By MWM2Dev in forum 2.x Legacy Premium Help
    Replies: 14
    Last Post: Dec 21, 2012, 4:23 AM
  5. Replies: 4
    Last Post: Oct 11, 2010, 3:39 PM

Tags for this Thread

Posting Permissions