topic
topic

Reputation: 372

Updating Angular scope variables from $http.get call

I'm fairly new to Angular, and I'm trying to figure out why scope variables isn't updating after they've been set.

I'm calling a Node API returing json objects containing my data. Everything seems to work fine except setting $scope.profile to the data returned from the API.

Setup:

app.js

(function() {
  var app = angular.module("gamedin", []);


  app.controller('profileController', function($scope, $http, $timeout) {
    $scope.profile = {};

    $scope.getProfile = function() {
      var vanityUrl = $scope.text.substr($scope.text.lastIndexOf('/') + 1);

      $http.get('/steamid/' + vanityUrl)
      .then(function(data) {
        $http.get('/profile/' + data.data.response.steamid)
        .then(function(data) {
          console.log(data.data.response.players[0]); // Correct data
          $scope.profile = data.data.response.players[0]; // View isn't updated
        })
      })

      // Reset the input text
      $scope.text = "";
    }
  });

  ...

  app.directive('giHeader', function() {
    return {
      restrict: 'E',
      templateUrl: 'components/header/template.html'
    };
  })

  app.directive('giProfile', function() {
    return {
      restrict: 'E',
      templateUrl: 'components/profile/template.html'
    }
  })

})();

components/header/template.html

<header>
  <div class="header-content" ng-controller="profileController">
    <div class="col-md-3"></div>

    <div class="col-md-6">
      <div class="header-content-inner">
        <input ng-model="text" ng-keyup="$event.keyCode == 13 && getProfile()" class="form-control" type="text" placeholder="Enter Steam URL">
      </div>
      <p>e.g., http://steamcommunity.com/id/verydankprofilelink</p>
    </div>

    <div class="col-md-3"></div>
  </div>
</header>

components/profile/template.html

<div class="container">
  <div ng-controller="profileController">
    <h3> 
      <strong>Username: {{ profile.personaname }}</strong>
    </h3>
    <p> SteamID: {{ profile.steamid }}</p>
  </div>
</div>

index.html

<!doctype html>
<html ng-app="gamedin">
<head>
  ...
</head>

<body>

  ...

  <gi-header></gi-header>

  <gi-profile></gi-profile>

  ...

</body>
</html>

I've tried wrapping it in $scope.$apply, like this

$scope.$apply(function () {
  $scope.profile = data.data.response.players[0];
});

... which resulted in Error: [$rootScope:inprog]

Then I tried

$timeout(function () {
  $scope.profile = data.data.response.players[0];
}, 0);

and

$scope.$evalAsync(function() { 
  $scope.profile = data.data.response.players[0]; 
});

... and although no errors were thrown, the view still wasn't updated.

I realize that I'm probably not understanding some aspects of angular correctly, so please enlighten me!

Upvotes: 0

Views: 228

Answers (2)

Omri Aharon
Omri Aharon

Reputation: 17064

The problem is that you have 2 instances of profileController, one in each directive template. They should both share the same instance, because what happens now is that one instance updates profile variable on its scope, and the other is not aware. I.e., the profileController instance of header template is executing the call, and you expect to see the change on the profile template.

You need a restructure. I suggest use the controller in the page that uses the directive, and share the profile object in both directives:

  <gi-header profile="profile"></gi-header>

  <gi-profile profile="profile"></gi-profile>

And in each directive:

return {
  restrict: 'E',
  scope: {
     profile: '='
  },
  templateUrl: 'components/header/template.html'
};

And on a more general note - if you want to use a controller in a directive, you should probably use the directive's "controller" property.

Upvotes: 1

Chrillewoodz
Chrillewoodz

Reputation: 28328

Try using this method instead:

$http.get('/steamid/' + vanityUrl)
  .then(function(data) {
    return $http.get('/profile/' + data.data.response.steamid).then(function(data) { 
      return data;
    });
  })
  .then(function(data) {
     $scope.profile = data.data.response.players[0]; // View isn't updated
  })

Where you use two resolves instead of one and then update the scope from the second resolve.

Upvotes: 0

Related Questions