angularjsangular-servicesangularjs-controlleras

AngularJS Ctrl As Syntax with Services


I'm migrating my Angular 1.x code to use the newer, more preferred syntax of avoiding using $scope and using the controller as syntax. I'm having an issue with getting data from a service though.

Here's my example:

var myApp = angular.module('myApp', []);

myApp.controller('MainCtrl', ['$http', '$location', 'userService',
  function($http, $location, userService) {

    this.name = 'John Doe';

    // this.userData = {

    // }

    this.userData = userService.async().then(function(d) {
      return d;
    });


    console.log(this.userData);


  }
]);

myApp.service('userService', function($http) {

  var userService = {
    async: function() {
      // $http returns a promise, which has a then function, which also returns a promise
      var promise = $http.get('https://jsonplaceholder.typicode.com/users').then(function(response) {
        // The then function here is an opportunity to modify the response
        // console.log(response);
        // The return value gets picked up by the then in the controller.
        return response.data;
      });
      // Return the promise to the controller
      return promise;
    }
  };
  return userService;

});
<body ng-app="myApp">
  <div id="ctrl-as-exmpl" ng-controller="MainCtrl as mainctrl">

    <h1>Phone Numbers for {{mainctrl.name}}</h1>

    <button ng-click="">newest</button>
    <p>{{mainctrl.userData}}</p>
    <ul>
      <li ng-repeat="users in mainctrl.userData">{{users.phone}} -- {{users.email}}</li>
    </ul>
  </div>
</body>

This issue I'm having is that the data returned from the userService.async is an object that I can't seem to drill down thru to get the data from, as the data is a child of $$state, which can't seem to be used in the view.

If this isn't the proper way to use services in the Controller As/$scope-less syntax, what is the proper way?

Live example here: http://plnkr.co/edit/ejHReaAIvVzlSExL5Elw?p=preview


Solution

  • You have a missed an important part of promises - when you return a value from a promise, you return a new promise that will resolve to this value.

    The current way to set the data returned from a promise to a local variable in your controller is this:

    myApp.controller('MainCtrl', ['$http', '$location', 'userService',
      function($http, $location, userService) {
    
        this.name = 'John Doe';
    
        userService.async().then(function(d) {
          this.userData = d;
        });
      }
    ]);
    

    But now you are in a catch because of the scope of this, so it is common to use a "placeholder" variable for the controller this scope.

    myApp.controller('MainCtrl', ['$http', '$location', 'userService',
      function($http, $location, userService) {
    
        var $ctrl = this; // Use $ctrl instead of this when you want to address the controller instance
    
        $ctrl.name = 'John Doe';
    
        userService.async().then(function(d) {
          $ctrl.userData = d;
        });
      }
    ]);