Question

I am working on an admin app with AngularJS. The app fetches its data from the server using $resource. I end up with data objects containing '$promise' property to determine when the data has been fetched. Everything's fine.

Now, this admin app can also create new objects. Those new objects are managed by the same controllers than the one that usually come from '$resource'.

So now I have 2 kind of objects:

  • Objects with $promise property. I should use $promise.then() before manipulating them with full data
  • Plain objects. They don't have a $promise property, their value is accessible instantly

I would like to reduce code and to have a single use-case, not having to check if object data is resolved, or if it is not a promise.

Is there any problem in building my 'plain' objects by adding them a '$promise' property which is already resolved to themselves? That way, I would always use 'myObject.$promise.then()'.

Is there any common pattern to handle this situation? I could not find any 'standard' method to create this kind of objects with Angular.

Was it helpful?

Solution

You could use $q.when if unsure whether the object has a promise or not.

(obj.$promise ? obj.$promise || $q.when(objResult)).then(function(result){
          //handle success case.
          //Incase of object not having the $promise property result will be object itself
}) 

if the resultant property does not have a promise this will resolve with promise.

Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

You do not need to always create a promise and attach it on the data that is being transferred, instead you could make your methods returns promise thus help you implement promise pattern and abstract out the promise logic on your service itself. Example:-

   function getCurrentUserData(userId){
       var defered = $q.defer();
       ... 
       //Check in my cache if this is already there, then get it from cache and resolve it
       defered.resolve(objectFromCache);
      //my else condition
      //It is not in cache so let me make the call.
      $http.get('myurl').then(function(result){
            //Do validation with data and if it doesnot match then reject it
            defered.reject(reason);
             //Else Do something with the data put it into the cache, mapping logic etc..
             defered.resolve(dto);


      }).catch(function(error){
            //do something with error and then reject
            defered.reject(reasonDerived);
      });
      return defered.promise;
   }

Here is a simplified and less explicit version (Credit: Benjamin Gruenbaum):

var cached = null;
function getCurrentUserData(userId){
    return cached = cached || $http.get('myurl').then(function(result){
        if(isNotValid(result)) return $q.reject(reason); // alternatively `throw`
        return transformToDto(result);
    }, function(response){
     if(checkforsomethingonstatusandreject)
       return $q.reject('Errored Out')
     //do some actions to notify the error scenarios and return default actions
     return someDefaults; });
    }

You can of course return $q.reject(derivedReason) here rather than returning the reason and transform it based on further checks, the idea is caching the promise rather than the value. This also has the advantage of not making multiple http requests if the method is called before it returns once.

Now you could always do:-

    getCurrentUserData(userid).then(function(user){

    }).catch(function(error){

    });

Promises can be chained through as well. So you could also do this:-

   return $resource('myresource').$promise.then(function(result){
          //Do some mapping and return mapped data
         return mappedResult;
    });

OTHER TIPS

A+ promises should offer a static method:

Promise.resolve(val);

...which generates a pre-resolved promise. You should return one of these if your promises library offers this. Great. I do this frequently to avoid interface duplication.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top