1

Angular Newbie Question:

I have a simple AngularJS test application that shows how a controller and a directive work together. The controller sets some hardcoded values aB and aC on the scope and the directive then displays these values in HTML. It works. Here is the JSFiddle. The code is below. When you run it, you can see the console output is as expected:

JS line #63: this.aB =  null
JS line #64: this.aC =  Goodbye
JS line #63: this.aB =  Hello
JS line #63: this.aC =  World

However, when I change the hardcoded values to ones that are retrieved from a test API, it fails. The console output is as follows:

JS line #63: this.aB =  null
JS line #64: this.aC =  Goodbye
JS line #63: this.aB =  undefined
JS line #63: this.aC =  undefined

The only change I made (seen here in this new JSFiddle) was in the controller's myFunc function: I replaced the hardcoded values with the following:

  response = $http.post('http://jsonplaceholder.typicode.com/posts',
    {
      'XXXXX': 'YYYYYYY'
    }
  )    
  self.scopeAB = response.id;
  self.scopeAC = response.id;

I have tested the API's response via curl and it is working fine. So why does the directive report the values of aB and aC as undefined? How do I solve this problem? I can tell that it has to do with the asynchronous nature of the HTTP call. But I don't know how to make this work correctly.

HTML:

<body ng-app="myApp">
  <div ng-controller="MyCtrl as ctrl">
    <div ng-view></div>
    <ul>
      <li>{{1+1}}</li>
      <li><my-directive a-b="null" a-c="'Goodbye'"></my-directive></li>
      <li><my-directive a-b="ctrl.scopeAB" a-c="ctrl.scopeAC"></my-directive></li>
      ab = {{ctrl.scopeAB}}, ac = {{ctrl.scopeAC}}
    </ul>
  </div>
</body>

Working Javascript:

myApp = angular.module('myApp',[]);
myApp.directive('myDirective',function(){
    return {
      restrict:'E',
      scope: {
        aB: '=',
        aC: '='
      },
      controller: 'DirectiveCtrl',
      controllerAs: 'dirCtrl',
      bindToController: true,
      template: 'aB={{dirCtrl.aB}} aC={{dirCtrl.aC}} <input ng-model="dirCtrl.aB" />'
    };
  }
);

myApp.controller('DirectiveCtrl', function(){
    var self = this;
    console.log('this.aB = ', self.aB);
    console.log('this.aC = ', self.aC);
})

myApp.controller('MyCtrl', function() {
    var self = this;
    self.myFunc = function() {
      self.scopeAB = 'Hello';
      self.scopeAC = 'World';
    }();
  }
);

UPDATE: Claies suggested I use this JSFiddle. But it won't work for me because I absolutely need the values of aB and aC to be accessible in the directive's controller. I need to vary the template based on their values. This JS Fiddle seems to show them as always undefined in there.

3
  • the problem here is another piece of angular that takes a bit of explaining. I've jumped back into the AngularJS chat channel chat.stackoverflow.com/rooms/63378/angularjs if you want to discuss what's going on and potential ways to deal with it. Commented Jan 16, 2016 at 6:42
  • The answer here does answer the question of why the controller values aren't updating with $http, but that isn't the whole picture. As to why the directive isn't updating when the controller does, that's a different topic, one in which I feel I may have partially led you astray when discussing this topic in earlier questions. Commented Jan 16, 2016 at 6:56
  • I have updated the question based on our most recent chat conversation. But the issue remains. Commented Jan 16, 2016 at 8:08

2 Answers 2

2

You should read the docs on the $http service. The call is asynchronous, and you handle a successful response in the then callback

$http.post('http://jsonplaceholder.typicode.com/posts', {'XXXXX': 'YYYYYYY'})
   .then(function(response) {
      self.scopeAB = response.data.id;
      self.scopeAC = response.data.id;
   })
Sign up to request clarification or add additional context in comments.

5 Comments

That produces the same console output: jsfiddle.net/saqibali75/8maswfqb/1
the answer is mostly correct, but when using .then, the response is slightly different. I updated the answer, response.data.id would be the correct usage. However, this still doesn't make 100% sense, as this code would actually make scopeAB and scopeAC equal to the same value, which is rather odd.
Check out what the console says: Mixed Content: The page at 'https://fiddle.jshell.net/saqibali75/8maswfqb/1/show/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://jsonplaceholder.typicode.com/posts'. This request has been blocked; the content must be served over HTTPS. Load the page without https.
Claies: Even after your edit, the console output remains the same: jsfiddle.net/saqibali75/wxx2xwg6/1 (Yes, I know the scopeAB and scopeAC are the same. this is just for testing purposes. In my real application they will be different)
Like I said, load the page without https and examine the output. jsfiddle.net/saqibali75/wxx2xwg6/1
1

Claies suggested I use this JSFiddle. But it won't work for me because I absolutely need the values of aB and aC to be accessible in the directive's controller. I need to vary the template based on their values. This JS Fiddle seems to show them as always undefined in there.

If you use @Claies methodology, you need to put a $watch on the object that fires when the $http request resolves.

myApp.controller('DirectiveCtrl', function($scope){
    var self = this;
    $scope.$watch(function() {return self.scopeObject}, function (objVal) {
        console.log("watch fired");
        console.log('objVal.aB = ', objVal.aB);
        console.log('objVal.aC = ', objVal.aC);    
    },true);

});

The DEMO on JSFiddle.

Frankly I think you are better off following the advice from @jumbopap. Use the httpPromise and the .then method and retrieve the data from the onFulfilled function.

3 Comments

@jumbopap was mentioned in this answer.
@claies was mentioned in this answer.
georgeawg I would like to use @jumbopap's answer but I don't fully understand it because he hasn't shown a JSFiddle of it fully working.

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.