-3

I have an object I'm saving to an API. The API returns a unique identifier: id.

When I save with I get a promise which includes my new, API assigned, id.

Now the user may want to carry out other operations, which will require the id. For example, renaming the widget and re-saving, or adding further objects that point back to its id.

What are the practical and ideally straightforward options*?

*Other advice on stackoverflow I've read suggests using 'resolve' which would be fine if I was reverting to the router at this point. But I'm not at the moment.

Here's a simple example:

widget.saveThis = function() {
    if ('id' in this) { 
        this.put(); 
    } else {
        var _this = this;
        rest.all('widgets').post(this).then(function(result) {
            // Copy the new properties I have received to this object. 
            // ignore the methods from restangular.
            for (var key in result) {
                if (typeof(result[key]) != 'function')
                    _this[key] = result[key];
            }

            p.refresh();
        }); 
    }
};

Where if save is pushed twice in a row we might get two copies of the object.

JCx
  • 2,689
  • 22
  • 32
  • we need code examples. generally, it isn't possible to "wait" for an asynchronous action to complete in javascript; instead you must use a callback. – Kevin B Nov 11 '15 at 20:04
  • I don't know what lib you are using to do the request, but normally is some thing like ````promisse.then(callback)```` or ````promisse.when(callback)````. – Joel R Michaliszen Nov 11 '15 at 20:05
  • If you don't want to prevent your user from doing stuff (such as editing), you could disable your "save" button and then enable it again once you get your ID in your "then" function. – amg-argh Nov 11 '15 at 20:05
  • I could disable the other editing options until the save has completed easily enough. That fixes the problem, but it doesn't look right unless I add some way of indicating that we are waiting for the id to come back. Definitely an option. – JCx Nov 11 '15 at 20:07
  • _"What are the practical and ideally straightforward options*?"_ Use `.then()` to access `id` following initial fulfilled `Promise` . _"it doesn't look right unless I add some way of indicating that we are waiting for the id to come back"_ Notify user ? Can include `js` tried at Question ? – guest271314 Nov 11 '15 at 20:08
  • It's more a UI/architecture question than a specific coding question, but I'll sketch out an example of the code if that'll help. – JCx Nov 11 '15 at 20:10
  • 1
    @JCx yes you should absolutely also indicate that you are currently "loading". The call will take time, if the next operation requires information from that call. Your options are 1) don't let them do anything; 2) let them edit, but prevent them sending the next request until you get the data. – amg-argh Nov 11 '15 at 20:14
  • I think (2) is really quite good. It's just a little 'odd' because the user won't really understand why they can't save. However with a fair wind behind them they'll never notice as it'll be so quick. – JCx Nov 11 '15 at 20:20
  • 1
    @Jcx then tell the user why. A disabled button, below a loading indicator should be sufficient. If you feel not so, tool tip on the disabled button "We need a little more time to process your last request, just a sec!" – amg-argh Nov 11 '15 at 20:39
  • @amg-argh thanks - I think that's the answer. much appreciated. i've been scratching my head not thinking about this in the right way, and generally over-complicating the issue. – JCx Nov 11 '15 at 20:40

1 Answers1

1

Imagine you have a service where you do the API Communication (maybe via REST?

"use strict";
(function() {
    var module = angular.module('myModule.service');

    module.factory('myService', function($http, $q) {
        return {
            /**
             * save and get
             */
            saveAndGet: function(myObject) {
                var deferred = $q.defer();
                $http.post(getContextPath()+'/rs/saveObj', JSON.stringify{myObject})
                .success( function(data) {
                    deferred.resolve(data);
                })
                .error(function(response) {
                    deferred.reject(response);
                });
                return deferred.promise;
            }
        }
    });
})();

now imagine you have a controller where you wait for the saving to be done:

"use strict";
(function() {
    var module = angular.module('myModule.controller');
    module.controller('MyController' , function($scope, myService) {

    var myObj = //set it somehow
    $scope.id; //needed for save the "new" id
    myService.saveAndGet(myObj)
    .then( function(result) {
         // is called if no error occured
         $scope.id = result.id;
    )};
})();

and then image you have that backend (in java for example)

    @POST
    @Path("saveObj")
    @Produces({"application/json"})
    public Response createProjectComment(MyObj obj) {
        // do something and create myNewId
        if (myNewId == null) {
            return Response.ok("error").build();
        }
        return Response.ok(myNewId).build();
    }

that would be one way to solve your problem.

David
  • 153
  • 7
  • saveAndGet should really just be `return $http.post(getContextPath()+'/rs/saveObj', JSON.stringify{myObject})` – amg-argh Nov 11 '15 at 20:15
  • I hadn't thought of that as an option. Just have the back-end error if the ID isn't presented then have the user and/or application retry. But if the save fails, because the id hasn't been returned, the application needs to go into a retry loop? And/or the user needs to retry? – JCx Nov 11 '15 at 20:21
  • @amg-argh: Yes this is possible too and a valid answer. I just use my way because i do some other things in service at success or error. In your solution you have to do everything in the controller which i do not want because it blows off the controller code. – David Nov 11 '15 at 20:28
  • 1
    @JCx: If you cant save your ID and there is an error in the backend, you should write your own errorhandling if the http post returns an error. At this point you can inform the user via a dialog that something went wrong. Please take a deeper look at http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/ – David Nov 11 '15 at 20:29
  • @JCx not true, creating a new promise just to return a exact copy of the promise is not ideal. If you need to do manipulation you should still return the original promise, as they are chained `return $http.post("path", obj).then(function(data){ return manipulate(data); })` – amg-argh Nov 11 '15 at 20:31