2

I have created an AngularJS factory in which I'm returning an object which has some functions as properties. My purpose is to call some function in another function from that returning using the this keyword but its giving an error. I tried to console.log the this keyword and I found out that, that this variable holds the scope of calling controller in which the factory is being injected. How can I tackle this kind of situation. this keyword should return the current returning object!

 app.module('admin').factory('login', [function() {
return {
    someFunc: function() {
        return 'abc'
    },
    anotherFunc : function(){

        var msg = this.someFunc();
        return 'Msg is '+ msg;

    },
    yetAnotherFunc : function(){

        var msg = this.someFunc();
        return 'Msg is '+ msg;

    }
}
}]).controller(['$scope', 'login', function($scope, login){
    login.someFunc(); // Works Fine
    login.anotherFunc(); // Error : this.someFunc is not a function
}])
Param Singh
  • 1,037
  • 3
  • 11
  • 27
  • Not sure if this is the solution but have you tried binding this to it ? – Bob Thomas May 20 '15 at 13:30
  • You could also just define the functions outside the return statement. – Bob Thomas May 20 '15 at 13:32
  • binding this to it?? how – Param Singh May 20 '15 at 13:32
  • but I want to return those functions too. – Param Singh May 20 '15 at 13:32
  • You can you just define them so they get hoisted and return the reference. like function bar() { console.log('foo') }; and then return { bar:bar} – Bob Thomas May 20 '15 at 13:35
  • there is an answer which will work for your scenario on your question here. For a more general overview of why this is occurring, however, there is a widely accepted answer on the subject already. This is not an angular issue, it is simply the way that closures work in JavaScript. See http://stackoverflow.com/questions/111102/how-do-javascript-closures-work/111111#111111. – Claies May 20 '15 at 13:46

3 Answers3

1

Try this:

app.module('admin').factory('login', [function() {
    function someFunc() {
        return 'abc';
    }

    return {
        someFunc: someFunc,
        anotherFunc : function(){

            var msg = someFunc();
            return 'Msg is '+ msg;

        },
        yetAnotherFunc : function(){

            var msg = someFunc();
            return 'Msg is '+ msg;

        }
    };
}]).controller(['$scope', 'login', function($scope, login){
    login.someFunc();
    login.anotherFunc();
}]);

Now the someFunc will be visible in the scope of the returned object.

EDIT:

About the this issue, I think you're misunderstanding how this works in Javascript.

The this keyword will only work "properly" in certain circumstances:

  • you're invoking a function on an object that was created using the new keyword
  • you're invoking a bound function (see the docs)
  • you're invoking a function using call or apply and giving it a context (this)

Looking at Waxolunist's answer, I can see that the code you posted in fact works, so you're probably doing something else that's causing the error. I'm guessing it looks something like this

$scope.yetAnotherFunc = login.yetAnotherFunc

This will "detach" yetAnotherFunc from its context, and that is why this is not what you expect it to be.

pablochan
  • 5,269
  • 23
  • 40
1

Here is the solution in a fiddle:

https://jsfiddle.net/waxolunist/fcxu5eej/

HTML:

<div ng-app="app">
    <div ng-controller="LoginController">
        Get a Message:
        <button ng-click="someFunc()">someFunc</button>
        <button ng-click="anotherFunc()">anotherFunc</button>
        <button ng-click="yetAnotherFunc()">yetAnotherFunc</button>

        <div>Answer: {{answer}}</div>
    </div>
</div>

JS:

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

app.controller('LoginController', function($scope, LoginFactory) {

    $scope.someFunc = function() {
        $scope.answer = LoginFactory.someFunc();
    }

    $scope.anotherFunc = function() {
        $scope.answer = LoginFactory.anotherFunc();
    }

    $scope.yetAnotherFunc = function() {
        $scope.answer = LoginFactory.yetAnotherFunc();
    }
});

app.factory('LoginFactory', [function() {
    return {
        someFunc: function() {
            return 'abc'
        },
        anotherFunc : function(){
            var msg = this.someFunc();
            return 'Msg is '+ msg;
        },
        yetAnotherFunc : function(){
            var msg = this.someFunc();
            return 'Another msg is '+ msg;
        }
    }
}]);

But I really doubt that a factory is the right thing to use. Maybe a service would be better suited. But basically your code works.

Christian
  • 2,019
  • 1
  • 18
  • 40
  • Doesn't it work for you in your browser or do you want an explanation? I don't know the rest of your code, but the snippet you posted is working. – Christian May 21 '15 at 05:02
  • $scope.loginFb = fbLogin.initLogin; Its there in my other js file. That's why it wasn't working. The explanation is in the above comment. – Param Singh May 21 '15 at 06:06
0

https://jsfiddle.net/6fwu7ghs/

HTML

<div ng-app="test" ng-controller="testController">
<input type="button" ng-click="testFunc2()" value="test2" />
<input type="button" ng-click="testFunc1()" value="test1" />
</div>

JS

var testModule = angular.module('test',[ ]);
testModule.controller('common' , function(  $scope ) {
$scope.testFunc1 = function(){
    alert("test1");
    };
});
testModule.controller('testController', function( $scope , $controller ){ 
//inherit
$controller('common', {
    $scope: $scope
});

$scope.testFunc2 = function (){
alert("test2");
};
});

how about this way. I'm just using controller with 'inherit'

Do you really need to use 'Factory' ? then this is not an answer.

Steven
  • 31
  • 1
  • 3