Search code examples
jsonangularjsangularjs-factory

Angular JS correct factory structure


I have a factory in my AngularJS single page application that parses a given date against a JSON file to return season and week-number in season. I am currently calling the JSON file twice in each method $http.get('content/calendar.json').success(function(data) {....

How can i factor out the call to do it once regardless of how many methods?

    emmanuel.factory('DayService', function($http, $q){
    var obj = {};
        obj.season = function(d){
            // receives a mm/dd/yyyy string parses against Calendar service for liturgical season
            d = new Date(d);
            var day = d.getTime();
            var promise = $q.defer();
            var temp;
            $http.get('content/calendar.json').success(function(data) {
                for (var i=0; i<data.calendar.seasons.season.length; i++){
                    var start = new Date(data.calendar.seasons.season[i].start);
                    var end = new Date(data.calendar.seasons.season[i].end);
                    end.setHours(23,59);
                    //sets the time to be the last minute of the season
                    if (day >= start && day <= end){
                        //if given time fits within start and end dates in calendar then return season
                        temp = data.calendar.seasons.season[i].name;
                        //console.log(temp);
                        promise.resolve(temp);
                        break;
                    }
                }
            });
            return promise.promise;
        }
        obj.weekInSeason = function(d){
            //receives a date format mm/dd/yyyy
            var promise = $q.defer();
            $http.get('content/calendar.json').success(function(data) {
                    for (var i=0; i<data.calendar.seasons.season.length; i++){
                    d = new Date(d);
                    var day = d.getTime();
                    var end = new Date(data.calendar.seasons.season[i].end);
                    end.setHours(23,59);
                    end = end.getTime();
                    var diff = end - day;

                    if (parseFloat(diff) > 0){
                        var start = new Date(data.calendar.seasons.season[i].start);
                        start = start.getTime();
                        var startDiff = day - start;
                        var week = parseInt(startDiff /(1000*60*60*24*7))+1;
                        promise.resolve(week);
                        break;
                    } 
                }
            });
            return promise.promise;
        }
        obj.getData = function (d) {
            console.log('DayService.getData')
            console.log(today)
            var data = $q.all([
                this.season(d),
                this.weekInSeason(d)
            ]);
            return data;          
        };
    return obj;
});

Solution

  • This solution assumes that content/calendar.json never changes.

    I have answered a question which can help you in this problem one way or another. Basically you must fetch all necessary configurations/settings before the application bootstraps. Manually bootstrap the application, this means that you must remove the ng-app directive in your html.

    Steps:

    [1] Create bootstrapper.js as instructed in the answered question I have mentioned above. Basically, it should look like this(Note: You can add more configuration urls in urlMap, if you need to add more settings in your application before it bootstraps):

    angular.injector(['ng']).invoke(function($http, $q) {
    
      var urlMap = {
        $calendar: 'content/calendar.json'
      };
    
      var settings = {};
    
      var promises = [];
    
      var appConfig = angular.module('app.settings', []);
    
      angular.forEach(urlMap, function(url, key) {
        promises.push($http.get(url).success(function(data) {
          settings[key] = data;
        }));
      });
    
      $q.all(promises).then(function() {
        bootstrap(settings);
      }).catch(function() {
        bootstrap();
      });
    
      function bootstrap(settings) {
        appConfig.value('Settings', settings);
    
        angular.element(document).ready(function() {
          angular.bootstrap(document, ['app', 'app.settings']);
        });
      }
    
    });
    

    [2] Assuming that the name of your main module is app within app.js:

    angular.module('app', [])
    
    .factory('DayService', function(Settings){
        var calendar = Settings.$calendar,
            season = calendar.seasons.season,
            obj = {};
    
        obj.season = function(d){
            var day = new Date(d).getTime(),
                start, end, value;
    
            for (var i = 0; i < season.length; i++){
                start = new Date(season[i].start);
                end = new Date(season[i].end);
                end.setHours(23,59);
                if (day >= start && day <= end){
                    value = season[i].name;
                    break;
                }
            }
            return value;
        };
    
        obj.weekInSeason = function(d){
            var day = new Date(d).getTime(),
                end, diff, start, startDiff, week;
    
    
            for (var i = 0; i < season.length; i++){
                end = new Date(season[i].end);
                end.setHours(23,59);
                end = end.getTime();
                diff = end - day;
    
                if (parseFloat(diff) > 0){
                    start = new Date(season[i].start);
                    start = start.getTime();
                    startDiff = day - start;
                    week = parseInt(startDiff /(1000*60*60*24*7))+1;
                    break;
                } 
            }
    
            return week;
        };
    
        return obj;
    
    });
    

    [3] Controller Usage(Example):

    angular.module('app')
    
    .controller('SampleController', function(DayService) {
    
      console.log(DayService.season(3));
      console.log(DayService.weekInSeason(3));
    
    });
    

    Another Note: Use .run() to check if Settings === null - if this is true, you can direct to an error page or any page that displays the problem(This means that the application bootstrapped but one of the requested configuration failed).

    UPDATE:

    I checked the link you have provided, and it seems that the version you are using is AngularJS v1.0.8, which does not have a .catch() method in their $q promise implementation.

    You have the following options to consider in solving this problem:

    -1 Change the AngularJS version you are using to the latest stable version 1.2.23. Note that this option may break some of your code that is highly reliant on the version that you are using.

    -2 Change this block:

      $q.all(promises).then(function() {
        bootstrap(settings);
      }).catch(function() {
        bootstrap();
      });
    

    to:

      $q.all(promises).then(function() {
        bootstrap(settings);
      }, function() {
        bootstrap();
      });
    

    This option is safer if you already have existing code that relies on the current AngularJS version you are using But I would suggest you change to the current stable version as it has more facilities and fixes than the one you are currently using.