3

I'm trying to instantiate a controller, passing arguments using a "resolve" object. Here is the syntax :

{
  controller: 'HtmlModalController',
  resolve: {
    myarg: function() {
      return "FOO";
    }
  }
}

This will cause my controller to have " myarg" variable injected as argument with value "FOO".

Now, this syntax also supports promises, which will be automatically resolved when instantiating the controller.

  resolve: {
    myarg: function() {
      // Does the same as above (auto-resolved promise)
      return $q.when("FOO");
    }
  }

Now, the question is : How can I inject a promise into the controller ? I don't want the promise to be resolved as I will resolve it once the controller is loaded. I tried to nest multiple promises, but they are all resolved back to the "FOO" value once in the controller.

  resolve: {
    myarg: function() {
      // All promises are resolved for me :'(
      return $q.when($q.when("FOO"));
    }
  }

My only solution for now is to wrap the promise into a function/object :

  resolve: {
    myarg: function() {
      // So in my controller I can do "myarg.promise.then(...)"
      return {promise: $q.when("FOO")};
    }
  }

I know this is a non-blocking detail, but I wonder if there is a way to properly resolve a promise "as a promise".

Thanks =)

John Slegers
  • 45,213
  • 22
  • 199
  • 169
Neozaru
  • 1,109
  • 1
  • 10
  • 25
  • 1
    Why would you do that and not just create the promise in the controller? – Omri Aharon Aug 11 '15 at 15:31
  • Other than an intellectual exercise, when would this scenario be of use? Typically, one would create a service for any async operations and the service method would return a promise – New Dev Aug 11 '15 at 15:44
  • The current use case is to instantiate Modals dynamically (http://pineconellc.github.io/angular-foundation/), with dynamic loadable content. - I don't want to load the content before the modal, because I need it to be open on user action (feedback). - I would like to let a service (containing logic) inject the content into the modal as there will be multiple cases with common logic, without creating a custom controller for each modal. Now the question was more about curiosity, as workarounds can be easily implemented. – Neozaru Aug 11 '15 at 20:31

2 Answers2

2

There is no way to resolve with a promise without it being implicitly recursively unwrapped to the final value. Resolving with a promise will always resolve with the promise's result and not with the promise itself.

The only option would be to pass a wrapper like you have done with {p: promise } and no then method.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
0

One pattern you can use is a service that provides the promise and caches it internally.

function MyService() {
    var promise;
    this.getSomePromise = function() {
        if(!promise) {
            promise = $q.when(...); // whatever produces your promise
        }
        return promise;
    }
}

myModule.service('myService', MyService);

You can then depend on this service in your controller:

myModule.controller('MyController', function(myService) {
    myService.getSomePromise().then(result => ...);
});

As well as using it to block your resolve:

resolve: {
    /** 
      * Note: we are not actually injecting this. 
      * Only using it to block the route. The name blocker is arbitrary and could be anything.
      */
    blocker: function(myService) {
        return myService.getSomePromise();
    }
}

This allows you to hook up as many then handlers as you want to the service method while ensuring only 1 call to the underlying asynchronous operation and 1 promise instance.

bingles
  • 11,582
  • 10
  • 82
  • 93