Search code examples
jqueryajaxpromisejquery-deferredrevealing-module-pattern

Unable to return an object from a .fail() callback?


I am trying to return an object through the _fail callback (Yes, this is meant to run the fail callback), but failSeries is returning as undefined in the console. Any ideas?

var ChartModule = (function ( $, HC, HA, window, undefined) {

//Define public object
var pub = {};

//Private methods
var _getChartData = function (URL, sendData) {

    var xhrObject =  $.ajax({
                            url: URL,
                            type: "POST",
                            data: sendData,
                            success: function (result) { },
                            error: function (jqXHR, textStatus, errorThrown) {}
                        });

    _chartDataResponse(xhrObject);
};

var _done = function (data, textStatus, jqXHR) {
    var seriesObject = $.parseJSON(data);
    return seriesObject;
};

var _fail = function(){
    var failSeries = [];
    var seriesData = {
        data: [{y: 7, id: 'pointAssets', color: '#5c8fb8'}, {y:10, id: 'pointLiabilities', color: '#bb77b5'}, {y:-3, id: 'pointResult', color: '#cc5971'}],
    };
    failSeries.push(seriesData);

    return failSeries;
};

var _chartDataResponse = function(xhrObject){
    xhrObject.then(_done, _fail);
};

var _renderChart = function(renderTo, seriesObject){
    console.log("Chart will be rendered to: '" + renderTo + "'");
    console.log(seriesObject);
};

//Public methods
pub.getChartData = _getChartData;
pub.renderChart = _renderChart;

return pub;
})(jQuery, Highcharts, HighchartsAdapter, window, undefined);

I am then using my module in the following way in an attempt to view the object that has been returned by either the success or fail callabcks:

$(function(){
var x = ChartModule.getChartData("someURL", {test: "test"});
ChartModule.renderChart("breakdown-chart", x);

});


Solution

  • With a slightly better understanding (and use) of $.ajax(), ChartModule will reduce down to far fewer public methods. In particular :

    • be aware that $ajax() returns a promise, which can (and should) itself be returned by getChartData() thus allowing consequential action on success/failure to be taken externally.
    • with appropriare low level error handling in a chained then() inside getChartData() you can make an error message AND the fake seriesData available where ChartModule.getChartData() is called.
    var ChartModule = (function ( $, HC, HA, window, undefined) {
        //Private members
        var fakeSeriesData = { data: [{y: 7, id: 'pointAssets', color: '#5c8fb8'}, {y:10, id: 'pointLiabilities', color: '#bb77b5'}, {y:-3, id: 'pointResult', color: '#cc5971'}] };
        function _getChartData(URL, sendData) {
            return $.ajax({
                url: URL,
                type: "POST",
                data: sendData,
                dataType: 'json'
            }).then(null, function (jqXHR, textStatus, errorThrown) {
                var e = new Error(textStatus);
                e.seriesData = getFakeSeriesData();
                return e; //return an augmented Error object.
            });
        }
        function _renderChart(renderTo, seriesObject) {
            console.log("Chart will be rendered to: '" + renderTo + "'");
            console.dir(seriesObject);
            return ...;//something meaningful
        }
        function _getFakeSeriesData() {
            return $.extend({}, fakeSeriesData);
        }
        //Public methods
        return {
            getChartData: _getChartData,
            renderChart: _renderChart,
            getFakeSeriesData: _getFakeSeriesData//make the fake series data directly availalbe.
        };
    })(jQuery, Highcharts, HighchartsAdapter, window, undefined);
    

    This strategy will allow control to be execised where ChartModule.getChartData() is called, with no assumptions about what actions might be taken on success or failure of the AJAX other than, on error, to make the fake seriesData available with no knowledge of how/whether it will be used.

    Call as follows :

    $(function() {
        ChartModule.getChartData("someURL", {
            test: "test"
        }).done(function(seriesData) {
            ChartModule.renderChart("breakdown-chart", seriesData);
        }).fail(function(error) {
            if(error.seriesData) {
                console.log(error.message);
                ChartModule.renderChart("breakdown-chart", error.seriesData);
            } else {
                console.error(error);
                //some othe couse of action
            }
        });
    });
    

    At some other point in your code, you could call ChartModule.getChartData() and take completely different actions in response. You could, on error, even inject some other fake seriesData that is, for whatever reason, more appropriate than the default.