1

Please excuse me if this is in some way obvious, but I can't seem to find any thing similar in the angularjs docs or on SO. I have an application where there is a table of data, including estimates by month. We are implementing something where you can drag and drop the estimate from one month to another as a way to make some quick changes. I'm running into an odd issue:

Resource:

function updatableResource(url){
  return $resource(url+":id/", {id: '@id'},
    {
      'update': {
        method:'PUT',
      },
    }
  );
}

// in API definition file
Estimate: foo.updatableResource('<URL>'),

That works fine, and I can call $update or $save with no issue normally

In this case though, I need to edit two things at the same time, and possibly even delete one. So I need to know if the first was successful in before I do the second. For example, if I move an estimate from one month to another with no estimate, I first make the new estimate and then delete the old one. I would think I could do something like this:

// source and target are objects that each contain an estimate from a different month.

successFunc = function() {
    source.estimate.$delete(
        function() {
            <refresh stuff>
        },
        function(rejection) {
            alert("Unable to delete source estimate.");
        }
    );
};

errorFunc = function(rejection) {
    <undo stuff>
    alert("Unable to add money to target estimate.");
};

// POST if new, PUT if updating
if (isNewEstimate) {
    target.estimate.$save(successFunc, errorFunc);
} else {
    target.estimate.$update(successFunc, errorFunc);
}

So in this code, the estimate creation/update works fine, but I cannot call any $resource calls on the source estimate in the success function. I get the error TypeError: source.estimate.$delete is not a function If I call it outside that function (at the same level as the target save/update call) it works though.

In my research I found something which sort of worked:

Api.Estimate.delete(source.estimate, 
<success and failure functions>
...

That made the deletion work, but nothing else seems to recognize the deletion and I need to do an expensive manual re-query in order for it to work. Is there something odd about scope since it's in a callback function? I just can't figure out why it won't work within that function...

EDIT:

Upon further testing, it seems to be an issue with the objects themselves:

source.estimate
Object
    amount: 12345
    date: "2017-02-25"
    foo: "ABC"
    id: 4715
    bar: 987
    baz: 123
    __proto__: Object

target.estimate
d
    $promise: undefined
    $resolved: true
    amount: 12345
    date: "2017-03-25"
    foo: "ABC"
    id: 4716
    bar: 987
    baz: 123
    __proto__: Object

So the target one, which is of type d, seems to work while the source one, which is just a standard object, doesn't. If I reverse the drag direction, I'm given the same type of result. Source is always an object, and target is always d (which I assume is because of minified JS).

zaknotzach
  • 977
  • 1
  • 9
  • 18
  • 1
    I've run into this issue when using $resource... which is why i switched to straight $http, but if you Console.log(source.estimate) does it actually have a value at that point? – Dylan Mar 10 '17 at 20:50
  • As I added in my edit above, the object seemed normal (as in it had the fields and values expected) but it turns out the actual objects are different. – zaknotzach Mar 10 '17 at 21:40

2 Answers2

0

The instance methods of $resource objects are promises and can be chained with standard chaining methods:

$scope.resource1 = updatableResource().get({id: 1});
$scope.resource2 = updatableResource().get({id: 2});

$q.all([$scope.resource1.$promise, $scope.resource2.$promise])
  .then(function(resArray) {
     var res1 = resArray[0];
     var res2 = resArray[1];
     //Process results
     //Return promise to chain
     return $q.all([res1.$update(), res2.$update()]);
}).then(function(resArray) {
     var res1 = resArray[0];
     var res2 = resArray[1];
     //Do next thing
});

Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises.

It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain. This makes it possible to implement powerful APIs.

— AngularJS $q Service API Reference - Chaining Promises

georgeawg
  • 48,608
  • 13
  • 72
  • 95
0

The issue actually turned out to be related to the plugin I was using to get the objects. For some reason it was mangling one of them, and while it had the same properties it wasn't a proper $resource anymore, which is why I couldn't call the function. It just seemed like it was related to the calling layout because of the order in which I was calling some functions.

zaknotzach
  • 977
  • 1
  • 9
  • 18