KevInSol
KevInSol

Reputation: 2610

angular controller needing refresh to pickup new data in a factory promise

I have a pretty standard app which will display news items from a remote JSON feed. So basically I have decided to poll the remote server and store the JSON in localStorage (to enable offline usage). For the moment, I have a manual page/view I must click on to update the localStorage , this works fine.

The problem is that after I use my temporary manual update page, I then go to the news page/view and it is not updated. To view the current JSON contents I must hit refresh (while still developing in the browser.)

I'm totally new to Angular and have tried to find solutions to this myself - $watch or reload: true seem to be suggested as fixes, but I cannot get them to work in my case.

Route

  .state('tab.news', {
      url: '/news',
      reload: true,
      views: {
        'news-tab': {
          templateUrl: 'templates/news_home.html',
          controller: 'newsCtrl'
        }
      }
    })

factory

angular.module('schoolApp.services', [])

    .factory('newsService', function($q) {

        var newsHeadlines =localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail status
        var newsHeadlinesObj = JSON.parse(newsHeadlines);// convert to an object 

        console.log("factory newsService ran");
        return {
            findAll: function() {
                var deferred = $q.defer();
                deferred.resolve(newsHeadlinesObj);
                return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
            },

            findById: function(newsId) {
                var deferred = $q.defer();
                var newsItem = newsHeadlinesObj[newsId];
                deferred.resolve(newsItem);
                return deferred.promise;
            }
          }   
    });

Controller

schoolApp.controller('newsCtrl', function($scope, newsService) {
  console.log ( 'newsCtrl ran' );

    newsService.findAll().then(function (newsHeadlinesObj) {
            $scope.newsHeadlinesObj = newsHeadlinesObj;
         }, function(error){
          console.log(error)
         });

})

Looking at my console, the first time I read the news, the factory then controller run, but if I go to pull more data down, then go hack to news, only the controller runs, unless I refresh, then both run again.

I do not need the news view to update 'live' while still on it (but if that can be easilly done all the better) - just to pick up new data when you go back to news after being elsewhere in the app.

Thank you.

Upvotes: 1

Views: 3493

Answers (2)

Fernando Carvajal
Fernando Carvajal

Reputation: 1945

I solved this withouut promises, I just used $rootScope in the factory and $scope.$on in the controller; when I change the factory, i use $rootScope.$broadcast to tell the controller that I change it.

.factory('dataFactory', ['$http', '$rootScope', function ($http, $rootScope) {
    var dataFactory = {
        stock: null,
        getStock: getStock
    }

    function getStock() {
        $http.get("/api/itemfarmacia/").then(function success(res) {
            dataFactory.stock = res.data;
            $rootScope.$broadcast('changingStock'); //Ones who listen this will see it
        }, function error(err) {
            console.log("Bad request");
        })
    }

    return dataFactory;
}])

and in the controller

.controller('atencion', ["$scope", "$state", "dataFactory", function ($scope, $state, dataFactory) {
    $scope.stock = dataFactory.stock; //At first is null
    dataFactory.getStock(); //wherever you execute this, $scope.stock will change

    $scope.$on('changingStock', function () {//Listening
        $scope.stock = dataFactory.stock; //Updating $scope
    })
}])

Upvotes: 1

ChevCast
ChevCast

Reputation: 59213

Factories return singletons and only run once. The object newsService is cached by angular. The var declarations for newsHeadlines and newsHeadlinesObj will only ever run once; meaning your promise returning methods will always resolve the promise with the same data that was retrieved when your factory was first instantiated. You should put them in a function and call it from your find methods on the singleton object.

.factory('newsService', function($q) {

    function getHeadlines() {
        var newsHeadlines = localStorage.getItem('newsHeadlines') || '{"status":"READFAIL"}'; // get news as a JSON string. if newsHeadlines not found return a JSON string with fail
        return JSON.parse(newsHeadlines);// convert to an object
    }

    return {
        findAll: function() {
            var headlines = getHeadlines();
            var deferred = $q.defer();
            deferred.resolve(headlines);
            return deferred.promise; // or reject(reason) to throw an error in the controller https://docs.angularjs.org/api/ng/service/$q
        },

        findById: function(newsId) {
            var headlines = getHeadlines();
            var deferred = $q.defer();
            var newsItem = headlines[newsId];
            deferred.resolve(newsItem);
            return deferred.promise;
        }
      }   
});

PS - I'm sure you know and are planning to do things differently later or something, but just in case you don't: Using promises here is pointless and you have no need for $q here. You could simply return the data instead of returning the promises.

Upvotes: 3

Related Questions