32

I understand I can get the scope by element:

scope = angular.element($0).scope();
scope.$id; // "003"

How do I get reverse: Find the DOM element using the scope $id, such as 003?

I'd like to do this for debugging purposes. My scope tree shows something and I'd like to identify where it came from.

SimplGy
  • 20,079
  • 15
  • 107
  • 144

5 Answers5

33

Although it's not very sexy each dom node gets a class ng-scope so you could tech do something like this maybe:

function getScope(id) {
var elem;
$('.ng-scope').each(function(){
    var s = angular.element(this).scope(),
        sid = s.$id;

    if(sid == id) {
        elem = this;
        return false; // stop looking at the rest
    }
});
return elem;
}
drzaus
  • 24,171
  • 16
  • 142
  • 201
btm1
  • 3,866
  • 2
  • 23
  • 26
  • 1
    Does the trick. Guess I thought there'd be a list of scopes somewhere ripe for the fetching :) – SimplGy Apr 25 '14 at 00:25
  • 2
    I'm with ya. I wish there was too as I've encountered a need for this very thing. – btm1 Apr 25 '14 at 03:04
  • If it's a real use case, don't forget you can also crawl up from $rootScope. I've used DOM vs rootScope traversal to count and always get different numbers. http://stackoverflow.com/a/21776522/111243 – SimplGy Apr 25 '14 at 05:56
  • seems like directives don't necessarily have `.ng-scope` -- http://stackoverflow.com/a/26413528/1037948 – drzaus Oct 16 '14 at 20:41
  • I think most people want this for when they aren't using a directive...you wouldn't need this unless you are doing something pretty advanced. – btm1 Dec 18 '14 at 16:35
  • @user2173353 What do you mean the `$element` on the controller? Then pass it into the directive? – mlhDev Dec 13 '16 at 22:25
  • This is only true in development mode. You should not use this as it does not work in release mode. – MGDavies Jun 30 '17 at 09:01
7

Trying out the answer, I found that directives don't seem to have the class ng-scope, so here's a modified version that'll fallback to everything.

var getByScopeId = function(id) { 
    var filterfn = function(i,el) {
        var sc = angular.element(el).scope();

        return sc && sc.$id == id;
    };
    // low hanging fruit -- actual scope containers
    var result = $('.ng-scope').filter(filterfn);
    if(result && result.length) return result;

    // try again on everything...ugh
    return $(':not(.ng-scope)').filter(filterfn);
}

Usage:

var results = getByScopeId('003')
Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201
  • note: should use something more optimized than the `.filter` fn, i.e. stop after success http://jsfiddle.net/drzaus/3vLwzgkh/3/ – drzaus Nov 18 '14 at 15:40
  • 3
    Note: scope() only works when debug data is enabled. This is usually turned off in production for performance – Charlie Brown Dec 04 '14 at 20:58
  • It did technically work for me but I don't understand the results: n.fn.init context: document length: 0 prevObject: n.fn.init(2304) [...] [[Prototype]]: Object(0) – mjaggard Oct 20 '21 at 08:11
  • Ah, I see. Despite being present in the page when I output {{$id}} the function didn't find the ID I was looking for (hence `length:0`) – mjaggard Oct 20 '21 at 08:15
5

The same solution written in es2015, without JQuery dependency:

getElementFromScopeId = (id) => [].slice.call(document.querySelectorAll('.ng-scope')).map((x) => angular.element(x)).filter(x => x.scope().$id == id).pop();
drphrozen
  • 317
  • 3
  • 4
0

Here's my version that detects and handles isolate scopes correctly and stops as soon as it finds the element. Angular's .scope() and .isolateScope() functions use the jquery .data() object and if a scope is not found, they search up through the parent nodes of the element. We're already searching through all nodes, so it's a lot more efficient to skip the parent search and inspect .data() directly.

const findScopeElement = $scope => {
    const {$id, $root} = $scope;
    let result;
    const isolate = $scope !== $root &&
        Object.getPrototypeOf($scope) === Object.getPrototypeOf($root);
    const cls = isolate ? '.ng-isolate-scope' : '.ng-scope';
    const search = (i, e) => {
        const data = $(e).data();
        const scope = isolate ? data.$isolateScope : data.$scope;
        if(scope && scope.$id === $id) {
            result = e;
            return false;
        }
    };
    $(cls).each(search);
    if(!result) {
        // could be an attribute directive
        $(`:not(${cls})`).each(search);
    }
    return result;
}
JohnLock
  • 381
  • 3
  • 5
-1

You can always get the scope using element's id.

Example:

html:

<div id="myId"></div>

js:

  var myEl = angular.element(document.querySelector('#myId'));
  var myScope = angular.element(myEl).scope(); 

Live example : http://jsfiddle.net/choroshin/7XQA7/2/

also as David Chase suggested, you can always use batarang - Extends Chrome Developer Tools, adding tools for debugging and profiling AngularJS applications.

Alex Choroshin
  • 6,177
  • 2
  • 28
  • 36