AngularJS模型内省/反射

3

能否在Angular应用程序中内省/反射模型,以便更改作用域并遍历它?类似于Batarang的东西,但允许更改值。

如果不行,是否可以通过包含另一个脚本来猴子补丁Angular代码,从而使其成为可能?


有一个项目 https://github.com/rev087/ng-inspector 可以展示应用程序的作用域,并且它有一个扩展/折叠和编辑作用域的计划。 - jcubic
3个回答

3

1. 访问/修改作用域属性

Angular中的$scope对象是普通的JS对象,因此可以按照标准方式进行操作。
例如:someScope.someProp检索属性的值,而someScope.someProp = someValue设置属性的值。


2. 通知Angular

修改对象的属性是一回事,让Angular意识到这个变化是另一回事。
只有在运行$digest周期后,Angular才会知道我们所做的修改。如果我们想立即应用这些更改,我们可以显式地触发$digest周期,使用someScope.$apply()方法。


3. 获取作用域

为了获取与DOM元素相关联的作用域引用,我们需要先获取相应的DOM节点对象的引用,然后将其包装在angular.element中,并执行其scope()方法。
代码看起来像这样:

<body class="ng-scope">

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

此外,如果我们想要访问$rootScope(所有作用域的父级作用域),我们可以使用注入器来注入$rootScope服务:
<html ng-app="myApp">

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

4. 遍历作用域树

一旦我们获得了一个作用域对象,我们可能想要遍历作用域树。每个作用域都有以下属性(以及其他属性):

  • $parent:这个作用域的父级作用域(如果有)。
  • $$nextSibling:这个作用域的下一个兄弟作用域(如果有)。
    (兄弟作用域是具有相同的$parent的作用域)
  • $$childHead:这个作用域的第一个子作用域(如果有)。

为了遍历以someScope为根的作用域树分支:

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); }
}

例如,要遍历整个作用域树,您可以使用以下代码片段:
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);
}

谢谢,现在我可以为 Angular 创建模型内省操作工具。 - jcubic
@jcubic:你可能已经知道了,但Batarang是开源的,所以无论如何都是一个很好的起点。 - gkalpak
它不允许更改范围,所以我认为这不是我想要的,而且需要刷新浏览器才能看到范围,因此我认为它修改了Angular应用程序或Angular本身才能看到模型。也许我只需fork Batarang并添加功能,以允许修改范围。 - jcubic
是的,那就是我想表达的意思。我知道它并不完全符合你的要求,但它似乎解决了部分问题,因此其中的一些部分应该作为起点很有用(以免重复造轮子)。 - gkalpak
1
我写了一个名为AngScope的小型Firebug插件,它可以帮助您检查$scope对象。它会在Firebug的DOM检查器中显示它们。从那里,您可以选择对象以在控制台中使用它,编辑属性并调用$apply() - Kos Prov

1

找到了方法,这里有一段可以改变Angular外部作用域的代码:

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

angular.element().scope()会返回作用域,并可进行调查。不确定如何遍历作用域树,但这是一个很好的起点。如果没有作用域遍历,您可以遍历DOM并检查该DOM元素上是否有新的作用域。


你可以使用jQuery $('.section:eq(0)').scope();,而不是像Michał Dudak所写的angular元素。 - jcubic

1
你可以通过调用由angular.element()(或$(),如果你还使用jQuery)返回的对象上的scope()方法来获取作用域,例如:angular.element(".foo").scope()。没有直接访问子作用域的方法。但是,知道所有具有新作用域的元素都具有ng-scope类,你可以遍历元素树。
$(".foo").find(".ng-scope").each(function() {
  var scope = $(this).scope();
});

然而,这可能有点棘手,因为find()会查找.foo下面的所有节点,无论它们的深度如何。你可以使用children()代替,但它可能不会产生任何结果,因为.foo的直接后代可能不会创建新的作用域。

很高兴知道你可以使用jQuery来获取元素,而不是使用angular.element并将作用域用作插件。但是你可以遍历作用域 - 参考@ExpertSystem的答案。 - jcubic
1
@jcubic:关于你对jQuery的评论:.scope()是Angular特定的方法。如果同时引入jQuery和Angular(jQuery脚本必须在AngularJS之前),那么angular.element()就是$()的别名,因此你可以通过前者做的任何事情也可以用后者来完成。如果没有加载jQuery(或加载在AngularJS之后),那么angular.element()将“回退”到使用集成的jQuery(jqLite)的“轻量”版本。 - gkalpak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接