2

Basically what I want to do, is to assign some model value from function call that resolves promise. Like so

value = someFun()

This is a service from which I call this function

app.factory('SomeService', function($q) {
    return {
        someFun: function() {
            var d = $q.defer();
            try {
                d.resolve("hi");
            } catch (e) {
                d.reject(e);
            }
            return d.promise.then(function(text){
                return text;
            });
        }
    };
});

Here is the HTML code

<div ng-init="value = 'yes'">
    <pre>{{value |json}}</pre>
</div>
<button type="button" ng-click="value = someFun()">click me</button>

And this is in the controller

$scope.someFun = SomeService.someFun;

Here is plnkr

http://plnkr.co/edit/qO5ofBXZDsi3cS3bBnT8

Right now it returns an empty object. What is wrong?

EDIT: As already answered below, yes this is one way, but let's say I want to call SomeService.someFun in ngRepeat?

EDIT2: HERE IS THE ANSWER -> Angular UI Bootstrap modal inside ngRepeat

Community
  • 1
  • 1
walts
  • 92
  • 3
  • 15
  • Could you explain/give more details regarding your ngRepeat usage? – apairet Aug 12 '14 at 10:55
  • So I made a new question in StackOverflow to illustrate the real case of this problem. Here is the new question about this problem with proper answer: http://stackoverflow.com/questions/25329596/angular-ui-bootstrap-modal-inside-ngrepeat – walts Aug 15 '14 at 17:30

4 Answers4

2

Generally if you want to use something that is updated asynchronously to appear in a view without having to write controller code as well (which is the case in your question, as you wanted to call the function inside ng-view), then you need to return a blank placeholder object, and either merge the result of the promise into it, or update a property of that object which you can reference in the view.

Your question is too abstract to know the exact way to approach this to make sense for your application, but here is the general (and abstracted) idea plunker here:

.factory('promiseService', function($q){
  return {
    someDeferredThing: function(){
      var placeholder = {};
      var d = $q.defer();
      d.promise.then(function(text){
        placeholder.text = text;
      });
      d.resolve("hi");
      return placeholder;
    }
  }
)

The service returns an object which is updated by the promise, and not a promise itself. If you return a promise then you have to deal with .then() etc. to get the value.

.controller('appCtrl', function($scope, promiseService){
  $scope.deferredResult = promiseService.someDeferredThing();
});

This is how you assign the value to the scope.

$scope.deferredResult.text will be undefined until the promise resolves, then it will become defined and resolve to "text". As such, your view should handle the undefined state for a short duration - usually it's very simple to do that with ng-show.

Ed_
  • 18,798
  • 8
  • 45
  • 71
  • Of course the task is much more complicated, but I made it extremly simple for you guys, but the actual task is like that. I have an object (Car) with array (Params) of objects (Param) that has properties 'Key' and 'Value' and in ngRepeat loop I create an input field for each Param in Params. So there is a button next to each Param Value. If you click on it an Angular UI Bootstrap modal is opened where you can edit this Value in a more bigger view (There is actually a Text-Angular plugin inside this modal for HTML editing). So this modal code must be inside factory for reusing in other views. – walts Aug 12 '14 at 12:07
  • Angular UI modal returns a promis via 'modalInstance.result', similar to my first example. You can read more about the directive here http://angular-ui.github.io/bootstrap/ – walts Aug 12 '14 at 12:13
  • And why is that the placeholder has to be an object not string? – walts Aug 12 '14 at 12:45
  • The placeholder has to be an object and not a string so that you can keep a reference. Remember in javascript strings are passed by value, so if you just return a string, the value is essentially copied to `$scope.deferredResult`. Then when you update with `placeholder = string` afterwards, `$scope.deferredResult` will have no link to `placeholder` so it'll remain `undefined`. – Ed_ Aug 12 '14 at 15:26
  • Okay, but what about my task, can you provide any ideas on how to approach it? – walts Aug 12 '14 at 20:41
  • I think you need to open a separate question and show the code you currently have and why it's not working. Ideally provide a plunker. I don't understand your task properly as it stands. – Ed_ Aug 13 '14 at 11:01
1

Controller.js:

  $scope.someFun = function () {
        return someservice.someFun().then(function (data) {
            $scope.value = data;
        });
    }

someservice.js:

    function someFun() {
        var d = $q.defer();
        try {
            d.resolve("hi");
        } catch (e) {
            d.reject(e);
        }
        return d.promise;
    }

html:

 <div ng-init="value = 'yes'">
                <pre>{{value |json}}</pre>
            </div>
            <button type="button" ng-click="someFun()">click me</button>
parthicool05
  • 255
  • 1
  • 10
0

promise.then() returns a promise as stated in the AngularJS Documentation. If you want to get the value, you should do it like this:

SomeService.someFun.then(function(text) {
  $scope.someFun = text;
});

Update:

Create a $scope function that will assign value to the value variable instead.

CONTROLLER

$scope.setValue = function() {
  SomeService.someFun.then(function(text) {
      $scope.value = text;
  });
};

HTML

<div ng-init="value = 'yes'">
    <pre>{{value |json}}</pre>
</div>
<button type="button" ng-click="setValue()">click me</button>
ryeballar
  • 29,658
  • 10
  • 65
  • 74
0

To expand upon Ed Hinchliffe's answer, if you're wanting to return an actual object, which will in turn be populated once a promise is resolved, you can do something like this. (Similar to his answer, but using $http as an example async operation) Then instead of copying the result into a hard coded property of the returned reference to placeholder, it copies all properties from the object received from $http into the existing reference to placeholder which has already been returned. This is of course, assuming you are wanting to return an object. I'm assuming this is similar to how $resource works internally when returning objects.

function getObjectToBeFilledByPromise() {
    var placeholder = {};            // Create a reference to return
    var deferred = $q.defer();       // This will handle your promise

    deferred.promise.then(function(data) {  // Once our promise is resolved, populate object

        // Add all properties of data into the existing placeholder reference which was already returned
        for(var attr in data) {
            placeholder[attr] = data[attr];
        }

    });

    // Some Asynchronous Operation...
    $http({
        method: 'GET',
        url: yourUrl
    })
    .success(function(data) {
        deferred.resolve(data); // Resolve promise, which will be handled above
    })

    // Return reference as it exists now.  
    // It will be populated once the promise is resolved
    return placeholder; 
}
Ryan
  • 17,511
  • 23
  • 63
  • 88