0
votes

I'm using the library ParallelJS to do encryption/decryption in a web worker but when the promise is resolved it doesn't update the view accordly to the model's changes. Now, i know that i have to wrap the code that is called outside angularjs' scope in a $scope.$apply, but even doing this doesn't help.

I think that the reason is that I am resolving a deferred object inside a callback that is called outside angular's scope. It's a little hard to explain so let me show my code:

function _encrypt(options){
... //crypto-js code to do AES encryption
}

function _decrypt(options){
... //crypto-js code to do AES decryption
}

angular.module('CryptoService', []).factory('Cryptor', function($q, $rootScope){
    function Cryptor(){};
    Cryptor.prototype = {
        encrypt: function(string, key) {
            var deferred = $q.defer();
            var ivS = generateIV();
            var p = new Parallel({
                iv: ivS,
                text: string,
                key: key
            }, { evalPath: '/assets/js/eval.min.js' });

            p.require('/assets/js/crypto.min.js');

            p.spawn(_encrypt).then(function(result){
                deferred.resolve(result);
            });

            return deferred.promise;
        },
        decrypt: function(string, key) {
            var deferred = $q.defer();
            var p = new Parallel({
                text: string,
                key: key
            }, { evalPath: '/assets/js/eval.min.js' });

            p.require('/assets/js/crypto.min.js');

            p.spawn(_decrypt).then(function(result){
                deferred.resolve(result);
            });
            return deferred.promise;
        }
    };
    return new Cryptor();
});

angular.module('ContactService', ['CryptoService']).factory('Contact', function($q, $rootScope, Cryptor){
    function Contact(){
        //initialization
    };
    Contact.prototype = {
       query: function(){
           var deferred = $q.defer();
           var options = {};
           _oauth.secureGET(this._endpoint,options).done(function(result){
               Cryptor.decrypt(result.cmc, key).then(function(string){
                   var data = JSON.parse(string);
                   var contacts = [];
                   for (var cidx in data){
                       var objContact = data[cidx];
                       var c = new Contact();
                       for(var pidx in this._properties){
                           var property = this._properties[pidx];
                           c[property] = objContact[property];
                       }
                       contacts.push(c);
                   }
                   //Since _oauth is using a jQuery method to execute the requests we are outside of angularjs' scope, so we need to wrap the promise resolution in 
                   //the $apply method of the rootscope
                   $rootScope.$apply(function(){
                       deferred.resolve(contacts);
                   });
               });

           }.bind(this)).fail(function() {
               $rootScope.$apply(function(){
                   deferred.resolve([]);
               });
           });
           return deferred.promise;
       },
   };
   return new Contact();
});

Now what's happen: If I leave the code the callback function of the query method is never called, since in the cryptor service the promise is called outside angular's scope. If I move the $rootScope.$apply wrapper to the Cryptor service then the callback inside the Contact service is called, the callback inside the controller is called BUT the view is not updated.

Any hint on how to resolve this?

Thanks you all

A.

2

2 Answers

1
votes

Ok I feel so stupid... The problem wasn't the view not updating but the model empty. Since i was missing a bind(this) to the Cryptor promise's callback the models were empty and the view wasn't showing anything. Changing this

Cryptor.decrypt(result.cmc, key).then(function(string){
    var data = JSON.parse(string);
    var contacts = [];
    for (var cidx in data){
        var objContact = data[cidx];
        var c = new Contact();
        for(var pidx in this._properties){
            var property = this._properties[pidx];
            c[property] = objContact[property];
        }
        contacts.push(c);
    }
    //Since _oauth is using a jQuery method to execute the requests we are outside of angularjs' scope, so we need to wrap the promise resolution in 
    //the $apply method of the rootscope
    $rootScope.$apply(function(){
        deferred.resolve(contacts);
    });
});

to this:

Cryptor.decrypt(result.cmc, key).then(function(string){
    ...
}.bind(this));

did the trick.

0
votes

From your code:

p.spawn(_encrypt).then(function(result){
    deferred.resolve(result); 
});

Promise from Parallel.js is not same promise which in Angular.js. So you need to wrap

deferred.resolve(result);

into Angular.js $timeout:

$timeout(function(){
    deferred.resolve(result);
}, 0)

in order to it notify Angular.js