简体   繁体   中英

AngularJS app loading and processing JSON data once for multiple controllers

I'm working on an Angular app, where I'm running into mostly the same problem as in this post: AngularJS App: Load data from JSON once and use it in several controllers

I've got a factory that reads a JSON file, and returns the whole data object. Each controller, then, uses this factory (as a service?) to obtain the data, but then each controller has to pick it apart on its own. The JSON has to be searched and processed to get the relevant payload like, $scope.currentArray = data.someThing.allItems[i]; etc, and I obviously don't want to repeat this code in all the controllers.

Seems to me I can either find some way to share the data, after, say, MainController (the "first one") has finished working it, or I can add some new module "between" the controllers and the factory. This new module -- let's call it myProcessService ? -- would then be the one getting the data object from the factory, and do all the processing there... once and for all. Then, each controller would only deal with myProcessService to (somehow) get the ready-formatted variables and arrays etc onto their respective scopes (yes, this is Angular 1).

If I try to give an example of how I'm doing this so far, maybe someone can help me with the necessary improvements? And, I am aware that it is a good idea to begin using the Angular 2 patterns already today, but please understand that I am first trying to get some grasp of how A1 works, before delving into A2 :)

var app = angular.module('myApp', ['ngRoute']);


app.factory('getDataFile', ['$http', function($http) {
    function getStream(pid) {
        return $http.get("data/" + pid + ".json")
            .success(function(data) {
                    console.info("Found data for pid " + pid);
                    return data;
            })
            .error(function(err) {
                    console.error("Cant find data for pid " + pid);
                    return err;
            });
    }
    return {getStream: getStream};
}]);


app.controller('MainController', ['$scope', 'getDataFile', 
    function($scope, getDataFile) {     
        getDataFile.getStream('10101011').success(function(data) {

            // process "data" into what's relevant:
            var i = getRelevantIndexForToday(new Date());
            $scope.myVar = data.someField; 
            $scope.currentArray = data.someThing.allItems[i]; 
            // etc... you get the drift
        }
    }]);

app.controller('SecondController', ['$scope', 'getDataFile', 
    function($scope, getDataFile) {     
        getDataFile.getStream('10101011').success(function(data) {

            // process "data" into what's relevant:
            var i = getRelevantIndexForToday(new Date());
            $scope.myVar = data.someField; 
            $scope.currentArray = data.someThing.allItems[i]; 
            // etc... you get the drift
        }
    }]);

Edit:

My ngRouter is set up something like this. They fill the ng-view div in my index.html. However -- and maybe this is frowned upon? -- I've also got a "MainController" which sits directly in the index.html body tag, such that I can show some data (from the back end) in the header part of the single page application.

app.config(function($routeProvider) {

  $routeProvider

  .when('/:id/work/:page_id', {
    controller: 'AssetController',
    templateUrl: 'app/views/work.html'
  })
  .when('/:id/work/', {
    redirectTo: '/:id/work/1'
  })
  .when('/:id/', {
    controller: 'DashController',
    templateUrl: 'app/views/dashboard.html'
  })
  .otherwise({
    redirectTo: '/'
  });

});

and index.html is a lot like this:

<body ng-app="myApp">

    <div class="container" ng-controller="MainController">

        <h1>Welcome, {{username}}</h1>

        <div ng-view></div>

    </div>
</body>

You can add another helper function in your factory, that returns the required object that you want to share between controllers.

app.factory('getDataFile', ['$http', function($http) {
    function getStream(pid) {
        return $http.get("data/" + pid + ".json")
            .success(function(data) {
                    console.info("Found data for pid " + pid);
                    return data;
            })
            .error(function(err) {
                    console.error("Cant find data for pid " + pid);
                    return err;
            });
    }

    function getCurrent(pid) {
        return getStream(pid).then(function() {
                var i = getRelevantIndexForToday(new Date());

            return  {
                myVar: data.someField,
                currentArray: data.someThing.allItems[i];
            };
        });
    }

    return {
        getStream: getStream,
      getCurrent: getCurrent
    };
}]);

app.controller('MainController', ['$scope', 'getDataFile', 
    function($scope, getDataFile) {     
        getDataFile.getCurrent('10101011').success(function(data) {
            $scope.myVar = data.myVar; 
            $scope.currentArray = data.currentArray; 
            // etc... you get the drift
        }
    }]);

app.controller('SecondController', ['$scope', 'current', 
    function($scope, current) {     
        .success(function(data) {
                        $scope.myVar = data.myVar; 
            $scope.currentArray = data.currentArray;  
        }
    }]);

Suggestion:

Also I suggest you to use resolve which allows you to pass data to your controller from your route.

Route:

.when('/:id/work', {
    controller: 'AssetController',
    templateUrl: 'app/views/work.html',
     resolve: {
        // you are injecting current variable in the controller with the data. You can inject this to each of your controller. you dont need to add the whole function in your next route. Just use current
        current: function(getDataFile){
           return getDataFile.getCurrent('10101011');
        }
  })

Controller:

app.controller('MainController', ['$scope', 'current', 
    function($scope, current) {     
        $scope.myVar = current.myVar; 
        $scope.currentArray = current.currentArray;
    }]);

app.controller('SecondController', ['$scope', 'current', 
    function($scope, current) {     

        $scope.myVar = current.myVar; 
        $scope.currentArray = current.currentArray;  

    }]);

Now that you have

Thanks for giving an answer in line with my use of deprecated methods success and error , Subash. However, I had some problems with the code in your answer, and I got some help on #angularjs, so I thought I should post the updated code here.

var myApp = angular.module('myApp',[]);

myApp.factory('getDataFile', ['$http', function($http) {
    function getStream(pid) {
        // here, using placeholder data URL, just to get some data:
        return $http.get('http://jsonplaceholder.typicode.com/users')
            .then(function(result) {
                    console.info("Found data for pid " + pid);
                    return result.data;
            })
            .catch(function(err) {
                    console.error("Cant find data for pid " + pid);
                    return err;
            });
    }

    function getCurrent(pid) {
        return getStream(pid).then(function(data) {
            var i = 1;  // test
            console.log("myVar = ", data[i].name);
            return  {
                myVar: data[i].name
            };
        });
    }

    return {
        getStream: getStream,
      getCurrent: getCurrent
    };
}]);

myApp.controller('MainController', ['$scope', 'getDataFile', 
    function($scope, getDataFile) {     
        $scope.name = "j";
        getDataFile.getCurrent('10101011').then(function(user) {
            $scope.myVar = user.myVar;

            console.log("controller. myVar = ", user);
            // etc... you get the drift
        });
    }]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM