AndrewD
AndrewD

Reputation: 451

Handling failed breezejs saves

I have a simple angularjs service which wraps breeze to make it available to my app. In this service I want to have a single mechanism for handling fails. I have tried to put a .fail() in chained on the end of the call, and if the breeze call fails (due to the user being logged out for example) the fail is called. The only problem is that the first .done() in the chain (usually residing in the controller calling the service) also gets called. I only want this to be called when breeze is successful.

How do I prevent the first done from being called as well?

The following is a sample save call (with the done which is incorrectly called)

dataStore.saveEntity(model)
    .then(function() {
    // This is being called on fail             
});

And a snippet of the service wrapping breeze

angular.module('app')
.factory('dataStore' ,function() {

        var _handleFail = function(error) {
            if (error && error.status && error.status === 401) {
                // Logged out error (for example

            }
        };



        function saveEntity(entity) {   
           return manager.saveChanges(entity).fail(_handleFail);    
        }

Upvotes: 4

Views: 554

Answers (3)

AndrewD
AndrewD

Reputation: 451

It turns out that the .fail() propagates the promise further. Consider the following:

promise.then(function(){
      console.log('Then 1');
      throw new Error('Error');
  })
  .then(function(){
      console.log('Then 2');
  })
  .then(function(){
      console.log('Then 3');   
  })
  .fail(function(){
      console.log('Fail 1');   
  })
  .then(function(){
      console.log('Then 4');
  });

Will output:

> Then 1
> Fail 1
> Then 4

The .fail() catches the error from the first .then() and then passes the promise to the fourth .then(). In my example the breeze query generates an error which is caught by the .fail(), dealt with then the promise is handed off to the .then() within the calling container.

To fix this the _handleFail() function needs to throw an error to prevent any chained .then()'s being called (until again the error is handled by a .fail().

So my service now looks like:

angular.module('app') .factory('dataStore' ,function() {

    var _handleFail = function(error) {
        if (error && error.status && error.status === 401) {
            // Logged out error (for example
            throw new Error('Unauthorized');
        }
    };



    function saveEntity(entity) {   
       return manager.saveChanges(entity).fail(_handleFail);    
    }

Upvotes: 3

PW Kad
PW Kad

Reputation: 14995

Jay's answer was the direction I was thinking, and I am not 100% sure EXACTLY how you would do this with Angular's bound objects, but another option, if you can't change the structure, is to pass up a status object to track failure conditions in the chain (the example uses a Knockout observable because I am not as familiar with how to pass objects that Angular is observing, but the general theory should persist) -

var status = ko.observable(true);    
// var $scope.status = true (maybe???)
dataStore.saveEntity(model)
    .then(function() {
        if (status()) { // status still equals true, no failures occurred, carry on 
        }
});


angular.module('app')
.factory('dataStore' ,function() {
    var _handleFail = function(error) {
        if (error && error.status && error.status === 401) {
            // Logged out error (for example)
            status(false);
        }
    };

    function saveEntity(entity) {   
       return manager.saveChanges(entity).fail(_handleFail);    
    }

The fail() should fire before the then() does in your controller and when the then() logic is reached it will know there has been a problem up the chain.

Upvotes: 0

Jay Traband
Jay Traband

Reputation: 17052

I haven't actually seen any examples where 'fail' gets called on the promises chain before the 'then' which is what you are doing. So I would try rewriting your code to look like:

function handleFail(error) {
    if (error && error.status && error.status === 401) {
        // Logged out error (for example

    }
};


dataStore.saveEntity(model)
 .then(function() {
    // your success code here. - should not get called except on success.
 }).fail(handleFail);

And a snippet of the service wrapping breeze

angular.module('app')
   .factory('dataStore' ,function() {

        function saveEntity(entity) {   
           return manager.saveChanges(entity);
        }
    });

There may be other ways as well, but this seems simplest.

Upvotes: 0

Related Questions