AngularJS数组添加元素后视图未更新问题求助
问题分析与解决方案
我明白你遇到的困境了——滚动到页面顶部加载更多消息时,虽然vm.messages数组已经成功更新(控制台能看到),但页面就是不渲染新添加的消息;而点击Reply按钮动态添加单条消息却能正常显示。这核心原因是滚动事件不在AngularJS的消化周期(Digest Cycle)内触发,导致Angular无法感知到数据的变化,自然不会更新视图。
为什么会这样?
$document.on('scroll')是绑定的原生JavaScript事件,它的回调函数运行在Angular的上下文之外。当你在这个回调里修改vm.messages时,Angular没有收到数据变更的通知,不会触发视图更新。- 而
vm.reply()是通过ng-click触发的,ng-click指令会自动触发Angular的消化周期,所以数据变化后视图能立刻同步。
解决办法
我们需要手动让Angular知道数据已经更新,有两种简单的实现方式:
方式1:注入$scope并调用$apply()
在控制器中注入$scope,然后在loadMoreMessages函数的末尾手动触发消化周期:
修改后的JavaScript代码:
angular.module('app',['ui.bootstrap']) .controller('mainCtrl', function($location, $anchorScroll, $document, $window, $scope) { // 注入$scope var vm = this; vm.messagesInPart = 20; //number of messages in one part vm.messagesLoaded = 0; vm.conversation = []; vm.messages = []; // 绑定滚动事件,记得在控制器销毁时解绑 const scrollHandler = function() { if($window.scrollY === 0) { console.log('top of the page'); loadMoreMessages(); } }; $document.on('scroll', scrollHandler); // 控制器销毁时解绑事件,避免内存泄漏 $scope.$on('$destroy', function() { $document.off('scroll', scrollHandler); }); //fake conversation function getConversation() { for(var i = 1; i < 30; i++) { vm.conversation.push({ side: 'left', badgeClass: 'info', badgeIconClass: 'glyphicon-comment', content: "My own message " + i, userName: '' }); } scrollToTheReply(); } getConversation(); function loadMoreMessages() { vm.messagesLeftToLoad = vm.conversation.length - vm.messagesLoaded; if(vm.messagesLeftToLoad > 0) { var iterationsUpTo = vm.messagesLeftToLoad >= vm.messagesInPart ? vm.messagesLeftToLoad - vm.messagesInPart : 0; console.log('iterationsUpTo = ', iterationsUpTo); for(var i = vm.messagesLeftToLoad - 1; i >= iterationsUpTo; i--) { vm.messages.unshift({ side: vm.conversation[i].side, badgeClass: vm.conversation[i].badgeClass, badgeIconClass: vm.conversation[i].badgeIconClass, userName: vm.conversation[i].userName, content: vm.conversation[i].content }); vm.messagesLoaded++; } console.log('vm.messages = ', vm.messages); // 手动触发消化周期,通知Angular更新视图 $scope.$apply(); } } loadMoreMessages(); function scrollToTheReply() { $location.hash('reply'); $anchorScroll(); } vm.reply = function() { vm.messages.push({ side: 'left', badgeClass: 'info', badgeIconClass: 'glyphicon-comment', userName: '', content: 'Reply msg' }); } })
方式2:注入$timeout间接触发消化周期
$timeout服务会自动触发消化周期,我们可以把更新vm.messages的逻辑放在$timeout回调里:
修改后的loadMoreMessages函数(记得在控制器依赖中添加$timeout):
function loadMoreMessages() { vm.messagesLeftToLoad = vm.conversation.length - vm.messagesLoaded; if(vm.messagesLeftToLoad > 0) { var iterationsUpTo = vm.messagesLeftToLoad >= vm.messagesInPart ? vm.messagesLeftToLoad - vm.messagesInPart : 0; console.log('iterationsUpTo = ', iterationsUpTo); // 用$timeout包裹数据更新逻辑 $timeout(function() { for(var i = vm.messagesLeftToLoad - 1; i >= iterationsUpTo; i--) { vm.messages.unshift({ side: vm.conversation[i].side, badgeClass: vm.conversation[i].badgeClass, badgeIconClass: vm.conversation[i].badgeIconClass, userName: vm.conversation[i].userName, content: vm.conversation[i].content }); vm.messagesLoaded++; } console.log('vm.messages = ', vm.messages); }, 0); } }
额外优化点
我还添加了滚动事件的解绑逻辑(在控制器销毁时执行),这样可以避免页面切换后事件仍然存在导致的内存泄漏问题。
现在你再测试一下,滚动到页面顶部加载更多消息时,视图应该就能正常显示新添加的内容了!
内容的提问来源于stack exchange,提问作者LazioTibijczyk
相关产品推荐
相关产品推荐

