Angularjs ng-repeat,不改变view视图

今天学习angular的时候,我将对于数据定义和操作方法都放到service里面,dom操作当然是放到了directive里面,然后碰到ng-repeat视图不刷新的情况:

index.html

<ul ng-controller="MainController">
    <li ng-repeat="book in books track by book.title">
        <span>{{book.title}}</span>
        <span>{{book.author}}</span>
    </li>
    <!--<button ng-click="add()">添加</button>-->
</ul>
<button add-book-button>Add Book</button>

directive.js

app.directive('addBookButton',function($_book){
    return {
        restrict: 'A',
        link: function(scope, ele, attr){
            ele.bind('click',function(){
                $_book.service.addBook({title: 'Hello,world', name: 'XL'});
            })
        }
    }
});

service.js

app.service('$_book',['$rootScope',function($rootScope){
    this.service = {
        books: [
            {
            title: 'Magician',
            author: 'Raymood'
            },
            {
            title: 'The Hobbit',
            author: 'J.R.R Tolkien'
            }
        ],
        addBooks: function(book){
            this.books.push(book);
            $rootScope.broadcast('books.update');
        }
    }
}])

    

MainController.js

app.controller('MainController',function($scope){
    $scope.books = $scope.service.books;
    $scope.$on('books.update',function(){
        $scope.books = $scope.service.books;    
        console.log($scope.books);    //这里打印出来的是包含3个object的数组,但是view层只显示一开始页面刷新的2个object
    })
})

出现的问题是MainController.js里面注释的部分。

但是如果我在MainController.js里面添加一个ng-click事件,触发添加数组这个事件的话,在view层是会动态添加新object的。

暂时没找到是什么原因导致这个情况的出现。

阅读 9.3k
3 个回答

刚看到评论,又想了一下,的确有点跑题了,根本原因是books的修改是通过dom事件触发的,这已经脱离angular了,导致angular的watch无反应,因此需要在dom事件处理完毕后手动调用apply触发angular的脏检查,正确的解决办法应该是:

app.directive('addBookButton',function($_book){
    return {
        restrict: 'A',
        link: function(scope, ele, attr){
            ele.bind('click',function(){
                $_book.service.addBook({title: 'Hello,world', name: 'XL'});
                scope.$apply();
            })
        }
    }
});

controller中的$apply是不需要的。
例子
-------------------------原答案------------------------

$scope.books = $scope.service.books; 

关键是这句话,你把scope的books对象跟service的books对象绑定到一起,ng-repeat也是绑定到同样的对象,换句话说这三个对象的引用是相同的,view的更新是伴随着脏检查的,而angular的脏检查有三种极致:

  1. 引用reference

  2. 集合collection

  3. 值value

区别直接从官网截张图吧:
clipboard.png

同时注意通过reference watch的对象:

Watching by reference ...detects a change when the whole value returned by the watch expression switches to a new value. If the value is an array or an object, changes inside it are not detected

回到你的例子,books是通过reference进行watch的,这就意味着你对books数组里面内容的修改是不会导致脏检查。

明白这一点暴力的解决办法很简单:更新数据后通过调用$scope.$apply("books")手动触发angular的脏检查流程.

例子

注意你在ng-repeat中设置了track为title,这意味着相同title的book会被认为重复,导致报错,所以我在新增book的时候添加了一个变量。另外,你的service方法名、方法体看着有点别扭,顺手改了下。

在网上晃了一圈,自己来回答这个问题:

Angular会自动监听DOM事件和$http事件来动态刷新view层,但是如果是自定义的事件(有时候调用jQuery的时候),就像我在这里的service里面注册的$rootScope.broadcast('book.update')这个事件,虽然$scope.books发生了改变,但是Angular并不监听到这个变化,因此view层不会自动刷新,那么这个时候需要手动触发$digest操作,这里选择使用$scope.$apply()的方法来触发。这样Angular就会监听到了$scope.books上发生的变化,因此view层便会动态刷新了。

给需要ng-repeat的地方重新赋值,并重新show出div即可。
如:

<div id="test">
 <span ng-repeat="plateNo in historyPlates" ">{{plateNo.plateNo}}</span>
</div>

(1)$scope.historyPlates=新值
(2)$("#test").show();

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