zoukankan      html  css  js  c++  java
  • angular中关于自定义指令——repeat渲染完成后执行动作

    业务中有时需要在异步获取数据并用ng-repeat遍历渲染完页面后执行某个操作,angular本身并没有提供监听ng-repeat渲染完成的指令,所以需要自己动手写。有经验的同学都应该知道,在ng-repeat模板实例内部会暴露出一些特殊属性$index/$first/$middle/$last/$odd/$even,$index会随着每次遍历(从0开始)递增,当遍历到最后一个时,$last的值为true,so,通过判断$last的值来监听ng-repeat的执行状态,怎么在遍历过程中拿到$last的值:自定义指令

    小实例,我只写了最重要的部分

    复制代码
    //要循环的数据
    $scope.data = [
        {
            str: 'a'
        },
        {
            str: 'b'
        },
        {
            str: 'c'
        }
    ]
    //自定义指令repeatFinish
    app.directive('repeatFinish',function(){
        return {
            link: function(scope,element,attr){
                console.log(scope.$index)
                if(scope.$last == true){
                    console.log('ng-repeat执行完毕')
                }
            }
        }
    })
    <div id="box">
        <span ng-repeat="item in data" repeat-finish>{{item.str}}</span>
    </div>
    复制代码

    打开控制台,会打印出0,1,2,当$index = 2点时候,$last值为true,ng-repeat渲染完毕 

    so easy!

    当然指令最好是能够复用,在这个指令内写具体的业务逻辑不利于复用,可以通过给指令指定一个处理函数renderFinish

    <div id="box">
        <span ng-repeat="item in data" repeat-finish="renderFinish()">{{item.str}}</span>
    </div>

    再通过指令的attr参数获取这个处理函数

    复制代码
    app.directive('repeatFinish',function(){
        return {
            link: function(scope,element,attr){
                console.log(scope.$index)
                if(scope.$last == true){
                    console.log('ng-repeat执行完毕')
                    scope.$eval( attr.repeatFinish )
                }
            }
        }
    })
    //controller里对应的处理函数
    $scope.renderFinish = function(){
        console.log('渲染完之后的操作')
    }
    复制代码

    attr获取到的属性只是一个字符串表达式,$scope.$eval方法是专门执行AngularJS表达式的,通过它处理函数得以执行,这样,指令用在不同的地方,可传递不同的处理函数。

    有些业务比较复杂,可能ng-repeat渲染完成之后,需要执行多个操作并且这多个操作有多个前端完成,需要用到angular的事件,在repeatFinish指令的link函数内触发一个事件,各位前端同学监听该事件完成各自的操作

    复制代码
    app.directive('repeatFinish',function(){
        return {
            link: function(scope,element,attr){
                console.log(scope.$index)
                if(scope.$last == true){
                    console.log('ng-repeat执行完毕')
                    //向父控制器传递事件
                    scope.$emit('to-parent');
                    //向子控制器传递事件
                    scope.$broadcast('to-child');
                }
            }
        }
    })
    //父控制器中监听事件
    $scope.$on('to-parent',function(){
        //父控制器执行操作
    })
    
    //子控制器中监听事件
    $scope.$on('to-child',function(){
        //子控制器执行操作
    })
    复制代码

    如何在当前控制器下监听到该事件呢?angular没有向当前控制器传递事件的方法,可以先向父(子)控制器传递事件,父(子)控制器监听到事件后反过来向子(父)控制器传递事件。

    补充:IE8可以直接在元素上用指令ng-if="$last && renderFinish()",当然IE8+也可以这样用

    一句话总结:指令是angular的核心功能之一,用好了事半功倍,监听ng-repeat执行状态仅仅是它功能的冰山一角吧

    补充2:然后我在使用的时候发现一个问题。这个问题依然是延时回调引起的,这不同于$http的异步问题,它不是异步,仅仅是延时问题。

        我实用fbug断点调试发现,原来所谓的渲染完毕,其实并没有真正把数据填上,而是dom的渲染完毕,数据要等所有的dom渲染完毕之后对号入座。

        可能有点抽象,直接上图。

        

        看效果,

    $('.mybox-content').scrollTop( $('.mybox-content')[0].scrollHeight );

        我使用scrollTop来对滚动条进行定位,原本应该是直接到最底部的,打印出来相应数据进行计算也是没有问题的,可是还是出现了这种情况,而且随着数据的增加,下面没滚动上去的也相应增加。

        我就很苦恼,思考了很久很久,终于想通问题关键。

        关键点就在于多出来的那部分,那么为什么多呢?是不是每一条的高度计算错误?这么想来,就对了,当聊天文本是多行的时候,就比默认的单行高度高出了一部分,而随着聊天记录的增加,多出来的这部分就随之增加。

        这么看来就凸显出上面的问题了——渲染并没有真正完成。于是我打开断点,如下:

        

        从图中我们可以看到,2 部分是渲染完成后进行的二次处理,处理完进入 3 ——滚动条置底。然而马上就要执行 3 的时候 2 部分却只有dom,没有相应的真实数据。而就像上面所说的,真实数据填入后条目的高度可能会变高,这就导致了滚动条不能置底。

        问题明白了,解决办法就简单了。

        

    setTimeout(function(){
        $('.mybox-content').scrollTop( $('.mybox-content‘)[0].scrollHeight );
    }, 1);

        只需要把代码放到一个延时器中就可以了,延时时间,1ms。。。。

  • 相关阅读:
    UIAutomator简介
    初识Anrdiod SDK
    Andriod App类型简介
    Mybatis之collection嵌套查询mapper文件写法
    Ubuntu16.04 启动纯文本界面方法
    Ubuntu16.04网络不能访问解决办法
    JavaScript之简易http接口测试工具网页版
    SpringBoot之修改单个文件后立刻生效
    JavaScript之Json的使用
    Ajax之Jquery封装使用举例2(Json和JsonArray处理)
  • 原文地址:https://www.cnblogs.com/yunzhexiaye/p/6293672.html
Copyright © 2011-2022 走看看