0

I'm trying to convert my $scope code to 'ControllerAs' code and I am having trouble writing a function inside my controller function.

index.html

<html ng-app="main">
    <head>
        <script src="angular.min.js"></script>
        <script src="script.js"></script>
    </head>

    <body ng-controller="MainController as mainCtrl">
        {{mainCtrl.message}}
        {{mainCtrl.result.username}}
    </body>
</html>

script.js

(function() {       

    angular.module("main", [])
        .controller("MainController", ["$http",MainController]);

    function MainController($http) {
        this.message = "Hello Angular!";
        this.result = callFunction($http);

        var callFunction = function($http) {
            return $http.get("https://api.github.com/users/robconery")
                .then(onUserComplete);      
        };

        var onUserComplete = function($response) {
            return $response.data;
        };
    };

}());

Here is the $scope code that I am trying to convert.

(function() {

  var app = angular.module("githubViewer", []);

  var MainController = function($scope, $http) {

  var onUserComplete = function(response) {
    $scope.user = response.data;
  };

  var onError = function(reason) {
    $scope.error = "Could not fetch the user";
  };


  $http.get("https://api.github.com/users/robconery")
    .then(onUserComplete, onError);


  $scope.message = "Hello, Angular!";


};

  app.controller("MainController", ["$scope", "$http", MainController]);

}());
LinhSaysHi
  • 632
  • 3
  • 14
  • 28
  • Not getting anything in the firefox web console – LinhSaysHi Jul 18 '15 at 17:41
  • So no error, what is not working as expected then? – ggdx Jul 18 '15 at 17:41
  • Getting the literal {{mainCtrl.message}} and not getting the actual data. Function is not returning data to 'result' – LinhSaysHi Jul 18 '15 at 17:42
  • 1
    The thing not working is OP is invoking `callFunction` which is undefined when he invokes it. The literal is appearing because Angular encountered an error, if you run this code in another browser/codepen/jsfiddle you will see an error displayed to the user. – Dan Jul 18 '15 at 17:43
  • Google chrome finds the error and reports: 'TypeError: callFunction is not a function'. Is my syntax incorrect? – LinhSaysHi Jul 18 '15 at 17:46
  • See my answer. `var callFunction - function() {}` creates a variable called `callFunction` and assigns it an unnamed function. You'd need to wait until that line is executed before `callFunction` has a value assigned to it - until that point, `callFunction` is equal to `undefined`. This means the `this.result` line is trying to invoke an `undefined` function and thus errors. You'd get the same error with `onUserComplete`. – Dan Jul 18 '15 at 17:47
  • possible duplicate of [var functionName = function() {} vs function functionName() {}](http://stackoverflow.com/questions/336859/var-functionname-function-vs-function-functionname) – Dan Jul 18 '15 at 17:51

2 Answers2

2

You are invoking callFunction before it is defined. You need to either use a function declaration or move callFunction before the invocation. Here's an example of both of those choices.

Function Declaration

(function() {       

    angular.module("main", [])
        .controller("MainController", ["$http",MainController]);

    function MainController($http) {
      this.message = "Hello Angular!";
      this.result = callFunction($http);
    }

    function onUserComplete(response) {
      return response.data;
    }

    function callFunction($http) {
      return $http.get('https://api.github.com/users/robconery')
        .then(onUserComplete);
    }
}());

Or:

(function() {       

    angular.module("main", [])
        .controller("MainController", ["$http",MainController]);

    var callFunction = function($http) {
        return $http.get("https://api.github.com/users/robconery")
                .then(onUserComplete);      
    };

    var onUserComplete = function($response) {
        return $response.data;
    };

    function MainController($http) {
        this.message = "Hello Angular!";
        this.result = callFunction($http);
    }
}());

See this excellent StackOverflow answer for the differences between these two syntaxes.

Community
  • 1
  • 1
Dan
  • 9,064
  • 1
  • 29
  • 59
  • Does the order matter when using the function onUserComplete() syntax? – LinhSaysHi Jul 18 '15 at 17:48
  • No. Function declarations are defined at parse time and can be placed in any order. Function expressions, however, are sensitive to order (in the same way that any variable is). The answer I've linked goes into this in much more detail. – Dan Jul 18 '15 at 17:48
  • Any reason why the http get is getting back empty curly braces? – LinhSaysHi Jul 18 '15 at 18:04
  • empty curly braces indicates an empty object. Since it's not a rejected promise, you likely have a problem with github's API, which is out of the scope of this question. Make sure to check the http response code as well. – Dan Jul 18 '15 at 18:05
  • Okay, thank you. Just seems odd that it would work with $scope and not this way – LinhSaysHi Jul 18 '15 at 18:11
  • You didn't paste your $scope code so I can't comment :( – Dan Jul 18 '15 at 18:12
  • Edited :) Sorry to be a bother! – LinhSaysHi Jul 18 '15 at 18:22
  • in your $scope code you have the functions the way I put in my answer which is why that works. in your controllerAs code you do not – Dan Jul 18 '15 at 18:26
  • @LinhSaysHi empty curly brace in my case, too. I change the code and it works. See my answer. – NeoWang Jul 18 '15 at 18:33
  • Is there a difference between having the callFunction and onUserComplete functions inside the MainController function? – LinhSaysHi Jul 18 '15 at 19:06
0

Aside from the function definition problem in @Dan's answer, there is another issue: you should not bind callFunction($http) in the template, callFunction($http) returns a Promise, it doesn't evaluates to the response, even after the onUserComplete callback.

This works for me:

function callFunction($http){
    return $http.get("https://api.github.com/users/robconery")
            .then(onUserComplete);
}
var that = this;
function onUserComplete($response){
    that.result = $response.data;
}
callFunction($http);

EDIT: Your $scope version code works fine, because in the onUserComplete() function, you assign to $scope.result, not return $response.data. You see, when you return from onUserComplete, then() doesn't return that, it still returns the promise, that's because it needs to support chaining.

NeoWang
  • 13,687
  • 19
  • 61
  • 112
  • Good catch. I missed that. – Dan Jul 18 '15 at 18:35
  • OH MY GOOOOSSSSSSSSSSH. thanks :) AngularJS is hard. The binding issue is fairly odd to me and seems really different from what I am use to, which is programming in Java. Is there a name for a rule like this? Like a design pattern or something? – LinhSaysHi Jul 18 '15 at 18:48
  • Is there also a cleaner way of doing this? The 'var that = this' seems odd to me, even if the naming of 'that' were to be different. – LinhSaysHi Jul 18 '15 at 18:48
  • @LinhSaysHi without using ES6 arrow functions, a language like coffeescript, or `bind`/`call`/`apply`, no. `var that = this` ensures that the `this` remains the same inside the closure as it is outside – Dan Jul 18 '15 at 18:51