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?

8
  • 1
    Most Possibly stackoverflow.com/questions/25111831/… or stackoverflow.com/questions/25330193/… Commented Aug 19, 2014 at 3: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. Commented Aug 19, 2014 at 3:18
  • @PSL I don't think it is either one of those. I will edit the question to make it more clear. Commented Aug 19, 2014 at 3:20
  • @Kamrul I am using 1.2.16. Commented Aug 19, 2014 at 3:21
  • 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. plnkr.co/edit/NYrVx6?p=preview Or probably a typical case of this ? Commented Aug 19, 2014 at 4:05

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

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

Comments

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.

3 Comments

Yep, that was my problem exactly. Thanks for going into an explanation of why. However PSL beat you to the answer :)
@BigHeadCreations no matter who beats whom. You've got your solution that what we need :) we are happy to help you!
upvoted your comment for proper grammar - 'who beats whom' - impressive :) @HADI

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.