简体   繁体   中英

Variable caching in AngularJS (factory)

I have the following controller in my application, but there is some strange behaviour that I cannot explain. I've numbered two of the lines to help with the description, they don't both exist at the same time in the live code.

var app = angular.module('movieListings', ['ngResource', 'ngRoute', 'ui.bootstrap', 'ng']);

var cachedMovieList = []; 

//Controller for movie list
app.controller('MovieListController', ['$http', function($http){
    var mlc = this; //needed for the $http request
    this.movies = cachedMovieList;    
    this.loaded = false;
    this.error = false;


    if(this.movies.length == 0) {            
        console.log("Grabbing new movie list from DB");
        $http.get('data/movies.json').success(function(data){
            mlc.movies = data; 
            mlc.loaded = true;
            cachedMovieList = data; //(1)
        }).error(function(data){
            mlc.error = true;
        });
        cachedMovieList = this.movies; //(2)
    } else {
        this.loaded = true;        
    }
}]);

With the code as above with line (1) present and line (2) not present, I am able to cache the result so that when I flick between pages I don't need to constantly re-get the data.

However if I remove line (1) and insert line (2), the variable "cachedMovieList" is never populated. I would expect it to be based on the fact that "mlc.movies" was assigned to... but I cannot understand why this is the case?

Any advice welcome.

If I've understood this correct, you're entering the if condition only when this.movies.length == 0 . In such a case, this.movies will be null, so cachedMovieList would get populated with a null value.

Because (2) probably gets executed first before the $http.get() request is finished. $http.get() is an AJAX request.

If you want to cache, you might want to use $cacheFactory instead :)

I believe you are mistaking the live updation of values that happens in view to live updation that would happen with variable assignments. Your line 2 will set cachedMovieList to [] initially. I believe that is quite obvious. But you think that since callback updates this.movies that change would cascade to cachedMovieList. That won't happen as you are re-assigning the mlc.movies variable that means it refer to new variable instead of modifying existing value.

If you really want to make you logic work, please update mlc.movies variables like following

mlc.length = 0 // Empty the array
mlc.push.apply(mlc, data);

Please check following answer for more information

How do I empty an array in JavaScript?

Implement a factory that retrieves the data. Use angular.copy to preserve the array reference when the data returns from the $http call.

 var app = angular.module('movieListings', ['ngResource', 'ngRoute', 'ui.bootstrap', 'ng']);
 app.factory('movies', function($http) {
     var movies = {
         data: [],
         loaded: false,
         error: false
     };

     $http.get('data/movies.json').success(function(data){
        angular.copy(data, movies.data); 
           movies.loaded = true;
        }).error(function(data){
           movies.error = true;
        });
     return movies;

 });

Inject the factory into your controller:

//Controller for movie list
app.controller('MovieListController', ['$scope','movies', function($scope, movies){
     this.movies = movies;
}]);

Factories (like services) are singletons. They are initialized once, and cached for the entire lifetime of the SPA.

Use the controller in the view:

 <div ng-controller="MovieListController as ctrl">
       <div ng-show="!ctrl.movies.loaded"> Loading... </div>
       <ul>
            <li ng-repeat="movie in ctrl.movies.data">
                {{ movie.name }}
            </li>
       </ul>
 </div>

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