msforest / notebook

好记性不如烂笔头,记录知识的点点滴滴。
https://github.com/msforest/notebook/wiki
0 stars 0 forks source link

angular中使用指令封装通讯录 #8

Open msforest opened 7 years ago

msforest commented 7 years ago

之前对js版的通讯录进行了一些修改,数据从静态改成动态加载显示,经过一番思考后很快就改完了。现在要将通讯录在angular中使用,考虑到数据也是动态加载,有点小麻烦。一一记录在此: 按照angular的思想,操作DOM结构不应该写在controller里,应该用指令来进行封装,而复杂业务逻辑应该写服务中。 尝试用angular写的代码如下: 一个精简的页面ng-txl.html

<!DOCTYPE html>
<html ng-app='app'>
<head>
    <meta charset="utf-8">
    <title>ng-txl</title>
    <link rel="stylesheet" href="style.css">
    <script type="text/javascript" src="jquery-1.8.3.min.js"></script>
    <script type="text/javascript" src="angular.js"></script>
    <script type="text/javascript" src="jquery.charfirst.pinyin.js"></script>
    <script type="text/javascript" src="ng-txl.js"></script>
</head>
<body>
<div ng-controller="txlCtrl">
    <txl></txl>
</div>
</body>
</html>

然后是ng-txl.js,我将service,directive,controller写在一起了,当然,这是不可取的。

var app = angular.module('app',[]);

app.service('InitialSort', function(){
    this.sort = function(html){
        var Initials=$('.initials');
        var LetterBox=$('#letter');
        Initials.find('ul').append('<li>A</li><li>B</li><li>C</li><li>D</li><li>E</li><li>F</li><li>G</li><li>H</li><li>I</li><li>J</li><li>K</li><li>L</li><li>M</li><li>N</li><li>O</li><li>P</li><li>Q</li><li>R</li><li>S</li><li>T</li><li>U</li><li>V</li><li>W</li><li>X</li><li>Y</li><li>Z</li><li>#</li>');
        initials2(html);
        $(".initials ul li").click(function(){
            var _this=$(this);
            var LetterHtml=_this.html();
            LetterBox.html(LetterHtml).fadeIn();
            Initials.css('background','rgba(145,145,145,0.6)');
            setTimeout(function(){
                Initials.css('background','rgba(145,145,145,0)');
                LetterBox.fadeOut();
            },1000);

            var _index = _this.index()
            if(_index==0){
                $('html,body').animate({scrollTop: '0px'}, 300);//点击第一个滚到顶部
            }else if(_index==27){
                var DefaultTop=$('#default').position().top;
                $('html,body').animate({scrollTop: DefaultTop+'px'}, 300);//点击最后一个滚到#号
            }else{
                var letter = _this.text();
                if($('#'+letter).length>0){
                    var LetterTop = $('#'+letter).position().top;
                    $('html,body').animate({scrollTop: LetterTop-45+'px'}, 300);
                }
            }
        })

        var windowHeight=$(window).height();
        var InitHeight=windowHeight-45;
        Initials.height(InitHeight);
        var LiHeight=InitHeight/28;
        Initials.find('li').height(LiHeight);
    }
});

app.directive('initialSort', ['$compile', 'InitialSort', function($compile, InitialSort){
    return {
        restrict:'EC',
        template: `<div>
                        <div id="letter" ></div>
                        <div class="sort_box"></div>
                        <div class="initials">
                            <ul>
                                <li><img src="img/068.png"></li>
                            </ul>
                        </div>
                    </div>`,
        link:function(scope, element, attr){
            var html = [], item=null;
            for(var i = 0,len = scope.chineseArr.length; i < len; i++){
                item = chineseArr[i];
                html += `<div class="sort_list" ng-click=payee(${JSON.stringify(item)})><div class="num_name">${item.name}</div></div>`;
                item = null;
            }
            InitialSort.sort(html);
            $compile(element.contents())(scope);  //使用compile对dom进行再次编译
        }
    }
}]);
app.controller('txlCtrl', ['$scope', function($scope){
        $scope.chineseArr = [
                        {"id":1,name:"涨水"},
                        {"id":2,name:"美女"},
                        {"id":3,name:"准备"},
                        {"id":4,name:"请问"},
                        {"id":5,name:"水电费"},
                        {"id":6,name:"不能"},
                        {"id":7,name:"更好"},
                        {"id":8,name:"熬吧"},
                        {"id":9,name:"凉快了"},
                        {"id":10,name:"潍坊"},
                        {"id":11,name:"拉屎"},
                        {"id":12,name:"胸围"},
                        {"id":13,name:"漂亮"},
                        {"id":14,name:"离开"},
                        {"id":15,name:"额无法"},
                        {"id":16,name:"123"},
                        {"id":17,name:"+sdkl"},
                        {"id":18,name:"AB"}
                        ];
            $scope.payee = function(item){
                console.log(item);
            }
}]);

这里有用到es6的模板字符串,想想它比字符串拼接更简单,我就用了,因为正好我开始看es6方面的知识了。 以上代码是没有问题的,效果也是OK的,现在说说在编写directive的过程中遇到的问题:

