12

I am trying to include a library of functions, held in a factory, into a controller. Similar to questions like this: Creating common controller functions

My main controller looks like this:

recipeApp.controller('recipeController', function ($scope, groceryInterface, ...){

$scope.groceryList = [];
// ...etc...    

/* trying to retrieve the functions here */
$scope.groceryFunc = groceryInterface; // would call ng-click="groceryFunc.addToList()" in main view
    /* Also tried this:
    $scope.addToList = groceryInterface.addToList();
    $scope.clearList = groceryInterface.clearList();
    $scope.add = groceryInterface.add();
    $scope.addUp = groceryInterface.addUp(); */
}

Then, in another .js file, I have created the factory groceryInterface. I've injected this factory into the controller above.

Factory

recipeApp.factory('groceryInterface', function(){

        var factory = {};

    factory.addToList = function(recipe){
        $scope.groceryList.push(recipe);
                    ... etc....
    }

    factory.clearList = function() {
            var last = $scope.prevIngredients.pop();
            .... etc...
    }

    factory.add = function() {
    $scope.ingredientsList[0].amount = $scope.ingredientsList[0].amount + 5;
    }

    factory.addUp = function(){
        etc...
    }

    return factory;
});

But in my console I keep getting ReferenceError: $scope is not defined at Object.factory.addToList, etc. Obviously I'm guessing this has to do with the fact that I'm using $scope in my functions within the factory. How do I resolve this? I notice that in many other examples I've looked at, nobody ever uses $scope within their external factory functions. I've tried injecting $scope as a parameter in my factory, but that plain out did not work. (e.g. recipeApp.factory('groceryInterface', function(){ )

Any help is truly appreciated!

3 Answers 3

25

Your factory can't access your $scope, since it's not in the same scope.

Try this instead:

recipeApp.controller('recipeController', function ($scope, groceryInterface) {

    $scope.addToList = groceryInterface.addToList;
    $scope.clearList = groceryInterface.clearList;
    $scope.add       = groceryInterface.add;
    $scope.addUp     = groceryInterface.addUp;
}

recipeApp.factory('groceryInterface', function () {

    var factory = {};

    factory.addToList = function (recipe) {
        this.groceryList.push(recipe);
    }

    factory.clearList = function() {
        var last = this.prevIngredients.pop();
    }
});

Alternatively, you can try using a more object oriented approach:

recipeApp.controller('recipeController', function ($scope, groceryInterface) {

    $scope.groceryFunc = new groceryInterface($scope);
}

recipeApp.factory('groceryInterface', function () {

    function Factory ($scope) {

        this.$scope = $scope;
    }

    Factory.prototype.addToList = function (recipe) {
        this.$scope.groceryList.push(recipe);
    }

    Factory.prototype.clearList = function() {
        var last = this.$scope.prevIngredients.pop();
    }

    return Factory;
});
Sign up to request clarification or add additional context in comments.

6 Comments

@Gnuey - Note that bind is not available in older versions of IE. If you have to support those and want to use the first method, either use this MDN polyfill, or - if you have jQuery on the page - use $.proxy instead.
Okay, great. That is actually extremely helpful info -- my dept head and site visitors use older IE versions at times for some reason :\ Would I instead write $scope.addToList = $.proxy(groceryInterface.addToList, $scope) ?
@Gnuey - Yes. Exactly like that.
Hmm, I am getting TypeError: Cannot call method 'push' of undefined at Object.factory.addToList I forgot to mention that groceryList is an array in the main controller ($scope.groceryList = []). Does this make a difference?
@Gnuey - Can you create a short concise fiddle of your problem?
|
4

You cannot use $scope in a factory as it is not defined. Instead, in your factory functions change the properties of the object the factory is returning, e.g.

factory.addToList = function (recipe) {
    this.groceryList.push(recipe);
}

these will then get passed on to your $scope variable

$scope.addToList = groceryInterface.addToList;
// ... = groceryInterface.addToList(); would assign to `$scope.addToList` what is returned, instead of the function itself. 

1 Comment

I edited your solution so that instead of $scope.addToList = groceryInterface.addToList();, it reads $scope.addToList = groceryInterface.addToList; I discovered that the parenthesis at the end of addTotList() was one big problem during my debug process... got myself quite a nice refreshing slap of javascript syntax...
3

This isn't the exact answer for this question, but I had a similar issues that I solved by simply passing $scope as an argument to a function in my factory. So it won't be the normal $scope, but $scope at the time the function in the factory is called.

app.controller('AppController', function($scope, AppService) {


  $scope.getList = function(){

    $scope.url = '/someurl'

    // call to service to make rest api call to get data

    AppService.getList($scope).then(function(res) {
      // do some stuff 

    });
  }

});


app.factory('AppService', function($http, $q){
  var AppService = {

    getList: function($scope){
      return $http.get($scope.url).then(function(res){
        return res;
      });
    },

  }

  return AppService;
});

1 Comment

DRY AF.. and a way to pass params into factory functions

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.