AngularJS,PhantomJS和ng-repeat问题

时间:2014-05-14 18:04:47

标签: angularjs phantomjs angularjs-ng-repeat

我正在使用PhantomJS生成我的Angular应用程序的快照,如here所述。

为此,我有一个PhantomJS脚本,它读取URL,将HTML随地吐痰到控制台,以及一个读取我的sitemap.xml的节点脚本,并为每个URL运行PhantomJS脚本。

在我的应用程序中,我还有一个标志,当应用程序认为所有内容都已渲染时,它会被触发。我对这个标志的设置方式非常保守,只是为了确保在PhantomJS抓取HTML之前所有内容都得到了渲染。

我遇到的问题是这个。应用程序的菜单是使用ng-repeat和无序列表创建的。对于这个指令 - 只有这个指令 - 会发生的事情是列表元素重复至少两次(实际上比这更多)。

假设菜单看起来像这样,有三个项目:

<ul>
   <li ng-repeat='item in items'>{{item.text}}</li>

我的快照脚本使用PhantomJS生成如下HTML:

<ul>
   <li ng-repeat='item in items'>{{item.text}}</li>        
   <li ng-repeat='item in items'>{{item.text}}</li>
   <li ng-repeat='item in items'>{{item.text}}</li>

如果我在浏览器中加载它,Angular会尽职尽责地执行ng-repeat指令 ,留下9个菜单项(每个3个)。

这显然不是我想要的,但我也不确定为什么会发生这种情况。而且我不知道问题是关于Angular中的某些内容还是PhantomJS中的内容。另外,为了让事情更令人沮丧,页面上还有其他ng-repeat指令执行得很好。

另一件事。菜单块在ng-if内。我不确定这是否重要,但也许确实如此。

那是怎么回事?以前有人遇到过这个问题吗?

编辑:

这里是设置标志的代码。由于我使用ui路由器,我等待$ viewContentLoaded事件。加载视图时会触发此事件。当事件被触发时,我设置或重置一个超时,它将改变$ rootScope上的标志。因为我有嵌套视图,所以每个页面加载会多次触发事件。想法是从上次发生此事件开始等待15秒,然后设置标志。

var timeout = null;
$scope.$on("$viewContentLoaded", function(){
    var interval = 15000; //15 seconds
   $rootScope.domStatus = 'notReady'; 
    function setDomStatus(){
        $rootScope.domStatus = 'ready';
    }

    if(timeout){
        $timeout.cancel(timeout);
    }  
    timeout = $timeout(setDomStatus, interval);

});

最后一个想法:

现在我开始怀疑这是否真的很重要。快照意味着由搜索引擎加载,搜索引擎不会执行JavaScript(事实上,这就是为什么快照是在一开始就制作的。)为了更好的衡量,我可以告诉Express寻找搜索在这种情况下引擎和NOT加载脚本。

2 个答案:

答案 0 :(得分:2)

我想知道同样的问题。但行为是正确的。原因如下:

PhantomJS呈现您的AngularJS网页。

你面临的'问题'是在输出html中你仍然有你的javascript代码和角度指令。如果您在浏览器中查看输出,则会再次执行整个AngularJS / javascript代码。

所以有这个简单的角度模块:

var app = angular.module('webApp', []);
        app.controller('myCtrl', function($scope){
            $scope.values = ['AAA', 'BBB', 'CCC'];
        });

以及这个html正文:

<body ng-app="webApp">
        <div ng-controller="myCtrl">
            <div ng-repeat="val in values">
            {{val}}
            </div>
        </div>
    </body>

PhantomJS提供的输出html如下所示:

<body ng-app="webApp" class="ng-scope">
        <div ng-controller="myCtrl" class="ng-scope">
            <!-- ngRepeat: val in values --><div ng-repeat="val in values" class="ng-binding ng-scope">
            AAA
            </div><!-- end ngRepeat: val in values --><div ng-repeat="val in values" class="ng-binding ng-scope">
            BBB
            </div><!-- end ngRepeat: val in values --><div ng-repeat="val in values" class="ng-binding ng-scope">
            CCC
            </div><!-- end ngRepeat: val in values -->
        </div>

</body>

现在此输出应用于禁用javascript的浏览器和搜索引擎机器人。由于他们不再执行javascript,他们得到了所需的输出

AAA
BBB
CCC  

如果您在启用javascript的浏览器中查看此html,则会再次执行所有'ng-repeat'指令,从而产生结果

AAA
AAA
AAA
BBB
BBB
BBB
CCC
CCC
CCC

要查看PhantomJS渲染html的方式,你必须关闭浏览器中的javascript功能。 (例如在Firefox中:在您的网址栏中输入'about:config'并将'javascript.enabled'更改为false)

答案 1 :(得分:0)

看起来PhantomJS正在捕捉页面,而角度正在忙着运行这个特定的ng-repeat(ng-if可能确实是原因)。如果在捕获页面之前添加延迟,则应该更好。如果确实如此,你要么保留延迟,要么修好你提到的旗帜。

有关如何设置标志的更多详细信息,我们可能会找到解决方案。