简体   繁体   中英

Angularjs : variable which is returned by a function in controller is “undefined”

<script type="text/javascript">
angular.module('angularApp', ['dialogs.main'])
.controller('TableController', function(dialogs, $http){

    function getEmailAddress(){
        var emailAddress ="empty";
        $http({
            //The http method is defined here, which works as expected
        }).then(function succes(response) {
            emailAddress = response.data;
            console.log(emailAddress + " : getEmailAddress success");//From this log, I can see what I expected
            return emailAddress; //However, this doesn't return the value, which can be seen in the log.
            // return {
            //  emailAddress:emailAddress
            // };  //This way doesn't work either
        }, function resendError() {
            return "Error!";
        });
    };

    this.resendConfirmationMail = function(orderId){
        //Inside this function, the function above is going to be used.
    };
});
</script>

What I'm trying to do is to create/use a function which returns a value, which comes from a http function.

As I described above, there are two functions, one of which is supposed to return a variable, called emailAddress , but the returned variable is described as undefined , not even empty , as its initial value.

I can see the right return value from console.log , but I can't just return it.

I would appreciate any advice.

getEmailAddress is an async function (as it uses $http which is also async). You can return the promise from the getEmailAddress method and use then to grab the email address:

function getEmailAddress() {
    return $http({
        //The http method is defined here, which works as expected
    });
}

this.resendConfirmationMail = function (orderId) {
    getEmailAddress().then(function (emailAddress) {
        console.log(emailAddress);
    });
};

It might help to brush up on async programming and Promises

Here is a great answer summarising using async functions.

As your working with $http your using promises.

As such its async. I'm not too sure if this is what your trying to do but feel its best to mention you can't return a value and expect to consume it straight away. By that i mean this won't work:

function test () {
    $http.get('url')
        .then(x => x.data);
}

var data = test();

What you need to do is return a promise with you then register against to say 'run my code when the promise resolves'. In this case the promise will resolve once the $http request returns. As $http returns a promise for you all you have to do is return it.

function test () {
    return $http.get('url')
        .then(x => x.data);
}

test().then(data => {
    //use data
});

So in your code just return before $http .

You need to return the promise from the $http call, and then when you want the value you have to use .then() to retrieve it.

angular.module('angularApp', ['dialogs.main'])
.controller('TableController', function(dialogs, $http){

    function getEmailAddress(){
        var emailAddress ="empty";
        return $http({
            //The http method is defined here, which works as expected
        }).then(function succes(response) {
            emailAddress = response.data;
            return emailAddress;
        }, function resendError() {
            return "Error!";
        });
    };

    this.resendConfirmationMail = function(orderId){
        //Inside this function, the function above is going to be used.
        getEmailaAddress().then(function(emailAddress) {
            // use the email address here.
        });
    };
});

The important thing to remember here is thet $http runs asynchronously, so you cannot 'return a value' from it and use that in code unless that code is inside .then() or .catch() success or failure handlers and those handler will not run until later

You should return promise by using $q

function getEmailAddress(){
        var deferred = $q.defer();
        var emailAddress ="empty";
        $http({
            //The http method is defined here, which works as expected
        }).then(function (response) {
            emailAddress = response.data;
            console.log(emailAddress + " : getEmailAddress success");//From this log, I can see what I expected
             deferred.resolve(emailAddress)
        }, function () {
            deferred.reject(""Error!")
        });
       return deferred.promise;
    };

Here your getEmailAddress function return nothing. You have to return at least $http (which return a promise) an handle the result as a promise. Or you could better create a promise instance in getEmailAddress and handle it directly in resendConfirmationMail

 function getEmailAddress(){
   var deferred = $q.defer();
   var emailAddress ="empty";
   $http({
     //The http method is defined here, which works as expected
   }).then(function succes(response) {
     emailAddress = response.data;
     deferred.resolve(emailAddress)
   }, function resendError() {
     deferred.reject("Error");
   });
   // This is what you return
   return deferred.promise;
};

this.resendConfirmationMail = function(orderId){
  getEmailAddress()
    .then(function(emailResponse){
      // Do the things
    }, function(errorMessage){
      // Error handler
    }
  );
};

Usefull documentation page: https://docs.angularjs.org/api/ng/service/ $q

( simple plunkr )

$http, in fact, returns a promise. Also, a good pattern is to first handle the returned promise in the service for any data related actions and then pass that promise up the 'chain' to the controller where UI actions can be handled. I have included a simplified plunkr that shows how these promises are passed from the service to the controller. Just click on the 'get data' button in the controller and you'll see how the events work with a series a 'alert()s'. Note, I have used $timeout as a substitute for $http. The promise handling will be identical using $http. More details below:

index.html: Starts everything. The get data button invokes getData() method in the controller.

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <script data-require="angular.js@1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
    <script src="app.js"></script>
    <script src="mock_ajax_service.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Click on button to request data and handle the promise chain</p>

    <div>
      <button ng-click="getData()">Get Data!</button>
    </div>
  </body>

</html>

app.js: getData(), in turn, invokes the requestData() method in the 'mock ajax' service. It waits on a promise to return from the service and upon return handles UI matters, ie shows an alert.

var app = angular.module('plunker', ['mockjaxservice_module']);

app.controller('MainCtrl', ['$scope', 'mockajaxservice', 
  function($scope, mockajaxservice) {
  $scope.name = 'World';

        var requestDataSuccess = function (results) {

      console.log('handle success data in controller/UI');
      alert('handle success data in controller/UI');
          return;
        }

        var requestDataFail = function (results) {

          console.log('handle failed data in controller/UI');
          return;
        }     

  $scope.getData = function () {

    var UIPromise = mockajaxservice.requestData();

    UIPromise.then(requestDataSuccess,requestDataFail);
  }
}]);

mock_ajax_service.js: Here, the service simulates a mock ajax call using $timeout which is invoked by the controller button. $timeout, much like $http, returns a bonafide promise. Once the promise returns, it is handled (an alert), and the promise is passed up the chain to the controller (return that;)

'use strict';

var mockAjaxModule = angular.module('mockjaxservice_module', []);

mockAjaxModule.factory('mockajaxservice', ['$timeout', function ($timeout) {

    this.service_count = 0;

    this.service_count_object = {

        count:0
    }

    var that = this; 

    var requestDataSuccess = function (results) {
        console.log('handle success data in service');  
        alert('handle success data in service then pass it up to controller');
        that.service_count = 10;
        that.service_count_object.count = 10;
        that.utcTime = Math.floor((new Date()).getTime() / 1000);
      return that;
    }

    var requestDataFail = function (results) {

      console.log('handle failed data in service')
      return that;
    }       

    this.requestData = function () {

        var requestDataPromise = $timeout(function() {

        }, 500);

        requestDataPromise.then(requestDataSuccess,requestDataFail);

        return requestDataPromise;

    }

    return this;

}]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM