1

AngularJS keeps calling the http get request over and over again!!

If I call getContexts directly in the controller it's fine... but if I call it from the html template, it just keeps calling the get.

Partial contextCtrl.js:

            'use strict';

            app.controller('ContextCtrl', function ContextCtrl($scope, $location, $http, $rootScope, ContextService) {  
                $scope.getContexts = function(keys)
                {
                    $scope.contexts = {};
                    $http.get('/getContextsWS?keys=' + keys).
                    success(function(data){
                      $scope.contexts = data;
                    });
                }

                // This works
                $scope.getContexts('["context::one", "context::two"]')
            });

Partial html section:

            <section id="contextSection" ng-controller="ContextCtrl">

                // This causes get to keep calling!
                {{getContexts('["context::one", "context::two"]')}}

                // Just checking
                {{contexts|json}}

                <nav>
                    <ul ng-show="contexts.length" ng-cloak>
                        <li ng-repeat="context in contexts">
                            <div class="view" ng-click="contextSelected(context)">{{context.name}}</div>
                        </li>
                    </ul>
                </nav>
            </section>

This is driving me nuts!

Okay I've tried your hidden field example and I can't seem to get it to work, but perhaps I'm missing something...

The html:

            <section id="contextSection" ng-controller="ContextCtrl">
            <input type="hidden" ng-model="contextIds" copy-to-model value='@product.item.contextIds'/>

The directive:

            mto.directive('copyToModel', function ($parse) {
                return function (scope, element, attrs) {
                    $parse(attrs.ngModel).assign(scope, attrs.value);
                }
            });

The controller:

            app.controller('ContextCtrl', function ContextCtrl($scope, $location, $http, $rootScope, ContextService) {  

                // I can read this, but it never changes, and without this the contextIds in the alert are null
                $scope.contextIds = "...";

                $rootScope.alert = function(text)
                {
                    alert(text);
                }

                $rootScope.alert("$scope.contextIds: " + $scope.contextIds);

            });

Any idea what am I doing wrong?

Mohan Gopi
  • 7,606
  • 17
  • 66
  • 117
derekdon
  • 136
  • 9

1 Answers1

4

The expressions inside your {{ }} binding code will be re-evaluated anytime Angular notices that something in the model has changed (I've oversimplified what actually occurs). If you want to call getContexts just once don't put it inside {{ }} in your markup and just call it in your controller code.

Basically assume that code inside {{ }} can and will get called multiple times..

So try removing:

// This causes get to keep calling!
{{getContexts('["context::one", "context::two"]')}}

And rely on your controller code (though you might not want getContexts in your scope if you don't call it from your markup):

// This works
$scope.getContexts('["context::one", "context::two"]')
Gloopy
  • 37,767
  • 15
  • 103
  • 71
  • Hi Gloopy, thanks for the tip! Only starting with AngularJS so still learning... I'm using it with Play 2 Framework so I have something like @(product: models.product.Product) at the start of my template which contains @product.contextIds which I want to pass it to that controller method... So the real code within the template is along the lines of {{getContexts('["@product.item.contextIds.mkString("\", \"")"]')}} – derekdon Oct 17 '12 at 22:47
  • Not sure if this helps (and it's definitely hacky) but if you have data generated on the server side you might be able to copy it to the Angular scope/model (via a directive) and then have your controller retrieve data based on those model values. See this answer for info on this type of directive: http://stackoverflow.com/a/11839256/1207991. If you could pass the inputs via the url and extract it using $location, $route, or $routeParams that would probably be best. – Gloopy Oct 17 '12 at 23:07
  • Thanks for the link... Yeah that seems a bit hacky but perhaps a fallback option. Surely other people have needed to do what I am trying to do, send in some server side values into a controller method? Can you point me to a link re {{ }} being re-evaluated continually? Interested to read more on this... – derekdon Oct 18 '12 at 09:10
  • They aren't re-evaulated continually but during the $digest phase of the scope life cycle. Read more about the data binding dirty checking [here](http://stackoverflow.com/questions/9682092/databinding-in-angularjs). Also see [Scope](http://docs.angularjs.org/guide/scope) and [$digest](http://docs.angularjs.org/api/ng.$rootScope.Scope#$digest). In your case I assume the success callback on $http.get setting $scope.contexts kicks off another $digest which processes the watchers (one of which would be your {{getContexts(...)}}) to see if anything has changed that needs to be updated in the view. – Gloopy Oct 18 '12 at 13:08
  • That makes sense now, thanks a million for your help... That was really starting to annoy me. – derekdon Oct 18 '12 at 16:09
  • I've just updated the post to show an attempt to use the hidden field approach you mentioned, but I can't seem to get it to work... – derekdon Oct 18 '12 at 19:01
  • Next time you might consider creating a new question for the followup but [here is a fiddle](http://jsfiddle.net/kntHE/) with the copyToModel working. The model isn't updated until after your alert is being executed I got around this by using `$scope.$watch` against the contextIds field before displaying it in an alert. I stress that going this route is very hacky and likely frowned upon :) You might want to read the other answers and notes in [this post](http://stackoverflow.com/questions/11838639/html-template-filled-in-server-side-and-updated-client-side) for other workarounds. – Gloopy Oct 18 '12 at 22:57
  • Fair point @Gloopy, I will keep my questions separate in future. Got that working, thanks for all your help! – derekdon Oct 19 '12 at 11:28