AngularJS Bind service array variable to controller scope
Asked Answered
W

2

9

I have been able to properly bind an object with primitive data from a service to a scope variable from a controller but I'm having trouble doing the same thing with an array variable from the service.

service.js

myServices.factory('MyService', ['$http', function($http) {
var mySets = [];

  return {
  initMySets: function(userId, success, error) {
    $http.get(apiUrl + "/controller/getmysets", {params:{id:userId}}).success(function (data) {
      mySets = [];
      angular.copy(data, mySets);
    }).error(error);
  },

  getMySets: mySets


}]);

controllers.js

myControllers.controller('MenuController', ['$scope', 'UserService', 'MyService',
  function ($scope, UserService, MyService) {

  $scope.user = UserService.user;
  $scope.mySets = MyService.getMySets;

  $scope.logout = function() {
    UserService.logout();
  }
}]);

index.html

<nav id="app-menu"  ng-controller="MenuController" ng-class="{hide:!global.showMenu,slide:global.showMenu}">
  <div id="menu-head">
    <h4>{{user.Email}}</h4>
  </div>
  <ul>
    <div ng-switch on="user.UserId != null">
      <li ng-switch-when="true"><a href="#/main" ng-click="toggleMenu();logout();">Logout</a></li>
      <li ng-switch-when="false"><a href="#/login" ng-click="toggleMenu()">Login</a></li>
      <li ng-switch-when="true" ng-repeat="mySet in mySets">
        <a href="#/mySet /{{mySet.MySetId}}" ng-click="toggleMenu()">{{mySet.Label}}</a>
      </li>
    </div>
  </ul>
</nav>

I have a login function that calls initMySets on success passing in the userId and I know that the mySets variable in the service is getting populated properly on the angular copy but I'm not getting the actual update in the controller.

The $scope.user variable is being updated from the UserService, but the mySet ng-repeat isn't displaying the list from the service.

The data being returned from the http.get is an IEnumerable collection of complex objects from my MVC Web API controller. I'm not sure if that makes a difference.

Woundwort answered 7/1, 2014 at 8:18 Comment(2)
Take a look at $apply() method: docs.angularjs.org/api/ng.$rootScope.ScopeMidday
@Cherniv whilst $apply does force an update and may be useful to some, services themselves don't have scopes and calling this method on the $rootScope can be expensive.Lunarian
N
16

Binding does not work because your array (in your controller) never change.

When your initSets success function is called, your internal array points to a new array but your scope variable still points to your empty initial array.

Instead of create a new array, clear your initial array and push new items to initial array:

Replace that:

mySets = [];
angular.copy(data, mySets);

By that:

mySets.length = 0; // Clear initial array
mySets.push.apply(mySets, data); // Push data to initial array

See this fiddle (I simulate an asynchronous callback using $timeout) : http://jsfiddle.net/UJTPD

Nary answered 7/1, 2014 at 10:29 Comment(4)
Thanks, I'm seeing the data in my controller scope now if I render {{mySets[0].Label}} on the view, however the ng-repeat looping through mySets isn't updating. Do I need to do something else to "restart" that loop?Woundwort
Okay it worked when I took off the ng-switch-when="true" on the ng-repeat. Any idea why this was causing an issue?Woundwort
That's a lifesaver!! I was struggling for hours to solve a similar problem. Thanks joshua.johnson.814 for asking and thanks @Nary for the answerDowner
@mickael sweet geesuz, I have no clue wtf you are doing or why. All I know is that this saved my life. wowUnitarianism
L
0

Instead of assigning a new list (mySets = []) and then calling angular.copy, you should just be calling angular.copy;

Like this;

angular.copy(data, mySets);
Lunarian answered 26/1, 2016 at 8:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.