John B.
John B.

Reputation: 2359

Inter-Communication (Directive to Service to Controller)

I am working on a multiple file upload module and am stuck when it comes to communicating from my service to my controller without using $rootScope.

A directive watches the file input and hands the files onchange to a service, where they get uploaded and the upload progress monitored. Based on the response (from success, error and progress changes), the parent controller should show thumbs and progress.

I do not want to use $emit and $on on my $rootScope, since I need the pattern quiet often and only that one parent controller needs to know about the uploads.

I created a simplified(!) Plunkr with some additional information to better understand the problem.

Is there another way for my controller to react to changes (happening inside the service factory)?
Or maybe a completely different way of achieving such?

Upvotes: 2

Views: 848

Answers (1)

Caio Cunha
Caio Cunha

Reputation: 23394

A proper way to handle async operations with Angular is using promises.

You could return a promise from the service call and resolve it to the src of the thumb. You could then not use a directive at all.

Your controller would use the service this way:

function ParentController($scope, imageService) {
  $scope.change = function() {
    $scope.src = imageService.change();

    $scope.then(function(result) {
      // the loading ended, but there is no need to set the src
      // as it will already bind to src when ends. 
    }, function(err) {
      // something went wrong
    });
  };
 }

Your service:

app.factory('imageService', function($q, $rootScope) {

  function doTheUploading(defer) {
    if (everythingOk) {
      defer.resolve(thumbSrcString); 
    } else {
      defer.reject({something: 'an error ocurred'});
    }
  }

  return {
    change: function() {
      // creates the promise
      var defer = $q.defer();
      doSomethingAsync(defer);
      return defer.promise;
    }
  };

});

At least, your HTML should look like:

<div ng-controller="ParentController">
  <img src="{{ src }}"><br><br>
  <span class="btn" ng-click="change()">Change!</span>
</div>

Regarding the progress, you will need to go with callbacks or returning an object that would bring the promise and a progress indicator (i.e: return {promise: defer.promise, percent: 0}). The progress indicator should be then updated from inside the service.

You could also chain your promise if you need any transformation in the URL returned from the server before sets it to the src property, i.e.:

$scope.src = imageService.change().then(function(result) {
    return 'http://dummyimage.com/' + result;
  });

Updated your Plunker.

Upvotes: 3

Related Questions