Question

Is it possilbe to introspect/reflect of the model in Angular app, where you can change the scope and traverse it? Something like batarang have but that will allow to change the values.

If not is it possilbe to monkey patch Angular code (by including another script on the page) that will make it possilbe?

Was it helpful?

Solution

1. Accessing/Modyfing scope properties

Angular's $scope objects are plain old JS objects, so thay can be manipulated the standard way.
E.g. someScope.someProp retrieves the value of a property, while someScope.someProp = someValue sets the value of a property.


2. Letting Angular know

Modifying the object's properties is one thing - making Angular aware of the change is another.
Angular will not know about our modifications, until it runs a $digest cycle. If we want to apply the changes right away, we can explicitely trigger a $digest cycle, by using someScope.$apply().


3. Getting hold of a scope

In order to get a reference to the scope associated with a DOM element, we need to have a reference to the corresponding DOM Node object, wrap it in angular.element and execute its scope() method.
Something like this:

<body class="ng-scope">

var someScope = angular.element(document.body).scope();

Furthermore, if we want to access the $rootScope (the parent scope of all scopes), we can use the injector to inject the $rootScope service:

<html ng-app="myApp">

var injector = angular.element(document.documentElement).injector();
var rootScope = injector.get('$rootScope');

4. Traversing the scope-tree

Once we get hold of a scope object, we might want to traverse the scope-tree. Every scope has the following properties (among others):

  • $parent: The parent scope of this scope (if any).
  • $$nextSibling: The next sibling scope of this scope (if any).
    (Sibling scopes are scopes that have the same $parent)
  • $$childHead: The first child scope of this scope (if any).

In order to traverse the branch of the scope-tree with someScope as its root:

var scopes = [someScope];
while (scopes.length) {
    var scope = scopes.shift();
    console.log(scope);
    if (scope.$$nextSibling) { scopes.unshift(scope.$$nextSibling); }
    if (scope.$$childHead) { scopes.unshift(scope.$$childHead); }
}

E.g. to traverse the whole scope-tree, you can use the following snippet:

var injector = angular.element(document.body).injector();
var rootScope = injector.get('$rootScope');

var scopes = [rootScope];
while (scopes.length) {
    var scope = scopes.shift();
    report(scope);
    if (scope.$$nextSibling) { scopes.unshift(scope.$$nextSibling); }
    if (scope.$$childHead) { scopes.unshift(scope.$$childHead); }
}

function report(scope) {
    var str = '' + scope.$id;

    while (scope.$parent) {
        str = scope.$parent.$id + ' => ' + str;
        scope = scope.$parent;
    }

    console.log(str);
}

OTHER TIPS

Found the way, here is a code that change the scope outside of Angular:

var $scope = angular.element($('.section:eq(0)')).scope();
$scope.$apply(function() {
   scope.color = "blue";
});

the angular.element().scope() will return scope and can be investigated. Not sure about how to traverse scope tree, but this is good starting point. If there is no scope traverse you could just traverse the DOM and check if there is new scope on that DOM element.

You can get the scope by calling the scope() method on an object returned by angular.element() (or $(), if you also use jQuery), e.g. angular.element(".foo").scope(). There's no direct way of accessing child scopes. But knowing that all elements that have a new scope associated with them have the ng-scope class, you can traverse the element tree instead.

$(".foo").find(".ng-scope").each(function() {
  var scope = $(this).scope();
});

This, however can be a bit tricky, as find() will find all nodes below .foo, regardless of their depth. You could use children() instead but it may not yield any results as direct descendants of .foo may not create a new scope.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top