2

I have written a custom angular directive as shown below:

dirs.directive('sectionInfo', function(){
    return {
        restrict: 'E',
        templateUrl: 'partials/section-home.html',
        transclude: true,
        controller: function($scope, $routeParams){
            var section = this;
            section.sectionItem = [];

            client.entries({"some params"}, 
                function(err, entries){
                    if (err) { console.log(err); return; }
                    $scope.$apply(function(){
                        section.sectionItem = entries;
                });
                }
            );
        },
        controllerAs: 'sectionCtrl'
    }
});

This is then displayed in a separate partial page which looks like:

<section-info></section-info>
<ul class="list-group">
  <li class="list-group-item" ng-repeat="entry in entriesCtrl.index_items">
    <a href="#/entries/{{entry.sys.id}}">
        <h4>{{entry.fields.title}} <small>{{entry.fields.author}}</small></h4>
    </a>
  </li>
</ul>

With the partial template code being simply:

{{sectionCtrl.sectionItem}}

When I load this page and keep the $scope$apply call in then I get an error:

Error: error:interr Interpolation Error Can't interpolate: {{sectionCtrl.sectionItem}} TypeError: Converting circular structure to JSON

When I remove the $scope.$apply call it disappears. Any idea what is causing the circular reference within the $scope.$apply call?

EDIT: Console log of content of entries and the error message.

console log

3
  • 1
    Can you show the value of entries? Most likely that's causing your issue, all $apply does in this case is tell the template it has to show the new value ( entries ) Commented Sep 24, 2014 at 14:49
  • 1
    Done - called console.log(entries) straight before $scope.$apply :-) Commented Sep 24, 2014 at 14:59
  • 1
    What i meant is, show the actual contents of it - the issue is that entries can't be represented as JSON, because some part of it refers to itself, making it circular. Commented Sep 24, 2014 at 17:27

2 Answers 2

4

What's happening is you have a circular reference, meaning an object is referring to itself, through one of its properties or elements. ( In your picture, it looks like the sys field is referencing to its parent object ). To illustrate here's a sample with an object that references itself through a field, you can't convert this to regular JSON. This'll alert the error.

angular.module("so", []);

console.error = function(msg){
  console.log("Well, an error occurred, here's what it said:" + msg);
}

angular.module("so").controller("AnswerCtrl", function($scope){

  // make a thing, and put the thing in itself
  $scope.thing = { somefield: 'lalalala' };
  $scope.thing.circle= $scope.thing;

});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="so" ng-controller="AnswerCtrl">
  {{ thing }}
</div>

So the problem isn't very much related to Angular, but more that angular is using JSON.stringify or something along those lines to convert the object to a JSON string so that it can display it in the view.

What you can do is, first off - do not try to display the actual object with the circular reference. You can display parts of it just fine, but don't try to serialize a cyclic object to JSON.

If you want to display all properties though, you can use something like this answer, demoed here:

angular.module("so", []);

console.error = function(msg){
  console.log("Well, an error occurred, here's what it said:" + msg);
}

angular.module("so").controller("AnswerCtrl", function($scope){

  // make a thing, and put the thing in itself
  $scope.thing = { somefield: 'lalalala' };
  $scope.thing.circle= $scope.thing;

  var cache = [];
  $scope.viewThing = JSON.stringify($scope.thing, function(key, value) {
    if (typeof value === 'object' && value !== null) {
      if (cache.indexOf(value) !== -1) {
        return;
      }
      cache.push(value);
    }
    return value;
  });

});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="so" ng-controller="AnswerCtrl">
  {{ viewThing }}
</div>

If you have to display it, use this libary for it.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the fix - ironically I was only outputting the entire object for debuggging, asking it to view specific fields has made this work perfectly.
1

Try something like this:

var safeApply = function (scope, callback) {
    if (scope.$$phase != '$apply' && scope.$$phase != '$digest' &&
        (!scope.$root || (scope.$root.$$phase != '$apply' && scope.$root.$$phase != '$digest')))    {
        scope.$apply();
    }
    if (angular.isFunction(callback)) {
        callback();
    }
};

and you can call this

safeApply($scope, function(){
   section.sectionItem = entries;
 });

2 Comments

to prevent $apply nesting is enough to wrap a function inside $timeout(); manually check for scope.$$phase is considered as bad practice.
This removes the error but the screen is not updated with the correct value, instead only displaying []

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.