1

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?

2
  • What is baseUrl ? is that another service ? I don't understand why you are creating 2 methods in this.post if you are only using one. I would suggest another approach, have you consider to create a factory and return a resource class ? The way this is now is creating instance each time you call each method Commented Nov 5, 2014 at 2:57
  • Too many redundant information in the question. I think, you are able to localize the bugs by yourself. Read documentation docs.angularjs.org/api/ngResource/service/$resource and format your code. Commented Nov 5, 2014 at 13:14

4 Answers 4

2
+50

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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.

8 Comments

model is declared in a different part of the file, its part of the MVC framework of AngularJS. It's scope encompasses the service.
@eugene1832 Yeah sorry, didn't notice the comment. What is the error message that you get?
There's no error message. I just get the alerts I put into the code up until 'Created resource.' Then the rest of my app doesn't load up, which means that something right after that alert doesn't work so my code stopped processing.
I'm sure I'm using $resource incorrectly, but I'm basing its use on a book and internet resources, so if the answer is out there I'm striking out.
@eugene1832, hope the edited answer helps. Fast test here: http://plnkr.co/edit/qrLcmQ6Aw2M9RbMsqkhm.
|
1

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

1 Comment

actually that is not true at all, you use $promise with class resources, here he is using a instance resource, so he can call then method, but the method is not called correctly, that should give an undefined error.
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.