Angular.js: angular.element.scope() returns undefined

Created on 9 Oct 2014  ·  29Comments  ·  Source: angular/angular.js

angular.element.scope() is undefined when the application is loaded in iframe by changing the iframe src attribute.

The following code should return reference to scope but returns undefined:

angular.element(document.body).scope()

The code above is executed in the onclick handler after the document is loaded and angular has finished bootstrap:

<script>
    document.addEventListener("click", function(){
        console.log(angular.element(document.body).scope());
    });
</script>

The issue manifests itself only when the document is loaded in the iframe by modifying its src attribute.
Tested with 1.3.0-rc.5, without jquery.

jqLite more info

Most helpful comment

If you want to enable debug information temporarily (e.g. for debugging an issue on a live production app) you can use angular.reloadWithDebugInfo() as described here.

All 29 comments

Can you provide a plnkr reproduction of the issue please?

I have the same issue in my own project, but can't reproduce it using plnkr.

scope() returns undefined on any element.

Using JQLite or JQuery 2.1.1 does the same.

AngularJS 1.3.1.

Try this:

document.addEventListener('DOMContentLoaded', function () {
  angular.element(document.body).scope();
});

are you disabling debug info in your project @VictorQueiroz?

@caitp You've found it.

Enabling debug info back is a good workaround for this issue.

a good workaround

:smile: :smile: :smile:

Sorry guys for not reacting for a while. The issue is somehow related to jqCache, or more specifically to the way how expandoId is created. In older RCs, the id was created like this:
var expandoId = element[JQLite.expando];
In newer ones, the id is get using hardcoded property:
var expandoId = element.ng339;
Reverting the change solved the issue.
Unfortunately, I do not know much about the stuff, so if somebody could shed light on the functionality, then perhaps I would be able to describe the issue in more detail.

Oh wow thanks @caitp that was driving me mad !
There should be a note in the doc on the $compileProvider explaining that $compileProvider.debugInfoEnabled(false) will make the scope() function return undefined, if that is the expected behavior.

Edit: my bad, it's already in the doc

Call this method to enable/disable various debug runtime information in the compiler such as adding binding information and a reference to the current scope on to DOM elements.

Is there a way around this; having debugInfoEnabled(false), but still being able to access the scope of an element?

If you want to enable debug information temporarily (e.g. for debugging an issue on a live production app) you can use angular.reloadWithDebugInfo() as described here.

I think that @gkalpak clarified things here!

Why closing ? Finding a workaround doen't means the issue is fixed, scope() should return the attached scope regardless the debug mode.

If not possible, it should be be cleary documented in angular.element#scope() doc.

@Toilal I've closed it since it is documented already as mentioned in the previous comments of this thread. But if you believe that it should documented in other places as well a PR would be much appreciated!

I can't because i'm not sure witch methods from angular.element won't work when disabling debug. scope() for sure because i've tried it, but how about the others, like controller(), injector() ...

After testing, it only impacts scope() and isolateScope(). I'll document those and make a PR.

But is there any way to retrieve the scope from a DOM element while having debug info disabled ?

@Toilal - not really - scope info was removed as exposing it has perf impact - this is why it is hidden behind a debug flag.

But it wouldn't be that hard to write a directive that could do that and put it on the elements that you need to check

Thanks @pkozlowski-opensource. I've added a small sentence on scope() and isolateScope() in docs, because it takes me some times to figure out why those methods didn't work in my application.

@ocombe That's what i'll do, because i'm writing a UI component and can't rely on scope/isolateScope as end developer could disable debug info (as it's recommended).

setTimeout(function() {
console.log(angular.element(document.body).scope());
}, 100);

qq 20160115164505

@ronnievdv

Is there a way around this; having debugInfoEnabled(false), but still being able to access the scope of an element?

I've used the following snipet _(not recommended)_ method in the link function of the directive definition.

link: function(scope, element, attrs) {
  var isolatedScope = scope.$$childTail;
}

Is there any way to check if debugInfo has been disabled or any flag for it?

From within the app, you can use $compileProvider.debugInfoEnabled().
If you want to debug a live instance that has debug info disabled (e.g. a production deployment), you can call angular.reloadWithDebugInfo() from the console and it will reload the app with debug info enabled.

@gkalpak I just want to check if debug info is enabled or not. Somewhat like this.

if(angular.isDebugInfoEnabled()) {
  // Do something
}

@hemkaran , it is still not obvious _where_ you want to check it from. I assume from inside your app.
If that is indeed the case, you can use $compileProvider.debugInfoEnabled().

@Toilal Did you write a directive like that? If so, would you mind sharing? I'm having this problem right now. I assume I'll find a solution, but having a second opinion on how to solve it really wouldn't hurt.
cc @ocombe

Sorry I don't use Angular 1 anymore, and I never had to write such a directive (because I only needed this info in dev mode)

I did the following. We'll see if my teammates will let it through the review :)

function exposeScope() {
    return {
        restrict: 'A',
        link: function(scope, element) {
            element[0].APPNAME = {
                getScope: function() {
                    return scope;
                }
            };
        }
    };
}

return {
        restrict: 'A',
        scope: false,
        link: function(scope, elem) {
            elem.data('$scope', scope);
        }
    };

You can also set $isolateScope to the same if you need it.

Was this page helpful?
0 / 5 - 0 ratings