sharpmachine
sharpmachine

Reputation: 3893

Function from factory not working, but when used in controller it works. Why?

I have a function called updatedUser() that I defined in a factory. If I inject factory and assign the function to the scope to be used in the view and then invoke it, it doesn't work properly. I expect it to update the user. Instead it just creates a new user.

However when I take that same function and define it in the controller and invoke it in the view, it works as expected. I don't get it! Any help would be appreciated.

Here's my code:

Factory

(function(){
  'use strict';

  angular
  .module('casemanagerApp')
  .factory('$cmUserData', $cmUserData);

  function $cmUserData($log, Restangular) {

    return {
      users: users,
      getUser: getUser,
      updateUser: updateUser
    };

    function users() {
      return Restangular.service('users');
    }

    function getUser() {
      return users().one(1).get().$object;
    }

    function updateUser() {
      Restangular.all('users')
      .post(getUser())
      .then(function(){
        $log.info('User updated');
      });
    }

  }

})();

Controller

(function() {
  'use strict';

  angular
  .module('casemanagerApp')
  .controller('CasePlanGoalsCtrl', CasePlanGoalsCtrl);

  function CasePlanGoalsCtrl($log, $cmUserData) {
    var vm = this;

    // Injections
    vm.user = $cmUserData.getUser();

    // Functions
    // vm.updateUser = $cmUserData.updateUser; <-This doesn't work
    vm.updateUser = updateUser; // This will work

    function updateUser() {
      Restangular.all('users')
      .post(vm.user)
      .then(function() {
        $log.info(vm.user.firstName + ' was updated');
      });
    }

  }

})();

View. See ng-change

<md-switch 
    aria-label="Stack Cards" 
    ng-model="vm.user.settings.stackCards" 
    ng-true-value="'isStacked'" 
    ng-false-value="'!isStacked'" 
    ng-change="vm.updateUser()" 
    class="text-uppercase-small pull-right">
    Stack Cards
</md-switch>

Upvotes: 2

Views: 703

Answers (2)

sharpmachine
sharpmachine

Reputation: 3893

Needed to pass vm.user to the factory's updateUser() function because of all the awesome stuff @shieldstroy said:

Factory

(function(){
  'use strict';

  angular
  .module('casemanagerApp')
  .factory('$cmUserData', $cmUserData);

  function $cmUserData($log, Restangular) {

    return {
      users: users,
      getUser: getUser,
      updateUser: updateUser
    };

    function users() {
      return Restangular.service('users');
    }

    function getUser() {
      return users().one(1).get().$object;
    }

    function updateUser(model) {
      Restangular.all('users')
      .post(model)
      .then(function(){
        $log.info('User updated');
      });
    }

  }

})();

Controller

(function() {
  'use strict';

  angular
  .module('casemanagerApp')
  .controller('CasePlanGoalsCtrl', CasePlanGoalsCtrl);

  function CasePlanGoalsCtrl($log, $cmUserData) {
    var vm = this;

    // Injections
    vm.user = $cmUserData.getUser();

    // Functions
    vm.updateUser = $cmUserData.updateUser;

  }

})();

View. See ng-change

<md-switch 
    aria-label="Stack Cards" 
    ng-model="vm.user.settings.stackCards" 
    ng-true-value="'isStacked'" 
    ng-false-value="'!isStacked'" 
    ng-change="vm.updateUser(vm.user)" 
    class="text-uppercase-small pull-right">
    Stack Cards
</md-switch>

Upvotes: 1

shieldstroy
shieldstroy

Reputation: 1327

Your controller is working but it's actually doing something different than your factory. It is using the vm.user object long after Restangular.getUser() has been called to fill that variable. In order to break your controller and make it function the same as your factory, change this:

function updateUser() {
  Restangular.all('users')
  .post(vm.user)
  .then(function() {
    $log.info(vm.user.firstName + ' was updated');
  });
}

to

function updateUser() {
  Restangular.all('users')
  .post($cmUserData.getUser())
  .then(function() {
    $log.info(vm.user.firstName + ' was updated');
  });
}

Your controller has given Restangular a chance to resolve the object it is calling, whereas your service is calling the getUser function and immediately trying to use the result of that to do a POST.

If the above code breaks in the same way that your service breaks, then you at least know why the issue is happening. It's because Restangular is giving you an object that it promises to fill, but when your service updateUser function tries to use that object it isn't filled yet.

Restangular returns an object immediately when you call getUser, but at that moment there is nothing in that object! It is a shell that Restangular will fill when the call from the server comes back. You need to make sure you are passing an actual user object to your factory.

There isn't an obvious answer to this. Maybe you cache the result of getUser in your factory, or maybe you pass the user into your updateUser() factory function. I suspect Restangular has a way it suggests that you do this, but I'm not that familiar with it so I can't help out much with the specific there.

Upvotes: 1

Related Questions