一开始按照基本的指令编写方式完成,页面展示的效果也是没问题的,beautiful.但是click没有用,控制台没有打印我想要的东西,其实知道问题出现在哪儿,指令template的模板,然后在link函数对template进行动态修改dom结构,这时,使用的ng-click肯定是没有效果的,这需要对angular的指令生命周期要有一点了解,不然问题出现在哪儿都不知道,在link函数里动态添加的dom结构不在angular监听范围内,这个时候的dom结构几乎已经出现在真实页面中展示了,要想对link函数动态添加的dom结构使用ng的东西,需要对动态dom进行angular的强制编译,来达到在我的监管范围内的目的,即使用$compile。这时,ng-click就有效了, 到这一步的时候,控制台还有一点错误,和往常一样的写法ng-click=payee(item),这不会报错,但是item具体的对象值没有传递过来,而是传了'item'字符串,这我又不乐意了,于是,改成了ng-click=payee(${item}),控制台报错,这就好办了,有错误就代表有解决方案,没错误才是最危险的,报什么传递的是对象[object,object],不符合语法规范,然后在stackoverflow网上找到解决方案,就是上面所写的ng-click=payee(${JSON.stringify(item)}),这样问题就没了。


2016.11.30 今天又发现了一个问题,就是通讯录显示的数据不会随着数据的更新而更新,这算是一个很严重的问题,所幸发现的早,有更多的时间去解决,这里发解决方案记录一下: 指令的生成是根据chineseArr值来变化的,所以我们去监听chineseArr值的变化

link:function(scope, element, attr){
//对新旧值进行监听保证数据变化时,指令也要更新
scope.$watch('chineseArr',function(newValue,oldValue){
     if (newValue){
        $(element).find('.sort_box').html("");  //置空原有html 
        var html = [], item=null;
        for(var i = 0,len = scope.chineseArr.length; i < len; i++){
            item = scope.chineseArr[i];
            html += `<div class="sort_list" ng-click=payee(${JSON.stringify(item)})><div class="num_name">${item.name}</div></div>`;
            item = null;
        }
        InitialSort.sort(html, scope.chineseArr);
        $compile(element.contents())(scope);  //使用compile对dom进行再次编译
     }
});
}

一开始我一直在想怎么让指令重新渲染,问过一些人,也搜了网上,都是说用$compile重新编译,这种方法没用,郁闷了一小会。


2016-12-25 增加触摸字母列表滑动的功能,修改自定义服务

app.service('InitialSort', function(){
    this.sort = function(html, list){
        var Initials=$('.initials');
        var LetterBox=$('#letter');
        Initials.find('ul').append('<li>A</li><li>B</li><li>C</li><li>D</li><li>E</li><li>F</li><li>G</li><li>H</li><li>I</li><li>J</li><li>K</li><li>L</li><li>M</li><li>N</li><li>O</li><li>P</li><li>Q</li><li>R</li><li>S</li><li>T</li><li>U</li><li>V</li><li>W</li><li>X</li><li>Y</li><li>Z</li><li>#</li>');
        initials2(html, list);

        $(".initials ul li").on('click', function(e){
            var _this=$(this);
            var LetterHtml=_this.html();
            LetterBox.html(LetterHtml).fadeIn();

            Initials.css('background','rgba(145,145,145,0.6)');
            setTimeout(function(){
                Initials.css('background','rgba(145,145,145,0)');
                LetterBox.fadeOut();
            },1000);

            var _index = _this.index()
            if(_index==0){
                $('html,body').animate({scrollTop: '0px'}, 300);//点击第一个滚到顶部
            }else if(_index==27){
                var DefaultTop=$('#default').position().top;
                $('html,body').animate({scrollTop: DefaultTop+'px'}, 300);//点击最后一个滚到#号
            }else{
                var letter = _this.text();
                if($('#'+letter).length>0){
                    var LetterTop = $('#'+letter).position().top;
                    $('html,body').animate({scrollTop: LetterTop-45+'px'}, 300);
                }
            }
        });
        $('.initials ul').on('touchmove', function(event){
            event.preventDefault();
            var x = event.originalEvent.targetTouches[0].clientX;
            var y = event.originalEvent.targetTouches[0].clientY;
            var target = document.elementFromPoint(x,y);
            if(!target) return ;

            var LetterHtml=target.innerHTML,
                arr = '<img src="images/star.png">abcdefghijklmnopqrstuvwxyz#';
            if(arr.indexOf(LetterHtml.toLowerCase()) === -1) return ;

            console.log(LetterHtml)
            LetterBox.html(LetterHtml).fadeIn();

            if('<img src="img/068.png">' === LetterHtml){
                $('html,body').animate({scrollTop: '0px'}, 0);//点击第一个滚到顶部
            }else if('#' === LetterHtml){
                var DefaultTop=$('#default').position().top;
                $('html,body').animate({scrollTop: DefaultTop+'px'}, 0);//点击最后一个滚到#号
            }else{
                var letter = LetterHtml;
                if($('#'+letter).length>0){
                    var LetterTop = $('#'+letter).position().top;
                    $('html,body').animate({scrollTop: LetterTop-45+'px'}, 0);
                }
            }
        }).on('touchstart', function(e){
            Initials.css('background','rgba(145,145,145,0.6)');
        }).on('touchend', function(e){
            setTimeout(function(){
                Initials.css('background','rgba(145,145,145,0)');
                LetterBox.fadeOut();
            },500);
        });

        var windowHeight=$(window).height();
        var InitHeight=windowHeight-45;
        Initials.height(InitHeight);
        var LiHeight=InitHeight/28;
        Initials.find('li').height(LiHeight);
    }
});

代码在文件夹的ng命名的文件

It is not easy to meet each other in such a big world.- -世界这么大,能遇见,不容易。

msforest commented 7 years ago

过滤repeat的结果指令用法:

<div ng-repeat="person in persons | filter:name as results">
    {{person.name}}
</div>
<li ng-show="results.length == 0">
      <strong>No results found...</strong>
</li>