-1

Problem Statement: I have an angular js function that fetches data from a service, I am concatenating some columns and then return the concatenated data.

Angular JS Function:

$scope.findCompanyAddressById = function( cmpId ) {

    var address = "";
    $http.get(someUrl+'/company/find?id='+cmpId ).

    then(function(response) {
        $scope.company = response.data;

        for( var i=0; i < $scope.company.locations.length; i++ ) {
            address += " " + $scope.company.locations[i].street1
            address += " " + $scope.company.locations[i].street2
            address += " " + $scope.company.locations[i].city
            address += " " + $scope.company.locations[i].state
            address += " " + $scope.company.locations[i].zip

            console.log( "Rendering address: " );
            console.log( address ); // logs perfect data.
        }

    });
    return address;
}

returns undefined when function is called something like this:

$scope.concatenatedData = $scope.findCompanyAddressById( 1 );

Any idea on how to return the concatenated data from the above function.

dipak_pusti
  • 1,645
  • 2
  • 23
  • 42
Sikandar Sahab
  • 638
  • 3
  • 10
  • 27
  • 1
    I think your pattern is off. That function returns as soon as it _starts_ the REST call; it won't wait for it to finish. In the anonymous function you should be making use of the data, not in the return value. – Tim Biegeleisen Jan 30 '18 at 09:16
  • You should wrap your REST API for Company info into an Angular _`Resource`_. See e.g. https://devdactic.com/improving-rest-with-ngresource/ – Alnitak Jan 30 '18 at 09:19
  • @Tim Biegeleisen the returning variable inside the function, just gets empty outside the get response body. – Sikandar Sahab Jan 30 '18 at 09:19
  • Does it really return undefined? or this empty string: `address = ""` ? – Buh Buh Jan 30 '18 at 09:30
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Buh Buh Jan 30 '18 at 09:33
  • @BuhBuh no, it's not really. Angular has built-in mechanisms that can assist here (see previous comment) that are not addressed in that other question. – Alnitak Jan 30 '18 at 09:35

3 Answers3

1

It's an asynchronous data. You don't know when it will arrive (after 200 ms or after a minute), so you can't assign it to a variable. You need to work with promises - asynchronous callbacks.

Your service should look like this:

$scope.findCompanyAddressById = function(cmpId) {
  return $http.get(someUrl + '/company/find?id=' + cmpId).
  then(function(response) {
    var address = "";
    $scope.company = response.data;

    for (var i = 0; i < $scope.company.locations.length; i++) {
      address += " " + $scope.company.locations[i].street1
      address += " " + $scope.company.locations[i].street2
      address += " " + $scope.company.locations[i].city
      address += " " + $scope.company.locations[i].state
      address += " " + $scope.company.locations[i].zip
    }
    return address;
  });
}

In short: return $http.get(url).then((res)=>{return ...});

You need both returns for a Promise from $http and your data.

Then your other code can have:

$scope.findCompanyAddressById( 1 ). // works as a promise
then((data)=>{ 
  $scope.concatenatedData = data;
})

Or you can pass it to your controller (function($scope,YourService)) and do the same:

YourService.findCompanyAddressById( 1 ).then(...)
Aleksey Solovey
  • 4,153
  • 3
  • 15
  • 34
  • Thanks it worked, but I want something like this, couldn't I do like it var address = $scope.findCompanyAddress( companyId ); ??? – Sikandar Sahab Jan 30 '18 at 12:16
  • @BadshahTracker you could create a global variable in a service, but that will create a memory leak if used in controllers. So you **have to** use Promises: `.then((data)=>{address=data;})` – Aleksey Solovey Jan 30 '18 at 12:19
  • @ AlekseySolovey is **address** usable outside **then**? – Sikandar Sahab Jan 30 '18 at 12:33
  • @BadshahTracker no, it will be used **before** `$http` call, and will always display _undefined_ – Aleksey Solovey Jan 30 '18 at 12:54
  • please tell me any way through which I can get the returned value in another variable and use it somewhere else? $scope.variable = $scope.findCompanyAddressById( 1 ); Think of using $scope.variable stored somewhere else in ng-model etc and then take it from there. Possible? – Sikandar Sahab Jan 31 '18 at 13:57
  • @BadshahTracker once a promise, always a promise. It's an asynchronous callback, you can't return anything from it as a value, you can only resolve it (with `.then`) – Aleksey Solovey Jan 31 '18 at 14:11
1

$http.get is asynchronous and returns a promise which you handle with then. Therefore, you're returning the variable address before the promise is resolved.

There are many ways of solving this problem, but this would be my way:

$scope.findCompanyAddressById = function( cmpId ) {

return $http.get(
    someUrl+'/company/find?id='+cmpId ).

then(function(response) {
    var address = '';
    $scope.company = response.data;

    for( var i=0; i < $scope.company.locations.length; i++ ) {
        address += " " + $scope.company.locations[i].street1
        address += " " + $scope.company.locations[i].street2
        address += " " + $scope.company.locations[i].city
        address += " " + $scope.company.locations[i].state
        address += " " + $scope.company.locations[i].zip

        console.log( "Rendering address: " );
        console.log( address ); // logs perfect data.
    }

    return address;

})

};

Now your function returns the promise. You would use it like this:

$scope.findCompanyAddressById( 1 )
.then(function(address){
     $scope.concatenatedData = address;
})

Here's the MDN documentation on Promise.

edit

Returning a new Promise is an anti-pattern, so the function now returns the promise from $http.get.

  • Creating a new Promise around a function that already returns a promise is a well known Promise _anti pattern_. – Alnitak Jan 30 '18 at 09:33
0

To help address, you must first understand a little bit about promises. Promises are asynchronous and because of that, they are not guaranteed to return right away. Think of a promise as "hey, I promise I'll do some work for you, then when I'm done, I'll give you the result back". This is most often used for HTTP calls back to the server.

One of the coolest things about asynchronous code is that it allows you to do some other work while the async code is executed. This is the very thing you are experiencing above.

Note that your return address is outside the .then function. To actually see the async nature in action, put a console.log right before your return statement and another inside the .then. You'll see that the console.log before your return is actually executed first!

That being said, you won't be able to work with async code like other code that you're used to. Once inside a promise, you really can't "escape" it. From here on out, you'll need to continue chaining onto that promise to do any work with the data.

$scope.findCompanyAddress(1).then(address => {
  /* do something with address */
})

Hope this helps!

Edit

Forgot to also mention that this means that you will now need to return the promise from your function instead of the address instead (the $http.get part).

jerelmiller
  • 1,643
  • 1
  • 10
  • 13