Angular JS——将ng-repeat应用于异步添加到DOM的东西

Posted

技术标签:

【中文标题】Angular JS——将ng-repeat应用于异步添加到DOM的东西【英文标题】:Angular JS -- Applying ng-repeat to things added to the DOM asyncronously 【发布时间】:2016-06-04 08:17:37 【问题描述】:

我正在开发一个“小部件”库,它创建一个带有可拖动、动态加载的小部件的仪表板。整体解决方案是使用 Angular。每个小部件都有一个 javascript 文件和一个 Jade 文件。对于我们的一些小部件,结果使用 ng-repeat 等角度指令。这是一个特定小部件生成的代码:

<tr ng-repeat="account in vmAccountList">
    <td ng-click="showTransactions(account)">account.account.accountNumber</td>
    <td ng-click="showTransactions(account)">account.account.accountDisplayName.defaultNormalAccountName</td>
    <td ng-click="showTransactions(account)" ng-if="account.account.acctTypeId==1">account.account.runningBalance.amount | currency</td>
    <td ng-click="showTransactions(account)" ng-if="account.account.acctTypeId==3">account.account.currentBalance.amount | currency</td>
    <td ng-click="showTransactions(account)" ng-if="account.account.acctTypeId==4">account.account.currentBalance.amount | currency</td>
    <td><a ng-click="deleteAccount(account.itemId);" class="btn btn-default btn xs">X</a></td>
</tr>

问题是 ng-repeat 永远不会被处理——生成的小部件只是呈现一个带有上面所有合并标签的单行,完全不知道数据。我尝试了许多不同的 $scope.$apply 和 $scope.$digest 组合——它们都不起作用。我收集到这个问题是因为当我的小部件被下载时,JADE 渲染和 JS 应用它可能在页面的生命周期中太“晚”了,无法使用 Angular 进程指令。

是否有一个命令可以手动告诉 Angular 去查找所有的 ng-repeats 并渲染它们?如果做不到这一点,有没有办法将东西插入到加载 aync 的 DOM 中并让 Angular 应用这些命令?

加载小部件和填充 DOM 的代码:

$.get('/v1/widget/' + name, (data) => 
    var jadeCode = data;
    $.get('/v1/widget/js/' + name, (data) => 
        if(!w)
            w = 2;
        if(!h)
            h = 1;
        if(that._lastX + w > SDB.NumberOfVerticalLanes) 
            that._lastX = 0;
            that._lastY += h;
        

        var retVal = new Widget(name, domId, that._lastX, that._lastY, w, h, '');   
        var processedWidget = eval(data + "(retVal)");
        processedWidget._jadeFN = jade.compile(jadeCode);

        var content = processedWidget._jadeFN(processedWidget.JadeScope);
        //The 'content variable contains the markup I posted above at this point, and SetContent applies it to the DOM
        processedWidget.SetContent(content);
    );
);

上面sn-p中注释高亮的SetContent方法,就是简单的用jquery插入新的内容:

this.SetContent = (cnt) => 
    this.Content = cnt;
    $("#" + this.Id).html(cnt);
;

如果我的一些术语(特别是关于 Angular)不正确,我深表歉意,我只是对它有点熟悉,坦率地说,我不是一个忠实的粉丝。但是该项目使用它,所以我必须找到一种方法来让它工作。谢谢!

【问题讨论】:

据我了解,当您有 ng-repeat 时,它会在您的案例“vmAccountList”中为数组值设置一个观察者,因此当您更改它时它应该更新。如果由于某些奇怪的原因它没有,您可以在获取数据后尝试 $apply。 我都试过了——看来 Angular 根本不知道我的 ng-repeat,即使我更新 vmAccountList 或调用 $apply 它根本不会更新我的组件。我们知道,如果代码没有异步加载,它就可以工作——但是一旦我们加载它并将其动态添加到 DOM 中,ng-repeat 就会停止工作。 尝试在 processesWidget.SetContent 行之后添加 $scope.$apply()。如果这不起作用,那么我猜您的 html 替换正在删除该过程中的角度链接。需要完整的页面代码才能深入挖掘。 【参考方案1】:

解决方案最终是使用 Angular 的 $compile 来创建一个数据感知 DOM 节点,如下所示:

//Step 1:  Create empty widget
var retVal = new Widget(name, domId, that._lastX, that._lastY, w, h, '');   
//Step 2:  Process Widget Javascript
var processedWidget = eval(data + "(retVal)");
//Step 3:  Process the Jade and set the content
processedWidget._jadeFN = jade.compile(jadeCode);
var content = processedWidget._jadeFN(processedWidget.JadeScope);
content = window.compile(content)(processedWidget.JadeScope);

//Angular Black Magic:
if(processedWidget.JadeScope.$apply)
    processedWidget.JadeScope.$apply();

SDB.AddWidget(processedWidget);
processedWidget.SetContent(content);

注意 $apply 调用和添加的 window.compile,它只是对注入 Angular CTOR 中的 $compile 函数的引用。

【讨论】:

以上是关于Angular JS——将ng-repeat应用于异步添加到DOM的东西的主要内容,如果未能解决你的问题,请参考以下文章

Angular js - 如果 ng-repeat 为空则显示消息

ng-repeat中的Angular js条件类

Angular.js - ng-repeat 不显示更新的数组

使用Angular JS时如何在ng-repeat内的img标签中显示图像?

angular.js ng-repeat 带有 html 内容的项目

如何在 angular.js 中解析 ng-repeat 中的 HTML [重复]