2

I'm new to AngularJS and I have trouble to update an object via REST. I'm using a PHP/Mysql Backend (Slim Framework).
I'm able to retrieve (GET), create (POST) a new object but not to edit (PUT) one. Here's the code:

my Form:

<form name="actionForm" novalidate ng-submit="submitAction();">
  Name: <input type="text" ng-model="action.name" name="name" required>
  <input type="submit">
</form>

My service:

var AppServices = angular.module('AppServices', ['ngResource'])
AppServices.factory('appFactory', function($resource) {
  return $resource('/api/main/actions/:actionid', {}, {
    'update': { method: 'PUT'},
  });
});

app.js

var app = angular.module('app', ['AppServices'])
app.config(function($routeProvider) {
  $routeProvider.when('/main/actions', {
    templateUrl: 'partials/main.html',
    controller: 'ActionListCtrl'
  });
  $routeProvider.when('/main/actions/:actionid', {
    templateUrl: 'partials/main.html',
    controller: 'ActionDetailCtrl'
  });
  $routeProvider.otherwise({redirectTo: '/main/actions'});
});

controllers.js:

function ActionDetailCtrl($scope, $routeParams, appFactory, $location) {
  $scope.action = appFactory.get({actionid: $routeParams.actionid});
  $scope.addAction = function() {
    $location.path("/main/actions/new");
  }
  $scope.submitAction = function() {
    // UPDATE CASE
    if ($scope.action.actionid > 0) {
      $scope.action = appFactory.update($scope.action);
      alert('Action "' + $scope.action.title + '" updated');
    } else {
      // CREATE CASE
      $scope.action = appFactory.save($scope.action);
      alert('Action "' + $scope.action.title + '" created');
    }
    $location.path("/main/actions");
  }
}

In Slim, in api/index.php, I've got these routes and functions defined:

$app->get('/main/actions', 'getActions');
$app->get('/main/actions/:actionid',    'getAction');
$app->post('/main/actions', 'addAction');
$app->put('/main/actions/:actionid', 'updateAction');

When I create a new "action", everything is working as expected. But when I try to edit an existing one, I've got this error:

PUT http://project.local/api/main/actions 404 Not Found

The action is not updated (although the alert message "Action xxx updated" is displayed)

Is it an issue with my routeProvider setting ? I guess the PUT url misses the id at the end...

I precise that if I try to simulate the PUT request with POSTMan-Chrome-Extension for example, everything is working well (PUT http://project.local/api/main/actions/3 returns the expected data)

claire_
  • 723
  • 2
  • 8
  • 17
  • There's at least a mismatch between your urls in your code: in the resource definition your url is '/api/main/actions/:actionid', but your call says **app/main/...**. And the alert message should be put in the success callback of your resource. If you put it after it, there's no logical connection between it and the xhr request. – Narretz Jul 15 '13 at 08:59
  • @Narretz, you're right for the success callback, thanks for the tip. However, my urls seem correct to me : my php backend (slim framework) is located under /api (routes are defined in /api/index.php) while the website is under /app. This architecture is inspired from https://github.com/Narretz/angular-cellar – claire_ Jul 15 '13 at 09:28
  • Interesting to see that the cellar is still popular. ;) Well, this is really difficult to debug from afar, but there's still the difference between the url in the resource and the url you pasted that returns 404, specifically the first says **api** and the second **app**, i.e. the xhr request is looking for the server in the app folder. If it's not a typo, then the issue must be there somewhere. – Narretz Jul 15 '13 at 10:10
  • OMG ! @Narretz, I didn't even realize it was YOUR github account :) And I must apologize a second time because there was indeed a TYPO: the error I got is "PUT http://project.local/api/main/actions 404 Not Found" (I've edited the question accordingly) – claire_ Jul 15 '13 at 14:00

1 Answers1

5

After reading more carefully, the problem is indeed that you don't send the id in the url when you do the update request. Furthermore, the way you call update / save is not quite what is intended with resource. After you have created a resource, you call the save / update methods on the instance, not as a factory function. You can also bind properties of the instance to the params in the url, so you don't have to specify them on each call:

AppServices.factory('appFactory', function($resource) {
  return $resource('/api/main/actions/:actionid', {actionid : '@actionid'}, {
    'update': { method: 'PUT'},
});

The second parameter of resource tells angular which properties it should use to populate the url parameters. so the get operation stays the same:

$scope.action = appFactory.get({actionid: $routeParams.actionid});

and the update is now:

$scope.submitAction = function() {
  // UPDATE CASE
  if ($scope.action.actionid > 0) {
    $scope.action.$update()
  } else {
    $scope.action.$save();  
  }
}

Since you used angular-cellar as a basis, I should probably check and see if I can improve this ...

Narretz
  • 4,769
  • 32
  • 40
  • Thanks again for your time, your explanation is perfectly clear. – claire_ Jul 15 '13 at 15:19
  • 1
    In order to get this to work, I needed to call the update using the id parameter, similar to the get: $scope.action.$update({actionid: $routeParams.actionid}). If not, the server would return a 404 since it's expecting an id in the path. – Nik May 18 '14 at 15:29
  • I have the same problem, but, still getting error 404 – fsalazar_sch Jan 18 '16 at 19:53