AngularJS forEach循环内使用$compile编译HTML模板变量不更新问题
问题根因
- 异步时序错误:
$templateRequest是异步网络请求,你在init中直接同步调用generateHtml时,模板内容还未返回,vm.itemTemplate为 undefined,编译逻辑本身无法正常执行。 - 作用域变量复用冲突:所有编译操作共用同一个根
$scope,循环中反复覆盖vm.currentItem的值。AngularJS 的数据绑定是动态引用绑定,不会保存赋值瞬间的快照,循环结束后vm.currentItem固定为数组最后一项,所有已编译的模板都会同步更新为最后一项的数据。 - 代码基础缺失:原控制器中没有声明
var vm = this,直接使用vm会导致变量未定义报错。
修复方案
- 将
generateHtml调用移到$templateRequest的成功回调中,保证编译时模板已经加载完成 - 每次循环编译时创建独立的子作用域,每个子作用域存储当前循环的单项数据,避免变量互相覆盖
- 调整模板绑定逻辑,直接绑定子作用域上的单项变量,不再复用全局的
currentItem,同时修正模板中误用React语法的className为HTML原生的class属性
修正后代码
控制器代码
angular.module('app').controller('PageController', ['$scope', '$sce', '$compile','$templateRequest', function ($scope, $sce, $compile,$templateRequest) { $scope.itemsHtml = ''; // 补全vm声明 var vm = this; // 动态数据数组 vm.items = [{ id: 1, name: 'abc', }, { id: 2, name: 'pqr', }, { id: 3, name: 'stu', }, { id: 4, name: 'xyz', }]; let templateUrl = $sce.getTrustedResourceUrl('/views/item.html'); $templateRequest(templateUrl).then(function(template) { vm.itemTemplate = template; // 模板加载完成后再执行编译 vm.generateHtml(); }, function() {}); vm.generateHtml = function() { vm.items.forEach(function (item, key) { // 每次创建独立的子作用域,存储当前循环的item let childScope = $scope.$new(); childScope.item = item; // 用子作用域编译模板 let compiledElement = $compile(vm.itemTemplate)(childScope); // 等待脏检查执行完成后再取HTML childScope.$apply(); /* 拼接编译后的动态HTML */ $scope.itemsHtml += compiledElement.html(); // 销毁子作用域避免内存泄漏 childScope.$destroy(); }); } } ]);
模板代码
<script type="text/ng-template" id="item.html"> <div class="item-wrapper"> <div class="item-inner"> {{ item.name }} </div> <div class="action-inner"> <div class="btn-action" role="button" ng-click="pageCtrl.edit(item.id)"> <i class="fa fa-plus"></i> </div> </div> </div> </script>
注意:如果需要保留
ng-click等Angular事件绑定能力,不要拼接HTML字符串,直接将编译得到的compiledElement插入到页面DOM中即可,拼接HTML会丢失所有动态绑定的事件。
内容的提问来源于stack exchange,提问作者Shailesh Daund
相关产品推荐
相关产品推荐

