Pavel Rogala
Pavel Rogala

Reputation: 163

Can't access value of scope in AngularJS. Console log returns undefined

I'm using ui-router for my application and nesting controllers within ui-view. My parent controller looks like this:

'use strict';

angular.module("discussoramaApp").controller("mainController", ["RestFullResponse", "Restangular", "localStorageService", "$scope", function(RestFullResponse, Restangular, localStorageService, $scope){

  var currentId = localStorageService.get("***");

  var user = Restangular.one("users", currentId);
  var Profile = user.get({}, {"Authorization" : localStorageService.get('***')}).then(function(profile) {
    $scope.profile = profile;
  });

}]);

And my child controller:

'use strict';

angular.module("discussoramaApp").controller("getTopicsController", ["RestFullResponse", "Restangular", "localStorageService", "$scope", function(RestFullResponse, Restangular, localStorageService, $scope){

  var topics = Restangular.all('topics');
  var allTopics = topics.getList({},{"Authorization" : localStorageService.get('***')}).then(function(topics){
    $scope.topics = topics;
  });

  console.log($scope); // this works
  console.log($scope.profile); // this returns undefined

}]);

The problem I'm having is getting the inherited $scope value for profile in the child controller. When I log $scope, profile is clearly visible in the console.

$scope console log

But when I try to log $scope.profile the console returns undefined. Any ideas?

Edit: Adding my ui-router config.

angular.module("discussoramaApp").config(
  function($stateProvider, $urlRouterProvider){
    $urlRouterProvider.otherwise('/home');
    $urlRouterProvider.when('', '/home');

    $stateProvider

      .state('main',{
        url: '',
        templateUrl: 'partials/main.html',
        requireLogin: true
      })

      .state('main.home',{
        url: '/home',
        templateUrl: 'partials/main.home.html',
        requireLogin: true,
        title: 'Home'
      });

  }
);

And the corresponding html files:

// main.html
<div ng-controller="mainController">

  <div class="container">
    <div ui-view></div>
  </div>

</div>

and the child html partial:

// main.home.html
<div ng-controller="getTopicsController">
  <div ng-repeat="topic in topics | filter:search">
    <a ui-sref="main.topic({id: topic.id})">{{ topic.topic_title }}</a>
  </div>
</div>

UPDATE: Solved this with a watcher set up like this in the child controller. Thanks @jonathanpglick and @Nix for the help.

$scope.$watch('profile', function(profile) {
  if(profile) {
    $window.document.title = "Discussorama | " + profile.user.name;
  }
});

Upvotes: 1

Views: 3080

Answers (2)

jonathanpglick
jonathanpglick

Reputation: 326

$scope.profile is being set after an asynchronous request so I suspect that the second controller is being instantiated before user.get() returns and assigns a value to $scope.profile.

I think you'll want to set up a watcher (like $scope.$watch('profile', function(profile) {});) in the child controller so you can do things when the profile becomes available or changes.

Also, the reason you can see the profile key on $scope when you console log $scope can be explained here: https://stackoverflow.com/a/7389177/325018. You'll want to use console.dir() to get the current state of the object when it's called.

UPDATE:

I just realized you're using the ui-router and so there's an even easier way to do this. The ui-router has a resolve object that you can use to dependency inject things like this into your controller. Each resolve function just needs to return a value or a promise and it will be available for injection into the controller with resolve key name. It would look like this for you:

angular.module("discussoramaApp").config(
  function($stateProvider, $urlRouterProvider){
    $urlRouterProvider.otherwise('/home');
    $urlRouterProvider.when('', '/home');

    $stateProvider

      .state('main',{
        url: '',
        templateUrl: 'partials/main.html',
        requireLogin: true,
        resolve: {
          profile: ['Restangular', 'localStorageService', function(Restangular , localStorageService) {
            var currentId = localStorageService.get("***");
            var user = Restangular.one("users", currentId);
            return user.get({}, {"Authorization" : localStorageService.get('***')});
          }
        }
      })

      .state('main.home',{
        url: '/home',
        templateUrl: 'partials/main.home.html',
        requireLogin: true,
        title: 'Home'
      });

  }
);

angular.module("discussoramaApp").controller("mainController", ["profile", "$scope", function(profile, $scope){
  $scope.profile = profile;
}]);

Upvotes: 2

Nix
Nix

Reputation: 58522

Just because you have nested scope, doesn't mean it will wait for user.get() to return before instantiating your nested getTopicsController.

Your issue is:

  1. mainController controller initializes and calls user.get()
  2. getTopicsController initializes and logs console.log($scope.profile)
  3. The call to user.get() returns and then sets on scope.

This is a common issue, if you need to gaurantee that $scope.profile is set, use resolve or watch the variable.

I actually gave an example of how to do this earlier today: AngularJS $rootScope.$broadcast not working in app.run

Upvotes: 1

Related Questions