Search code examples
angularjsangularjs-directiveangularjs-scopebatarang

Inspecting AngularJS scopes using the Batarang Chrome extension


I have an question about AngularJs scopes and especially the way those can be inspected with the Batarang Chrome extension.

I have the following html:

<!doctype html>
<html lang="en" ng-app="myApp">
<head>
    <meta charset="utf-8">
    <title>My AngularJS App</title>
    <link rel="stylesheet" href="css/app.css"/>
</head>
<body>

<div ng-controller="myCtrl">

    <div enhanced-textarea ng-model="name"></div>
     <div cmp>
        <h3>{{name}}</h3>
        <div notice></div>
    </div>
</div>

<script src="lib/angular/angular.js"></script>
<script src="js/directives.js"></script>
<script src="js/controllers.js"></script>
<script src="js/app.js"></script>
</body>
</html>

Here are the directives:

'use strict';

angular.module('myApp.directives', [])
    .directive('cmp', function () {
        return {
            restrict: 'A',
            controller: 'cmpCtrl',
            replace: true,
            transclude: true,
            scope: {
                name: '='
            },
            template: '<div ng-transclude></div>'
        };
    })
    .controller('cmpCtrl', ['$scope', '$element', '$attrs' , function ($scope, $element, $attrs) {
        $scope.$parent.$watch('name', function (newVal) {
            if (newVal) {
                $scope.$parent.updatedSize = newVal.length;
            }
        }, true);
    }])
    .directive('enhancedTextarea', function () {
        return {
            restrict: 'A',
            replace: true,
            transclude: true,
            template: '<textarea ng-transclude></textarea>'
        };
    })
    .directive('notice', function () {
        return {
            restrict: 'A',
            require: '^cmp',
            replace: true,
            scope: {
                updatedSize: '='
            },
            template: '<div>{{size}}</div>',
            link: function ($scope, $element, $attrs, cmpCtrl) {
                $scope.$parent.$watch('updatedSize', function (newVal) {
                    if (newVal) {
                        $scope.size = newVal;
                    }
                }, true);
            }
        };
    });

and the controller:

'use strict';

angular.module('myApp.controllers', [])
  .controller('myCtrl', ['$scope', function($scope) {
        $scope.name = 'test';
  }]);

When I inspect the scopes using batarang, I come up with the following conclusion:

  • Scope 002: ng-app
  • Scope 003: ng-controller (myCtrl)
  • Scope 004: ????
  • Scope 005: cmpCtrl (controller for cmp directive)
  • Scope 006: inside cmp (h3 and notice)
  • Scope 007: link function of notice directive

    1. Is the above correct?
    2. Also, my biggest interrogation is what does the 004 scope correspond to?

Full app is located on github here

See also screen capture below:

screen capture


Solution

  • It's not that each $scope has to correspond to an element of your page. In fact in every AngularJS app there are a bunch of $scopes which aren't directly linked to any element.

    In your case it's the ng-transclude which causes a child scope to be created.

    Take a look at the implementation of AngularJS which causes the creation of your 004 $scope.

    if (!transcludedScope) {
        transcludedScope = scope.$new();
        transcludedScope.$$transcluded = true;
        scopeCreated = true;
    }
    

    https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L959

    If you like to dig deeper yourself, go and set a breakpoint right here in your AngularJS file:

    enter image description here

    Then just use the call stack and follow the rabbit...