2

In normal JS I can do this:

function Droppable() {
    this.relevant = true;
    this.uuid = generateUUID();
};

var generateUUID = function() {
   return '12345';
}


console.log(new Droppable); // returns Droppable {relevant: true, uuid: "12345"}

But in Angular I have this:

angular.module('myApp').controller('MyCtrl', ['$scope', function($scope) {

    function Droppable() {
        this.relevant = true;
        this.uuid = generateUUID();
    }

    var generateUUID = function() {
        return '12345';
    }

    // initalize droppable areas
    $scope.region1 = [new Droppable];
    $scope.region2 = [new Droppable];
    $scope.region3 = [new Droppable];

}]);

I am trying to make 3 droppable areas all with a UUID.

But when I do this I get 'undefined is not a function' referring to the line this.uuid = generateUUID(); in function Droppable() {...}

Why is that?

BigHeadCreations
  • 1,583
  • 3
  • 16
  • 31
  • 1
    Most Possibly http://stackoverflow.com/questions/25111831/controller-not-a-function-got-undefined-while-defining-controllers-globally/25111942#25111942 or http://stackoverflow.com/questions/25330193/angularjs-asp-net-mvc-controller-undefined/25330617#25330617 – PSL Aug 19 '14 at 03:06
  • I have just tried your code inside an angular controller and I got it working as expected. Do you know which version of angular you are using? I have tested in angular 1.2.15. – Kamrul Aug 19 '14 at 03:18
  • @PSL I don't think it is either one of those. I will edit the question to make it more clear. – BigHeadCreations Aug 19 '14 at 03:20
  • @Kamrul I am using 1.2.16. – BigHeadCreations Aug 19 '14 at 03:21
  • It must be a typo, app name not specified, controller registration not loaded etc.. then? Show us an example to replicate the issue. The code you have provided does not tell much. – PSL Aug 19 '14 at 03:22
  • @PSL I don't think that other question is the answer in my case because I have been working on this app for about a month now and am just running into this problem today. It is not a day one set up problem. All the other functionality in my controller is working just fine. – BigHeadCreations Aug 19 '14 at 03:59
  • 1
    OK more code. Now it makes sense.. Are you sure you are instantiating `Droppable` only after `var generateUUID=..` Change `var generateUUID = function() {` to `function generateUUID () {` and see what happens. http://plnkr.co/edit/NYrVx6?p=preview Or probably a [typical case of this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function#Function_declaration_hoisting) ? – PSL Aug 19 '14 at 04:05
  • @BigHeadCreations Sure i l add it. – PSL Aug 19 '14 at 04:18

3 Answers3

1
function Droppable() {
  this.relevant = true;
  this.uuid = generateUUID();
}
var generateUUID = function() {
  return '12345';
}

Okay, this is not a problem about AngularJS but it is more about Javascript. Read up on Javascript variable hoisting. When you declare a function using the var keyword, there are two things happens: You create a variable, and then assign a variable to it. Because of javascript hoisting, variable declaration (var generateUUID) will be hoisted to the top, where the declaration happens later where you wrote it.

var generateUUID;   //undefined
function Droppable() {
  this.relevant = true;
  this.uuid = generateUUID(); //you are invoking undefined here.
}
generateUUID = function() {
  return '12345';
}

the right way to do it would be:

function Droppable() {
  this.relevant = true;
  this.uuid = generateUUID();
}
function GenerateUUID() {
  return '12345';
}

Here, because you use by "function GenerateUUID()", the whole thing get hoisted to the top instead of only just the variable

1

Turning my comment to answer. Your issue is due to the variable holding the function expression is being used before it has been assigned, in which case the variable generateUUID is not defined yet.

var generateUUID = function() {
    return '12345';
}

One way to fix it, is to turn it to function declaration so that it is automatically hoisted to the top of the scope.

function generateUUID () {
        return '12345';
 }

or define the variable generateUUID to a function reference before its possible usage.

//Some variable declarations, start of the scope
var generateUUID = function() {
    return '12345';
}

function Droppable() {
    this.relevant = true;
    this.uuid = generateUUID();
}

When you do:-

.... Some Code ....


var generateUUID = function() {
        return '12345';
    }

.... Some Code ...

it is same as:-

var generateUUID;
.... Some Code ....
//Any usage of this till here will cause the error that you are facing, since there is no value assigned to generateUUID yet
generateUUID = function() {
            return '12345';
  }

.... Some Code ...

Some reference:- MDN, SO

and a Plnkr

Note:- This issue should not happen in your example because you are invoking the constructor after it (generateUUID) has been assigned with value, probably a different usecase (which is not present in the question) might be having this issue.

Community
  • 1
  • 1
PSL
  • 123,204
  • 21
  • 253
  • 243
1

Giving some quick examples

function Droppable() {
    this.relevant = true;
    this.uuid = generateUUID();
}

// this will throw an error because you are calling generateUUID before it initialize
console.log(new Droppable);

var generateUUID = function() {
   return '12345';
}

// this will give you actuall output what you wanted
console.log(new Droppable);

So when you will call function generateUUID before calling Droppable it will work. Like this -

// initializes
var generateUUID = function() {
    return '12345';
}

// initializes
function Droppable() {
    this.relevant = true;
    this.uuid = generateUUID();
}

// this will work as expected
console.log(new Droppable);

In JavaScript you can define function in two method

var varibaleName = function(){

}

and

function variableName(){

}

There is a significant differences. When you are declaring a function with a variable then before call that function that variable need to register.

if you call like this -

varibaleName();
var varibaleName = function(){

}

this will give you an error.

But if you call like this it will work -

varibaleName();
function varibaleName (){

}

So this should work too -

function Droppable() {
    this.relevant = true;
    this.uuid = generateUUID();
}

// this will work as expected
console.log(new Droppable);


function generateUUID() {
    return '12345';
}

Hope this make sense.

However in angular this will work -

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

myApp.controller("MyCtrl", function($scope){

    function Droppable() {
        this.relevant = true;
        this.uuid = generateUUID();
    }

    var generateUUID = function() {
        return '12345';
    }

    // initalize droppable areas
    $scope.region1 = [new Droppable];
    $scope.region2 = [new Droppable];
    $scope.region3 = [new Droppable];

    console.log( $scope.region1 );
});

But best practice is -

myApp.controller("MyCtrl", function($scope){

    // you can call this before Droppable or after
    $scope.generateUUID = function() {
        return '12345';
    }

    $scope.Droppable = function () {
        this.relevant = true;
        this.uuid = $scope.generateUUID();
    }

    // initalize droppable areas
    $scope.region1 = [new $scope.Droppable];

    console.log( $scope.region1 );
});

Why i am calling those with $scope. prefix, because later you may need to talk between controller to controllers even in directives!

Happy coding and happy prototyping! How this helps you to understand how things are working.

HADI
  • 2,829
  • 1
  • 26
  • 26