Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use AngularJS $resource custom actions?

Tags:

angularjs

I've been using custom actions in a few repositories. And up until now I only had to specify the url and the method.

For example:

updatePassword: {
  url: ENV.NITRO_PROJECT_REST_URL + '/admins/:adminId/password',
  method: 'PUT'
}

But then, I had to code a custom action that had, not one, but two path parameters:

technicianModule.controller('technician.teamCtrl',
  ['$scope', '$state', '$stateParams', 'CommonService', 'TechnicianService', 'TeamService', 'TeamTechnicianService',
  function($scope, $state, $stateParams, CommonService, TechnicianService, TeamService, TeamTechnicianService) {

    $scope.add = function(teamId) {
      TeamTechnicianService.add(teamId, $stateParams.technicianId, function() {
        TeamService.get(teamId, function(data) {
          $scope.teams.push(data);
          $scope.unassignedTeams.splice(CommonService.getResourceIndex($scope.unassignedTeams, data), 1);
        });
      });
    };

  }
]);

teamModule.factory('TeamTechnicianService',
  ['RESTService',
  function(RESTService) {
    var factory = {};

    factory.add = function(teamId, technicianId, callback) {
      return RESTService.TeamTechnician.add({teamId: teamId, technicianId: technicianId}).$promise.then(callback);
    }

    return factory;
  }
]);

So I first coded it like:

TeamTechnician: $resource(ENV.NITRO_PROJECT_REST_URL + '/teamtechnicians/:teamtechnicianId', {}, {
add: {
  url: ENV.NITRO_PROJECT_REST_URL + '/teamtechnicians/:teamId/:technicianId',
  method: 'POST'
}

But it would not work. The parameters were not passed in.

After a few tries I found out it worked when adding some parameter definition, right before the custom action definition.

It had to be like:

TeamTechnician: $resource(ENV.NITRO_PROJECT_REST_URL + '/teamtechnicians/:teamtechnicianId', {
  teamId: '@teamId',
  technicianId: '@technicianId'
}, {
add: {
  url: ENV.NITRO_PROJECT_REST_URL + '/teamtechnicians/:teamId/:technicianId',
  method: 'POST'
}

Note the presence of:

teamId: '@teamId',
technicianId: '@technicianId'

My understanding was then that in a $resource definition, a custom action that has more than one path parameter, requires them to be defined with @ signs.

And not when it has only one.

Why is that ?

And why can't the path parameters be declared in the custom action instead of above in the resource ?

like image 936
Stephane Avatar asked Sep 19 '14 07:09

Stephane


2 Answers

The parameters can be declared per custom action.
The default parameters are what their name implies: default parameters (as in: "used in case other parameters are not provided").

The use of '@' (either in default parameters or in action parameters) is not mandatory.
It is provided as a convenience and has a special meaning. paramKey: '@someProp' means:
"For methods that have a request body (e.g. POST, PUT etc), if I do not explicitly provide a value for the parameter paramKey, please look into my data object for a property named someProp and use its value as the value for the paramKey parameter."


Note that when you use a class method, you have to explicitly provide the data object:

SomeResourceClass.save({.../* data object */...});

When you use an instance method, the instance itself acts as the data object:

var instance = SomeResourceClass.get(...);
instance.$save(); /* `instance` will act as the data object. */

See, also, this short demo.


UPDATE:

It seems like you want to call the following custom action:

add: {
    url: ENV.NITRO_PROJECT_REST_URL + '/teamtechnicians/:teamId/:technicianId',
    method: 'POST'
}

Trying to call it like this <ResourceClass>.add({teamId: teamId, technicianId: technicianId}) does not work as expected, as it interpret the (intended to be) params object as a data object.

From the ngResource documentation, the method signatures for "non-GET" methods (like yours) are:

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

From the above exerpt, it is clear that if you only pass one object in "class" action call, then it is interpreted as the data object (the request's payload). Additionally, if you have @-prefixed default parameters, then the URL parameters are going to get resolved against that data object (which is why it worked with default parameters).


In order for Angular to interpret the params object as a params (and not data object) and since the data param is mandatory, you need to call it like this:

<ResourceClass>.add({teamId: teamId, technicianId: technicianId}, {})

(Alternatively, you could be using a TeamTechnician instance, but that's another story.)

like image 71
gkalpak Avatar answered Oct 28 '22 07:10

gkalpak


When you define a $resource the definition looks like this

$resource(url, [paramDefaults], [actions]);

The second parameter paramDefaults is just there to provide default values. The @ syntax is used to derive the parameter value from the payload of a PUT or POST request.

You can always provide parameter values while actually invoking the resource action. If you don't then the default parameter values are taken and if that too are not there then the fragment is removed.

What can be parameterized needs to be define on $resource using the : syntax and same goes for what the default value is. For the action method they just take the same $http config object.

like image 32
Chandermani Avatar answered Oct 28 '22 08:10

Chandermani