0

My html is this:

<body ng-controller="MainCtrl as ctrl" class="bodyContainer">
<div ng-repeat="stuff in ctrl.stuffies">
    here
</div>

This is my controller:

angular.module("AuthenticationApp", ["BaseApp"])
    .controller("MainCtrl", ["$http", "$window", "BaseService", function($http, $window, BaseService) {
        var self = this;

        BaseService.fetch.stuffs(function() {
            self.stuffies = BaseService.stuffies;
            console.log(self.stuffies);
            self.cerrorMessages = BaseService.cerrorMessages;
        });
    }]);

And this is my BaseApp:

angular.module("BaseApp", [])
    .config(['$httpProvider', function($httpProvider) {
        $httpProvider.defaults.xsrfCookieName = 'csrftoken';
        $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
    }])

    .config(['$locationProvider', function($locationProvider){
        $locationProvider.html5Mode(true);
    }])

    .factory("BaseService", ["$http", "$window", function($http, $window) {
        var self = this;


        /* All functions which call fetch should have a callback function
         * on the front-end which sets 
         * 1) a variable (i.e. stuffies etc.)
         * 2) and BaseService.cerrorMessages. */
        self.fetch = {
             stuff: function(callback) {
                 $http.get("/stuffs/")
                 .then(function(response) {
                     self.stuffies = response.data;
                     callback();
                 }, function(response) {
                     self.accessErrors(response.data);
                     callback();
                 });
             }
        };

The problem is, even though it logs an item inside self.stuffies (when this line is run: console.log(self.stuffies);), here is not printed in the HTML. There doesn't seem to be anything inside ctrl.stuffies. I tried moving console.log(self.stuffies); outside of BaseService.fetch.stuffs(function() and it logs undefined. How do I get ctrl.stuffies to be BaseService.stuffies?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
SilentDev
  • 20,997
  • 28
  • 111
  • 214
  • Read [Why are Callbacks from Promise `.then` Methods an Anti-Pattern](http://stackoverflow.com/questions/35660881/why-are-callbacks-from-promise-then-methods-an-anti-pattern). – georgeawg Mar 22 '17 at 04:37

2 Answers2

1

You're using a factory like a service.

Have your factory self call (initialize) its self.fetch function, then upon construction of your controller, set the controller's stuffies variable equal to the factory's stuffies variable.

Edit:

If you only want some of the data initialized at once, you can self call only the functions you want to retrieve data initially.

You can then use a resolve in your routing to initialize more data when a specific route is hit.

Ben
  • 2,441
  • 1
  • 12
  • 16
  • But I only want the factory to fetch `stuffies` when it is needed (when a controller / `HTML` page asks for it). There are other variables (`stuffies2`, `stuffies3`, `stuffies4` etc.) which all need to be fetched as well for different `HTML` pages so I don't want my factory to fetch them all at the start (would only want them to be fetched when the `HTML` page which needs it is loaded). – SilentDev Mar 21 '17 at 23:45
  • Try resolving the factory method as a promise when the route changes to the route with your controller/template: http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx – Ben Mar 21 '17 at 23:49
1

There is no need to use callbacks with the $http service as it returns promises:

//.factory("BaseService", ["$http", "$window", function($http, $window) {
app.service("BaseService", ["$http", "$window", function($http, $window) {
    //var self = this;


    /* All functions which call fetch should have a callback function
     * on the front-end which sets 
     * 1) a variable (i.e. stuffies etc.)
     * 2) and BaseService.cerrorMessages. */
    this.fetch = {
         stuff: function() {
           //vvvv RETURN the promise
           return $http.get("/stuffs/")
             .then(function(response) {
                 //vvvv RETURN the data
                 return response.data;
                 //self.stuffies = response.data;
                 //callback();
           });//, function(response) {
             //    self.accessErrors(response.data);
             //    callback();
             //});
         }
    };
});

There is no need to do anything with $http errors in the service. Errors will be carried forward automatically in the promise.

Then in the controller extract the data (or the error) from the promise:

/* REPLACE
BaseService.fetch.stuffs(function() {
    self.stuffies = BaseService.stuffies;
    console.log(self.stuffies);
    self.cerrorMessages = BaseService.cerrorMessages;
});
*/


BaseService.fetch.stuffs
  .then(function(data) {
    self.stuffies = data;
    console.log(self.stuffies);
}).catch(function(errorResponse) {
    self.cerrorMessages = errorResponse.data;
});

For more information, see


I changed your service to a factory and made it this: and used it this way:

BaseService.fetch.stuffs() 
  .then(function(data) {
    self.stuffs = data;
    console.log(self.stuffs);  
}).catch(function(errorResponse) { 
    self.cerrorMessages = errorResponse.data; 
});

Is this fine (using factory because there are other functions inside the factory as well and I don't want to have to change them all to services)?


Sorry, the factory is

self.fetch = { 
    stuffs: function() { 
        return $http.get("/stuffs/")
          .then(function(response) {
              return response.data; 
        });
    }
};

Either a service or a factory will work. With a service, functions can be added by adding properties to the this context which is automatically returned (unless overridden with a return statement). With a factory, a return statement is manditory. Functions are added to the object returned. The choice is a question of style. They are both used the same way.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • I changed your service to a factory and made it this: `BaseService.fetch.stuffs() .then(function(data) { self.stuffs = data; console.log(self.stuffs); }).catch(function(errorResponse) { self.cerrorMessages = errorResponse.data; });` Is this fine (using factory because there are other functions inside the factory as well and I don't want to have to change them all to services)? – SilentDev Mar 25 '17 at 21:45
  • Sorry, the factory is `self.fetch = { stuffs: function() { return $http.get("/stuffs/") .then(function(response) { return response.data; }); } };` And the controller is what I posted in the comment above. – SilentDev Mar 25 '17 at 21:50
  • Would you be able to look at this question: http://stackoverflow.com/questions/43130404/how-to-correctly-override-errresponse-which-is-passed-to-the-catchfunctione and say how to fix the code without using a callback? (I know how to do it with a callback, but since you mentioned it is an Anti-Pattern, I just wanted to see your opinion) – SilentDev Mar 31 '17 at 01:09