artis3n
artis3n

Reputation: 840

AngularJS $resource in service function

I'm working in a team that is building an Android application using web technologies (angular.js, etc.) and Phonegap to turn the project into an Android application. We're fairly new to AngularJS and have run into a problem integrating services into our code. We are trying to do some basic server calls, which are working as regular code, but we are trying to make them a service so we don't duplicate this all over the place. We're using a Phonegap localStorage plugin to store the ID of a database object on the phone's HTML5 local storage.

Here is our code:

.service("contactServer", function($resource, $http, baseUrl) {
    // Initialize resource and modify methods s.t. create POSTS and save PUTS.
    this.post = function() {
        alert("Starting post");
        var item = {"name": model.userName, "position": model.position};
        alert("Creating resource");
        var serverResource = $resource(baseUrl,
            {create: {method: "POST"}, save: {method: "PUT"}});
        alert("Created resource");
        new serverResource.create(item).then(function(data, status, headers, config) {
            alert("id: " + data._id);
            window.localStorage.setItem("DBid", data._id);
        }, function(data, status, headers, config) {
            alert(JSON.stringify(['Error', data, status, headers, config]))
        })
    }

    this.put = function() {
        alert("Starting put");
        var item = {"name": model.userName, "position": model.position, "id": window.localStorage.getItem("DBid")};
        alert("Creating resource");
        var serverResource = $resource(baseUrl + "/:id", {id: "@id"},
            {create: {method: "POST"}, save: {method: "PUT"}});
        alert("Created resource");
        new serverResource(item).save().then(function(data, status, headers, config) {
            alert(JSON.stringify(['Success', data, status, headers, config]));
        }, function(data, status, headers, config) {
            alert(JSON.stringify(['Error', data, status, headers, config]));
        })
    }
})

baseUrl is a URL link to our database. We call the services here:

.run(function(contactServer) {
    document.addEventListener("deviceready", onDeviceReady, false);

    function onDeviceReady() {

        if (window.localStorage.getItem("DBid") == null) {
            alert("no DBid");
            contactServer.post();
        }
        else {
            alert("Retrieved stored DBid: " + window.localStorage.getItem("DBid"));
            contactServer.put();
        }

    }
})

deviceready is a Phonegap event that fires when the application has loaded on the user's phone. We want to call these services in several of our controllers, but also initially during this run function.

The code fires up to the "starting post" alert after being called in the run function, but then breaks. Are we using $resource wrong? (It is correctly listed as a dependency). Are we implementing the service wrong?

Upvotes: 1

Views: 5047

Answers (4)

rahpuser
rahpuser

Reputation: 1249

The problem you have is the definition of your method change this

var serverResource = $resource(baseUrl,
            {create: {method: "POST"}, save: {method: "PUT"}});

Into this:

var serverResource = $resource(baseUrl, {},
            {create: {method: "POST"}, save: {method: "PUT"}});

Take a look at the documentation

So as yours methods are non Get instances you should execute them like following:

new serverResource.$create(item).then(function(data, status, headers, config) {
            //content
        }, function(data, status, headers, config) {
            //error content
        })

The documentation says:

HTTP GET "class" actions: Resource.action([parameters], [success], [error])

non-GET "class" actions: Resource.action([parameters], postData, [success], [error])

non-GET instance actions: instance.$action([parameters], [success], [error])

I would encourage you to use console.log instead of alerts to debug your code, the alert could create some problems with the digest cycle.

Let me give you an alternative solution:

.factory('contactServer', ['$resource', function($resource){
    var baseUrl = 'testing/:id';
    var resource = $resource(baseUrl, {id: '@id'}, {update: {method: 'PUT'}});
    return resource;
}]);

and you would use it as:

.run(function(contactServer) {
    document.addEventListener("deviceready", onDeviceReady, false);

    function onDeviceReady() {

        if (window.localStorage.getItem("DBid") == null) {
            alert("no DBid");
            //item creation
            var instance = new contactServer(item);
            contactServer.$save(item).then(function(res){
                 window.localStorage.setItem("DBid", res._id);
            });
        }
        else {
            alert("Retrieved stored DBid: " + window.localStorage.getItem("DBid"));
            var id = //theId;
            var updateObject = //item update;
            var instance = new contactServer();
            contactServer.$update({id: id}, updateObject).then(function(res){
                console.log('object updated!');
            });
        }

    }
});

..or something like that. Hope this help.

Upvotes: 2

Artemis
Artemis

Reputation: 4851

Actually it's interesting question.

As workaround I would suggest you to use Restangular if you want to build reusable instance for making http queries. In this case it has some advantages: - It uses promise instead of returning empty object and filling it with new data. (it's actually behaviour of ngResource). Thus you can reuse your services in resolve sections. - You don't have to create one $resource object per request. - Support a lot of http methods.

Here is example:

angular.module('app').service('accountService'function(Restangular){

   var baseAccounts = Restangular.all('accounts');

   this.getAll = function(){
      return baseAccounts.getList();
   }

   this.getById = function(id){
       /accounts/profile/{id}
       return baseAccounts.one('profile',id)
   }

})

More information you can find here. https://github.com/mgonto/restangular#differences-with-resource

Upvotes: 0

mvermand
mvermand

Reputation: 6117

Don't you need to change

serverResource.create(item).then(function(data, status, headers, config) {...}

into

serverResource.create(item).$promise.then(function(data, status, headers, config) {...}
                            --------

see https://docs.angularjs.org/api/ngResource/service/$resource at the end of the "Credit card resource" example

Upvotes: 1

Absor
Absor

Reputation: 536

Hope this helps you on the right way:

  var app = angular.module("app", ["ngResource"]);

  app.service("contactServer", function($resource, $http) {
      // Initialize resource and modify methods s.t. create POSTS and save PUTS.
      var baseUrl = "test";
      var model = {
        userName: "",
        position: ""
      }

      var serverResource = $resource(baseUrl + "/:id", {id: "@id"},
              {create: {method: "POST"}, save: {method: "PUT"}});

      this.post = function() {
          alert("Starting post");
          var item = {"name": model.userName, "position": model.position};
          serverResource.create(item).then(function(data, status, headers, config) {
              alert("id: " + data._id);
              window.localStorage.setItem("DBid", data._id);
          }, function(data, status, headers, config) {
              alert(JSON.stringify(['Error', data, status, headers, config]))
          })
      }

      this.put = function() {
          alert("Starting put");
          var item = {"name": model.userName, "position": model.position, "id": window.localStorage.getItem("DBid")};
          serverResource.save(item).then(function(data, status, headers, config) {
              alert(JSON.stringify(['Success', data, status, headers, config]));
          }, function(data, status, headers, config) {
              alert(JSON.stringify(['Error', data, status, headers, config]));
          })
      }
  });

  app.run(function(contactServer) {
      document.addEventListener("deviceready", onDeviceReady, false);

      function onDeviceReady() {

          if (window.localStorage.getItem("DBid") == null) {
              alert("no DBid");
              contactServer.post();
          }
          else {
              alert("Retrieved stored DBid: " + window.localStorage.getItem("DBid"));
              contactServer.put();
          }

      }

  });

To make it a bit better I would return serverResource object from the service contactServer and use the resource's save and create methods in the controllers and run block (also resolve promises there).

In short: you have to create the $resource only once with $resource() (outside service function declarations) and just use that in the functions. Also no need for new keyword, might be the thing that breaks this.

Upvotes: 2

Related Questions