How to properly convert controller and service from AngularJS to using ng-resource
Asked Answered
M

2

10

I only started learning ng-resource recently. I'm wondering if I am on the right track when it comes to converting my regular AngularJS application to one which uses ng-resource.

This is my old home.js controller (without using ng-resource):

angular.module("HomeApp", ["BaseApp"])
    .controller("MainCtrl", ["$http", "$window", "BaseService", function($http, $window, BaseService) {

        var self = this;

        BaseService.fetch.posts(function() {
            self.posts = BaseService.posts;
            self.cerrorMessages = BaseService.cerrorMessages;
        });

        self.like = function(id, postType) {
            BaseService.like(id, postType, function() {
                self.cerrorMessages = BaseService.cerrorMessages;
            });
        };
    }]);

And this is my old base.js (BaseService):

angular.module("BaseApp", [])
    .factory("BaseService", ["$http", "$window", function($http, $window) {
        var self = this;
        self.posts = [];
        self.cerrorMessages = [];
        self.accessErrors = function(data) {
             self.cerrorMessages = [];
             for (prop in data) {
                 if (data.hasOwnProperty(prop)){
                     self.cerrorMessages.push(data[prop]);
                 }
             }
         };

        self.fetch = {
             posts: function(callback) {
                 $http.get('/posts/')
                 .then(function(response) {
                     self.posts = response.data;
                     callback();
                 }, function(response) {
                     self.accessErrors(response.data);
                     callback();
                 });
             }
         };

        self.like = function(id, postType, callback) {
            $http.post("/" + postType + "/" + id + "/like/")
            .then(function(response) {
                angular.forEach(self.posts, function(post, index, obj) {
                    if (post.id == id) {
                        post.usersVoted.push('voted');
                        post.voted=true;
                    };
                });
            }, function(response) {
                 self.accessErrors(response.data);
                 callback();
            });
        };

        return self;
    }]);

I was told that I can greatly benefit from using ng-resources since I have a RESTful backend. I tried converting the code above to start using ng-resource, and this is what I have now. This is my resources.js:

angular.module('PostsResources', ['ngResource'])

.factory('PostsFactory', function($resource) {
  return $resource('/posts/:postId', { postId:'@id' });
});

.factory('PostLikeFactory', function($resource) {
  return $resource('/posts/:postId/like', { postId:'@id' });
});

And this is my controller (home.js):

angular.module("HomePageApp", ["BaseApp", "PostsApp"])
    .controller("MainCtrl", ["$http", "$window", "BaseService", "PostsResource", "PostLikeResource", function($http, $window, BaseService, PostsResource, PostLikeResource) {

var self = this;

function loadPosts() {
    self.posts = PostsFactory.query(function(data) {},
        function(error) {
          BaseService.accessErrors(error.data);
          self.cerrorMessages = BaseService.cerrorMessages;
        }
    );
};

self.like = function likePost() {
    PostLikeFactory.save(function(data) {
        angular.forEach(self.posts, function(post, index, obj) {
            if (post.id == id) {
                post.usersVoted.push('voted');
                post.voted=true;
            };
        });
    }, function(error) {
        BaseService.accessErrors(error.data);
        self.cerrorMessages = BaseService.cerrorMessages;
    }
)};

And this is my BaseService (base.js):

angular.module("BaseApp", [])
    .factory("BaseService", ["$http", "$window", function($http, $window) {
        var self = this;
        self.posts = [];
        self.cerrorMessages = [];
        self.accessErrors = function(data) {
             self.cerrorMessages = [];
             for (prop in data) {
                 if (data.hasOwnProperty(prop)){
                     self.cerrorMessages.push(data[prop]);
                 }
             }
         };
        return self;
    }]);

Am I on the right track of converting a regular AngularJS app to using ng-resource? To me it seems like the amount of code required looks the same. Thanks in advance!

Merissa answered 24/11, 2015 at 2:33 Comment(1)
Main point is: User = $resource('/user') -> User.get(), User.post, .... If you use only one method for each url - there is no much profit from ng-resource.Nernst
A
1

I agree with @Mark that you probably don't have enough operations in your API to fully take advantage of $resource, so the benefits aren't as great. For what you have now though, if you're wanting to use $resource, I would set it up like this:

angular.module('PostsResources', ['ngResource'])
   .factory('Post', function($resource) {
      return $resource('/posts/:postId', { postId:'@id' }, {
         like: {
            url: '/posts/:postId/like',
            method: 'POST',
            params: { postId:'@id' }
         }
      })
   });

If you get to where you're using more than one action per endpoint, I would have a Post and PostLike resource, but in your case, that's not necessary.

In your controller, you'll be able to work with your posts like this:

self.posts = Post.query(null, BaseService.errorHandler(self));

self.like = function(post) {
   post.$like(function() {
      angular.forEach(self.posts, function(post, index, obj) {
            if (post.id == id) {
                post.usersVoted.push('voted');
                post.voted=true;
            };
        });
   }, BaseService.errorHandler(self));
};

The errorHandler method you could add to your BaseService to get rid of some duplication. It would look something like this:

self.errorHandler = function(viewModel) {
   return function(error) {
      BaseService.accessErrors(error.data);
      viewModel.cerrorMessages = BaseService.cerrorMessages;
   };
};
Asperity answered 1/12, 2015 at 19:53 Comment(0)
V
4

Generally, I only use ng-resource when I have complete CRUD web applications. Example would be a CRM kind of app, where you need to create customers, update customers, delete customers and show a list of customers. It makes sense to use ng-resource as it "handles" the get, post, put, and delete HTTP methods for you.

However, in your example, it seems like the only actions you have is "Like" and "Getting a list of Posts". If you are ONLY going to use one of the HTTP methods, for each resource type, then I'd suggest just sticking to your old way of using the BaseService.

Just my 2 cents on this matter.

Visional answered 26/11, 2015 at 17:58 Comment(0)
A
1

I agree with @Mark that you probably don't have enough operations in your API to fully take advantage of $resource, so the benefits aren't as great. For what you have now though, if you're wanting to use $resource, I would set it up like this:

angular.module('PostsResources', ['ngResource'])
   .factory('Post', function($resource) {
      return $resource('/posts/:postId', { postId:'@id' }, {
         like: {
            url: '/posts/:postId/like',
            method: 'POST',
            params: { postId:'@id' }
         }
      })
   });

If you get to where you're using more than one action per endpoint, I would have a Post and PostLike resource, but in your case, that's not necessary.

In your controller, you'll be able to work with your posts like this:

self.posts = Post.query(null, BaseService.errorHandler(self));

self.like = function(post) {
   post.$like(function() {
      angular.forEach(self.posts, function(post, index, obj) {
            if (post.id == id) {
                post.usersVoted.push('voted');
                post.voted=true;
            };
        });
   }, BaseService.errorHandler(self));
};

The errorHandler method you could add to your BaseService to get rid of some duplication. It would look something like this:

self.errorHandler = function(viewModel) {
   return function(error) {
      BaseService.accessErrors(error.data);
      viewModel.cerrorMessages = BaseService.cerrorMessages;
   };
};
Asperity answered 1/12, 2015 at 19:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.