1

I built this factory service:

spApp.factory('siteCollection', function(){
  return {
    usersObject : [],
    getUsers : function (){
      $().SPServices({
        operation: "GetUserCollectionFromSite",
        completefunc: function(xData, Status) {
          var responseXML = $(xData.responseXML);
          responseXML.find("User").each(function() {
            usersObject.push({
              id: $(this).attr("ID"),
              name: $(this).attr("Name"),
              domainAccount: $(this).attr("LoginName")
            });
          });
        }
      });
      return usersObject;
    }
  }
})

It's suppose to return the usersObject which I declared at the top but the console is giving me an undefined error for the object.

This is the controller:

spApp.controller('userCtrl', 
    function userCtrl($scope,siteCollection){
        $scope.users = siteCollection.getUsers();
    }
);

I'm pretty new to Angular.

2 Answers 2

3

There are two issues with your code:

The first: your factory returns an object with the properties usersObject and getUsers, but in getUsers you try to access a variable "usersObject" (which is not the property of the returned object). You have to declare the variable outside:

var usersObject = [];
return {
  getUsers: function () {
    // ...
    return usersObject;
  }
};

The second: you fill your usersObject in a callback function which is called asynchronously. AngularJS will not register the changes in your array. You can use $rootScope.$apply(), then AngularJS will run a digest and update the views after the new data has been added to the array.

spApp.factory('siteCollection', function ($rootScope) {
  // ...
  $rootScope.$apply(function () {
    responseXML.find("User").each( function () { ... } );
  });
}

Using $rootScope.$apply() is not very clean. A better way would be to return a promise:

spApp.factory('siteCollection', function ($q) {
  return {
    getUsers : function (){
      var deferred = $q.defer();

      $().SPServices({
        operation: "GetUserCollectionFromSite",
        completefunc: function(xData, Status) {
          var responseXML = $(xData.responseXML),
              usersObject = [];

          responseXML.find("User").each(function() {
            usersObject.push({
              id: $(this).attr("ID"),
              name: $(this).attr("Name"),
              domainAccount: $(this).attr("LoginName")
            });
          });

          deferred.resolve(usersObject);
        }
      });

      return deferred;
    }
  }
});

If you decide to use promises, add this to your controllers to do something after the data has been loaded:

siteCollection.getUsers.then(function (users) {
  // ...
});
Sign up to request clarification or add additional context in comments.

2 Comments

Hey the first change fixed the issue. However, I don't really understand the need for promises here. I'm using an ng-repeat and it seems to be working. How would promises help? Thank you.
You load data from the server with an external library (jQuery/SPServices). When this process runs asynchronously (and I hope that it does so) the script continues running and your callback is executed as soon as the data is loaded. But Angular will not know when this process finishes and therefore it will not run a digest cycle, will not execute watches, update the view etc. When you use a promise, Angular runs a digest cycle after the promise's fulfillment/rejection handlers have been executed.
0

It should look like this

spApp.factory('siteCollection', function(){
  var usersObject = [];
  return {
    getUsers : function (){...

Your return is an object of methods to access the data, but they are unrelated in scope. So if you only define usersObject in the return object, it is not accessible within getUsers.

Comments

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.