1. #1
    Sencha User johnmmcparland's Avatar
    Join Date
    Feb 2013
    Location
    Scotland
    Posts
    9
    Vote Rating
    0
    johnmmcparland is on a distinguished road

      0  

    Default Answered: How do I dynamically update a chart from JSON response?

    Answered: How do I dynamically update a chart from JSON response?


    Hi all,

    I'm trying to make a column chart update it's data series after values are returned from the server. However I am struggling to accomplish this, especially when using the ExtJS MVC architecture.

    I have a small example program where a user enters some values into a form then hits submit. The values are used by the server (Spring MVC based) to perform calculations which are returned to the ExtJS client.

    The ExtJS client should then update a column chart, using the values returned from the server.

    The form and chart are displayed on the GUI at all times. I.e.




    So far I've managed to;
    1. Get the form values sent to the server
    2. Perform the calculations on the server
    3. Return the results to the client via JSON
    the one bit I cannot manage is updating the chart's Store to display the returned calculations.

    My controller looks as follows;


    Code:
    Ext.require('FMCoachRoles.store.AssignmentStore');
    
    Ext.define('FMCoachRoles.controller.CalculatorController', {
        extend : 'Ext.app.Controller',
    
        stores: ['AssignmentStore'],
    
        // Initialize!
        init : function() {
            this.control({
                'nonPlayerBox button[action=calculate]' : {
                    click : this.calculate
                }
            });
        },
    
        // Calculate
        calculate : function(button) {
            console.log("Calculating...");
            var form = button.up('form').getForm();// get the basic form
    
            if (form.isValid()) { // make sure the form contains valid data before
                console.log("Form is valid: " + form);
                form.add
                // submitting
                form.submit({
                    // success is based on the "success" property returned by
                    // CalculatorController.java
                    success:function (form, action) {
                        //console.log(action);
                        console.log(action.result);
                        loadAssignmentsData(action.result);
                        //console.log(action.result.msg);
                        Ext.Msg.alert('Success', 'Look at the recommended assignments');
                    },
                    // failure is based on the "success" property returned by
                    // AuthenticationController.java
                    failure:function (form, action) {
                        console.log(action.result);
                        var msg = action.result.msg;
                        Ext.Msg.alert('Error', 'Sorry there was a problem determining the coach assignments');
                    }
                });
            } else { // display error alert if the data is invalid
                Ext.Msg.alert('Invalid Data', 'Please correct form errors.');
            }
        },
    
        loadAssignmentsData: function(result) {
            this.getAssignmentStoreStore().loadData(result);
        }
    });
    and the chart is;

    Code:
    Ext.require('Ext.chart.*');
    Ext.require('FMCoachRoles.store.AssignmentStore');
    
    Ext.define('FMCoachRoles.view.CoachAssignmentChart', {
        extend:'Ext.chart.Chart',
        alias:['widget.coachAssignmentChart'],
    
        width:800,
        height:600,
        animate:true,
        store: 'AssignmentStore',
        shadow:true,
        axes:[
            {
                type:'Numeric',
                position:'left',
                fields:['stars'],
                title:'Stars',
                grid:true,
                minimum:0.0,
                maximum: 5.0
            },
            {
                type:'Category',
                position:'bottom',
                fields:['role'],
                title:'Role'
            }
        ],
        series:[
            {
                type:'column',
                axis:'bottom',
                highlight:true,
                xField:'role',
                yField:'stars',
                tips: {
                    trackMouse: true,
                    width: 75,
                    height: 30,
                    renderer: function(storeItem, item) {
                        this.setTitle(storeItem.get('stars') + ' stars');
                    }
                }
            }
        ]
    
    })
    ;
    The whole project is available on Google Code See the app folder for the ExtJS code.

    Any help would be much appreciated.
    Attached Images

  2. There is no alias config for a store that I know of by the way...you need to use storeId.

    Anyway...

    I made this test case for you....just quick simple code. Just put extjs-4.1.3 in your web server then unzip the attachment in your extjs-4.1.3/examples folder and this should work.

    You can change the form values and save and the chart will update...maybe this will help you.

  3. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,649
    Vote Rating
    898
    Answers
    3575
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    All you should have to do is update the store bound to the chart.
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  4. #3
    Sencha User johnmmcparland's Avatar
    Join Date
    Feb 2013
    Location
    Scotland
    Posts
    9
    Vote Rating
    0
    johnmmcparland is on a distinguished road

      0  

    Default


    Quote Originally Posted by mitchellsimoens View Post
    All you should have to do is update the store bound to the chart.
    Thanks Mitchell. Conceptually yes I see that's what I must do, but I don't know how to. I don't know how to get a reference to the chart store in the controller.

  5. #4
    Sencha Premium Member
    Join Date
    Dec 2012
    Location
    Sunnyvale, CA
    Posts
    180
    Vote Rating
    20
    Answers
    16
    MikeRH will become famous soon enough MikeRH will become famous soon enough

      0  

    Default


    Well first thing...you are not calling your loadAssignmentsData function properly.

    You have:

    PHP Code:
    success:function (formaction) {
        
    //console.log(action);
        
    console.log(action.result);
        
    loadAssignmentsData(action.result);
        
    //console.log(action.result.msg);
        
    Ext.Msg.alert('Success''Look at the recommended assignments');

    However you lost scope there. You should have

    PHP Code:
    success:function (formaction) {
        
    //console.log(action);
        
    console.log(action.result);
        
    this.loadAssignmentsData(action.result);
        
    //console.log(action.result.msg);
        
    Ext.Msg.alert('Success''Look at the recommended assignments');

    Try that and post back.

  6. #5
    Sencha User johnmmcparland's Avatar
    Join Date
    Feb 2013
    Location
    Scotland
    Posts
    9
    Vote Rating
    0
    johnmmcparland is on a distinguished road

      0  

    Default


    Quote Originally Posted by MikeRH View Post
    Well first thing...you are not calling your loadAssignmentsData function properly.

    You have:

    PHP Code:
    success:function (formaction) {
        
    //console.log(action);
        
    console.log(action.result);
        
    loadAssignmentsData(action.result);
        
    //console.log(action.result.msg);
        
    Ext.Msg.alert('Success''Look at the recommended assignments');

    However you lost scope there. You should have

    PHP Code:
    success:function (formaction) {
        
    //console.log(action);
        
    console.log(action.result);
        
    this.loadAssignmentsData(action.result);
        
    //console.log(action.result.msg);
        
    Ext.Msg.alert('Success''Look at the recommended assignments');

    Try that and post back.
    Thanks for the reply Mike. I tried that however the scope of "this" within the success:function() block is to the Ext.form.action.Submit rather than the controller. I get this error on the Chrome console.

    Code:
    Uncaught TypeError: Object [object Object] has no method 'loadAssignmentsData'
    and on Firefox with Firebug

    Code:
    TypeError: this.loadAssignmentsData is not a function
    this.loadAssignmentsData(action.result);
    How do I refer to the controller from within that function?

  7. #6
    Sencha Premium Member
    Join Date
    Dec 2012
    Location
    Sunnyvale, CA
    Posts
    180
    Vote Rating
    20
    Answers
    16
    MikeRH will become famous soon enough MikeRH will become famous soon enough

      0  

    Default


    Try
    PHP Code:
    form.submit({                
        
    // success is based on the "success" property returned by                
        // CalculatorController.java                
        
    scope:this,
        
    success:function (formaction) {
            
    //console.log(action);                    
            
    console.log(action.result);                    
            
    this.loadAssignmentsData(action.result);                    
            
    //console.log(action.result.msg);                    
            
    Ext.Msg.alert('Success''Look at the recommended assignments');                
         } 

  8. #7
    Sencha User johnmmcparland's Avatar
    Join Date
    Feb 2013
    Location
    Scotland
    Posts
    9
    Vote Rating
    0
    johnmmcparland is on a distinguished road

      0  

    Default


    Thanks Mike. I'm getting a bit further - now the call to this.loadAssignmentsData works however the chart doesn't reflect the change!

    I've tried a few things - adding the chart as a view of the controller (the application didn't load in the browser at all) and trying to reference it using Ext.getCmp('coachAssignmentChart'), which returned null.

    I think at the heart of the problem is that I do not understand how, by declaring configs like "stores: ['xxx']" how that can refer to the instance of the store which the chart is using? I cannot see how it all joins together.

    Here is the latest controller;

    app/controller/CalculatorController.js
    Code:
    Ext.require('FMCoachRoles.store.AssignmentStore');
    
    Ext.define('FMCoachRoles.controller.CalculatorController', {
        extend : 'Ext.app.Controller',
    
        stores: ['AssignmentStore'],
        views: ['CoachAssignmentChart'], // this doesn't seem to do anything
    
        // Initialize!
        init : function() {
            this.control({
                'nonPlayerBox button[action=calculate]' : {
                    click : this.calculate
                }
            });
        },
    
        // Calculate
        calculate : function(button) {
            console.log("Calculating...");
            var form = button.up('form').getForm();// get the basic form
    
            if (form.isValid()) { // make sure the form contains valid data before
                console.log("Form is valid: " + form);
                // submitting
                form.submit({
                    // success is based on the "success" property returned by
                    // CalculatorController.java
                    scope:this,
                    success:function (form, action) {
                        //console.log(action);
                        console.log(action.result);
                        console.log(this);
                        this.loadAssignmentsData(action.result);
                        //console.log(action.result.msg);
                        Ext.Msg.alert('Success', 'Look at the recommended assignments');
                    },
                    // failure is based on the "success" property returned by
                    // AuthenticationController.java
                    failure:function (form, action) {
                        console.log(action.result);
                        var msg = action.result.msg;
                        Ext.Msg.alert('Error', 'Sorry there was a problem determining the coach assignments');
                    }
                });
            } else { // display error alert if the data is invalid
                Ext.Msg.alert('Invalid Data', 'Please correct form errors.');
            }
        },
    
        loadAssignmentsData: function(result) {
            console.log("loadAssignmentsData called");
            this.getAssignmentStoreStore().loadData(result);
            this.getAssignmentStoreStore().datachanged;
            // var chart = Ext.getCmp('widget.CoachAssignmentChart'); // Seems to return null
            // chart.redraw(); // Error: 'chart is undefined'
            //this.getCoachAssignmentChartView().getAssignmentStoreStore().loadAssignmentsData(result); // Isn't happy that getCoachAssignmentCharView() is a chart
            //this.getCoachAssignmentChartView().redraw(); // Isn't happy that getCoachAssignmentCharView() is a chart
            console.log("loadAssignmentsData done");
        }
    });
    The main viewport (app/view/Viewport.js)
    Code:
    Ext.define('FMCoachRoles.view.Viewport', {
        extend : 'Ext.container.Viewport',
        requires : [
            'FMCoachRoles.view.CoachAssignmentChart',
            'FMCoachRoles.view.NonPlayerForm'
            ],
        layout : 'fit',
    
        initComponent : function() {
            this.items = {
                xtype : 'panel',
                title : 'FM Coach Roles',
                layout : 'column',
                // This is what's shown in the box
                items : [{
                    xtype : 'nonPlayerBox',
                    columnWidth: 0.25
                },
                    {
                        columnWidth: 0.75,
                        xtype: 'coachAssignmentChart'
                    }]
            };
            this.callParent();
        }
    });
    The form contained within the viewport (app/view/NonPlayerForm.js)
    Code:
    Ext.require('Ext.form.field.Number');
    
    Ext.define('FMCoachRoles.view.NonPlayerForm', {
        extend : 'Ext.form.Panel',
        alias : 'widget.nonPlayerBox',
        title : 'Non Player',
        width : 400,
        bodyPadding : 5,
        id: 'nonPlayerForm',
        fieldDefaults : {
            msgTarget : 'side',
            labelWidth : 100
        },
        defaultType : 'textfield',
        // NOTE: The URL maps to the fmcoachroles servlet in the web.xml
        // file!
        url : 'calculate.form',
        items : [ {
            fieldLabel : 'Attacking',
            name : 'attacking',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Defending',
            name : 'defending',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Fitness',
            name : 'fitness',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Goalkeepers',
            name : 'goalkeepers',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Man Management',
            name : 'manManagement',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Mental',
            name : 'mental',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Tactical',
            name : 'tactical',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Technical',
            name : 'technical',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Working With Youngsters',
            name : 'workingWithYoungsters',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Adaptability',
            name : 'adaptability',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Determination',
            name : 'determination',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Judging Player Ability',
            name : 'judgingPlayerAbility',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Judging Player Potential',
            name : 'judgingPlayerPotential',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Level of Discipline',
            name : 'levelOfDiscipline',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Motivating',
            name : 'motivating',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Physiotherapy',
            name : 'physiotherapy',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }, {
            fieldLabel : 'Tactical Knowledge',
            name : 'tacticalKnowledge',
            xtype : 'numberfield',
            value : 1,
            minValue : 1,
            maxValue : 20
        }
    
        ],
        buttons : [ {
            text : 'Calculate',
            action : 'calculate'
        }, {
            text : 'Reset'
        } ]
    });
    The chart contained in the Viewport (app/view/CoachAssignmentChart.js)
    Code:
    Ext.require('Ext.chart.*');
    Ext.require('FMCoachRoles.store.AssignmentStore');
    
    Ext.define('FMCoachRoles.view.CoachAssignmentChart', {
        extend:'Ext.chart.Chart',
        alias:['widget.coachAssignmentChart'],
    
        width:800,
        height:600,
        animate:true,
        store: 'AssignmentStore',
        shadow:true,
        axes:[
            {
                type:'Numeric',
                position:'left',
                fields:['stars'],
                title:'Stars',
                grid:true,
                minimum:0.0,
                maximum: 5.0
            },
            {
                type:'Category',
                position:'bottom',
                fields:['role'],
                title:'Role'
            }
        ],
        series:[
            {
                type:'column',
                axis:'bottom',
                highlight:true,
                xField:'role',
                yField:'stars',
                tips: {
                    trackMouse: true,
                    width: 75,
                    height: 30,
                    renderer: function(storeItem, item) {
                        this.setTitle(storeItem.get('stars') + ' stars');
                    }
                }
            }
        ]
    
    })
    ;
    So how do these things tie together? Apologies, I'm coming from a Java/Spring background, so the lack of passing instances around is a little weird.

  9. #8
    Sencha Premium Member
    Join Date
    Dec 2012
    Location
    Sunnyvale, CA
    Posts
    180
    Vote Rating
    20
    Answers
    16
    MikeRH will become famous soon enough MikeRH will become famous soon enough

      0  

    Default


    What does your store and model code look like?

  10. #9
    Sencha User johnmmcparland's Avatar
    Join Date
    Feb 2013
    Location
    Scotland
    Posts
    9
    Vote Rating
    0
    johnmmcparland is on a distinguished road

      0  

    Default


    Quote Originally Posted by MikeRH View Post
    What does your store and model code look like?
    Store (app/store/AssignmentStore.js)
    [CODE]
    Ext.define('FMCoachRoles.store.AssignmentStore', {
    extend:'Ext.data.Store',
    model:'FMCoachRoles.model.Assignment',
    alias: 'store.assignmentStore',
    autoLoad:false,
    // http://docs.sencha.com/ext-js/4-1/#!/api/Ext.data.Store-event-load
    proxy:{
    type:'ajax',
    //api:{
    // read:'data/assignments.json'
    //},
    reader:{
    type:'json',
    root:'assignments',
    successProperty:'success'
    }
    },
    sorters:[
    { property:'role'
    }
    ]
    });

    Model (app/model/Assignment.js)
    Code:
    Ext.define('FMCoachRoles.model.Assignment', {
       extend: 'Ext.data.Model',
    
        fields: [
            {name: 'role', type: 'string'},
            {name: 'stars', type: 'number'}
        ]
    
    });
    And finally app.js
    Code:
    Ext.application({
    
        requires: ['FMCoachRoles.view.CoachAssignmentChart', 'FMCoachRoles.store.AssignmentStore'],
    
        appFolder: 'app',
        controllers: ['CalculatorController'],
        stores: ['AssignmentStore'],
    
        name: 'FMCoachRoles',
        autoCreateViewport: true,
    
        launch: function() {
            console.log("Launching " + this.name);
        }
    });
    FYI the project is hosted on Google Code. You can see the ExtJS code here: https://code.google.com/p/fmcoachrol...Fmain%2Fwebapp

    And the whole project (which contains a Spring MVC backend) here: https://code.google.com/p/fmcoachroles/source/browse/

  11. #10
    Sencha Premium Member
    Join Date
    Dec 2012
    Location
    Sunnyvale, CA
    Posts
    180
    Vote Rating
    20
    Answers
    16
    MikeRH will become famous soon enough MikeRH will become famous soon enough

      1  

    Default


    There is no alias config for a store that I know of by the way...you need to use storeId.

    Anyway...

    I made this test case for you....just quick simple code. Just put extjs-4.1.3 in your web server then unzip the attachment in your extjs-4.1.3/examples folder and this should work.

    You can change the form values and save and the chart will update...maybe this will help you.
    Attached Files

Thread Participants: 3

Tags for this Thread