美文网首页
AngularJS Dropdown指令相关的总结

AngularJS Dropdown指令相关的总结

作者: void_main | 来源:发表于2016-09-22 11:36 被阅读1702次

Dropdown组件

自己封装的一个简单的dropdown的组件

.ui-dropdown {
    position:relative;
    display:inline-block;
    *display:inline;
    *zoom:1;
    width:162px;
    border-radius:3px;
    text-align:left;
    font:12px/28px tahoma,arial,Hiragino Sans GB,WenQuanYi Micro Hei,'\5FAE\8F6F\96C5\9ED1','\5B8B\4F53',sans-serif;
    vertical-align:top
}
.ui-dropdown-bd {
    display:none;
    top:31px;
    left:0;
    min-width:calc(100% - 2px);
    max-height:346px;
    overflow-y:auto;
    overflow-x:hidden;
    _overflow:auto;
    position:absolute;
    padding-bottom:8px;
    background-color:#fff;
    border:1px solid #848484;
    border-radius:0 0 3px 3px;
    -webkit-box-shadow:0 0 0 2px #f7f7f7;
    box-shadow:0 0 0 2px #f7f7f7
}
.ui-dropdown-bd-right {
    left:auto;
    right:0
}
.ui-dropdown-bd .selected a,.ui-dropdown-bd .selected a:hover,.ui-dropdown-bd .selected a:active {
    background-color:#f10180;
    color:#fff
}
.ui-dropdown-bd a {
    display:block;
    padding:0 10px;
    text-overflow:ellipsis;
    white-space:nowrap;
    overflow:hidden;
    color:#333;
    text-decoration:none;
    *zoom:1
}
.ui-dropdown-bd a:hover {
    color:#666;
    text-decoration:none;
    background-color:#ededed
}
.ui-dropdown-bd a:active {
    background-color:#f10180;
    color:#fff
}
.ui-dropdown-hd {
    position:relative;
    z-index:2;
    padding:0 10px;
    border:1px solid #b3b3b3;
    border-radius:3px;
    background-color:#fff;
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
    cursor:pointer;
    *zoom:1
}
.ui-dropdown-hd:hover {
    border-color:#949494
}
.ui-dropdown-hd:hover .vipFont {
    color:#949494
}
.ui-dropdown-hd .vipFont {
    position:absolute;
    right:7px;
    color:#b3b3b3;
    -webkit-transition:-webkit-transform .2s;
    transition:transform .2s;
    line-height:1
}
.ui-dropdown-hd .i-arrow-down {
    top:7px
}
.ui-dropdown-hd .i-arrow-up {
    visibility:hidden;
    -webkit-transform:rotate(-180deg);
    -ms-transform:rotate(-180deg);
    transform:rotate(-180deg);
    top:6px
}
.ui-dropdown-current {
    display:block;
    zoom:1;
    color:#999;
    line-height:30px;
    text-decoration:none;
    font-size:14px;
    margin-right:10px;
    text-overflow:ellipsis;
    white-space:nowrap;
    overflow:hidden
}
.ui-dropdown-current:hover {
    color:#999;
    text-decoration:none
}
.ui-dropdown-tips {
    padding:12px 0 4px 10px;
    line-height:20px;
    color:#ed1616
}
.ui-dropdown .ui-dropdown-menu .ui-dropdown-loading {
    float:none;
    width:auto;
    padding-top:10px;
    line-height:80px;
    text-align:center;
    font-size:14px;
    color:#333
}
.ui-dropdown .ui-dropdown-menu .ui-dropdown-loading .ii-loading-pink-16x16,.ui-dropdown .ui-dropdown-menu .ui-dropdown-loading .ii-loading-pink-24x24,.ui-dropdown .ui-dropdown-menu .ui-dropdown-loading .ii-loading-pink-32x32 {
    vertical-align:middle;
    margin-right:12px
}
.ui-dropdown .ui-dropdown-menu .ui-dropdown-loading .text {
    display:inline-block;
    vertical-align:middle
}
.ui-dropdown-col-2,.ui-dropdown-col-3,.ui-dropdown-col-4 {
    width:115px
}
.z-ui-dropdown-open .ui-dropdown-col-2 .ui-dropdown-hd,.z-ui-dropdown-open .ui-dropdown-col-3 .ui-dropdown-hd,.z-ui-dropdown-open .ui-dropdown-col-4 .ui-dropdown-hd {
    border-bottom:0;
    padding-bottom:1px
}
.ui-dropdown-col-2 .ui-dropdown-bd,.ui-dropdown-col-3 .ui-dropdown-bd,.ui-dropdown-col-4 .ui-dropdown-bd {
    padding-top:8px
}
.ui-dropdown-col-2 .ui-dropdown-tips,.ui-dropdown-col-3 .ui-dropdown-tips,.ui-dropdown-col-4 .ui-dropdown-tips {
    padding-top:0
}
.ui-dropdown-col-2 .ui-dropdown-menu,.ui-dropdown-col-3 .ui-dropdown-menu,.ui-dropdown-col-4 .ui-dropdown-menu {
    padding:0 5px
}
.ui-dropdown-col-2 .ui-dropdown-menu li,.ui-dropdown-col-3 .ui-dropdown-menu li,.ui-dropdown-col-4 .ui-dropdown-menu li {
    float:left;
    _overflow:hidden
}
.ui-dropdown-col-2 .ui-dropdown-menu li a,.ui-dropdown-col-3 .ui-dropdown-menu li a,.ui-dropdown-col-4 .ui-dropdown-menu li a {
    _width:83px;
    padding:0 0 0 5px;
    margin:0 5px 2px
}
.ui-dropdown-col-2 .ui-dropdown-bd {
    width:190px
}
.ui-dropdown-col-2 .ui-dropdown-menu li {
    width:50%
}
.ui-dropdown-col-3 .ui-dropdown-bd {
    width:280px
}
.ui-dropdown-col-3 .ui-dropdown-menu li {
    width:33%
}
.ui-dropdown-col-4 .ui-dropdown-bd {
    width:370px
}
.ui-dropdown-col-4 .ui-dropdown-menu li {
    width:25%
}
.ui-dropdown-float {
    line-height:26px;
    width:120px
}
.ui-dropdown-float:hover,.ui-dropdown-float-hover {
    z-index:100
}
.ui-dropdown-float:hover .ui-dropdown-hd .vipFont,.ui-dropdown-float-hover .ui-dropdown-hd .vipFont {
    color:#848484;
    line-height:1
}
.ui-dropdown-float:hover .ui-dropdown-hd .i-arrow-up,.ui-dropdown-float-hover .ui-dropdown-hd .i-arrow-up {
    visibility:visible;
    -webkit-transform:rotate(0deg);
    -ms-transform:rotate(0deg);
    transform:rotate(0deg)
}
.ui-dropdown-float:hover .ui-dropdown-hd .i-arrow-down,.ui-dropdown-float-hover .ui-dropdown-hd .i-arrow-down {
    visibility:hidden;
    -webkit-transform:rotate(180deg);
    -ms-transform:rotate(180deg);
    transform:rotate(180deg)
}
.ui-dropdown-float:hover .ui-dropdown-bd,.ui-dropdown-float-hover .ui-dropdown-bd {
    display:block
}
.ui-dropdown-float:hover .ui-dropdown-current,.ui-dropdown-float-hover .ui-dropdown-current {
    color:#333
}
.ui-dropdown-float .ui-dropdown-current {
    display:inline-block;
    padding-right:16px;
    margin-right:0;
    color:#666;
    font-size:12px
}
.ui-dropdown-float .ui-dropdown-current:hover {
    color:#333
}
.ui-dropdown-float .ui-dropdown-hd {
    border:0 none;
    background:0
}
.ui-dropdown-float .ui-dropdown-hd .vipFont {
    right:auto;
    margin-left:-16px
}
.ui-dropdown-float .ui-dropdown-bd {
    top:0;
    max-height:312px;
    padding-top:30px;
    border-radius:0;
    border-color:#cbcaca
}
.ui-dropdown-float .ui-dropdown-bd a {
    color:#666
}
.ui-dropdown-float .ui-dropdown-bd a:hover,.ui-dropdown-float .ui-dropdown-bd a:active {
    background:0;
    color:#f10180
}
.ui-dropdown-float .ui-dropdown-bd .selected a,.ui-dropdown-float .ui-dropdown-bd .selected a:hover,.ui-dropdown-float .ui-dropdown-bd .selected a:active {
    background:0;
    color:#f10180
}
.ui-dropdown-float .ui-dropdown-menu .ui-dropdown-loading {
    line-height:50px;
    font-size:12px
}
.z-ui-dropdown-selected .ui-dropdown-current,.z-ui-dropdown-selected .ui-dropdown-current:hover {
    color:#333
}
.z-ui-dropdown-disable .ui-dropdown-hd {
    background-color:#f0efef;
    color:#999;
    cursor:not-allowed
}
.z-ui-dropdown-disable .ui-dropdown-hd:hover .vipFont {
    color:#b3b3b3
}
.z-ui-dropdown-disable .ui-dropdown-current,.z-ui-dropdown-disable .ui-dropdown-current:hover {
    color:#999;
    cursor:not-allowed
}
.z-ui-dropdown-open {
    z-index:100;
    -webkit-box-shadow:0 0 0 2px rgba(153,153,153,.08);
    box-shadow:0 0 0 2px rgba(153,153,153,.08)
}
.z-ui-dropdown-open .ui-dropdown-hd {
    border-radius:3px 3px 0 0;
    border-color:#848484;
    border-bottom-color:#dcdada
}
.z-ui-dropdown-open .ui-dropdown-hd .vipFont {
    color:#848484
}
.z-ui-dropdown-open .ui-dropdown-hd .i-arrow-up {
    visibility:visible;
    -webkit-transform:rotate(0deg);
    -ms-transform:rotate(0deg);
    transform:rotate(0deg)
}
.z-ui-dropdown-open .ui-dropdown-hd .i-arrow-down {
    visibility:hidden;
    -webkit-transform:rotate(180deg);
    -ms-transform:rotate(180deg);
    transform:rotate(180deg)
}
.z-ui-dropdown-open .ui-dropdown-bd {
    display:block
}
function BindDropdownEvent(elements, callback) {

    $('.ui-dropdown-hd', elements).unbind().click(function() {

        var element = $(this),
            uiDropdown = element.parent('.ui-dropdown');
        if (element.hasClass('z-ui-dropdown-disable')) {
            return false;
        }

        if (uiDropdown.hasClass('z-ui-dropdown-open')) {
            uiDropdown.removeClass('z-ui-dropdown-open');
        } else {
            $('.ui-dropdown').not(uiDropdown).removeClass('z-ui-dropdown-open');
            $('.multi-dropdown').not(uiDropdown).removeClass('z-ui-dropdown-open');
            uiDropdown.addClass('z-ui-dropdown-open');
            $(document).one('click', function() {
                uiDropdown.removeClass('z-ui-dropdown-open');
                return false;
            });
        }
        return false;
    });

    $('.ui-dropdown-bd', elements).unbind().each(function() {
        var element = $(this),
            uiDropdown = element.parent('.ui-dropdown'),
            liList = element.find('li'),
            uiDropdownCurrent = uiDropdown.find('.ui-dropdown-current');
        liList.click(function() {
            var subElement = $(this),
                item = subElement.find('a');
            liList.removeClass('selected');
            subElement.addClass('selected');

            var selectValue = item.attr('value');
            uiDropdownCurrent.attr('value', selectValue).text(item.text());
            uiDropdown.removeClass('z-ui-dropdown-open');
            $.isFunction(callback) && callback(item.text(), selectValue);

        });
    });
}

/**
     * Dropdown单选下拉框指令的实现
     * name : 区分不同的下拉框组件,字符串形式
     * ngModel : 单选下拉框绑定的值,默认是选中项的key值
     * dataList : 下拉框下拉选项列表,数组形式,数组元素对象包含key 以及value。key作为向后台交互的值,value作为前端控件显示的值
     * empty : 布尔属性值,当赋值为true的时候,对下拉框组件进行充值,下拉框中的值显示的是placeholder的值"请选择"
     * TODO : 多层级联下拉框的实现方法      ------     当下拉框列表接受点击事件时,广播带有组件name值为参数的selectUpdate的事件,用户可以捕获事件,根据时间的参数做出相应的响应。
     *        下拉框未选为空的状态
     *        下拉框相应双向数据绑定的model,当model变化时,对应的下拉框组件可以显示对应的选项
     */

    app.directive('dropdown',function(){
        return {
            restrict : 'EA',
            replace: true,
            template : '<div class="ui-form-item-group">'+
                            '<div class="ui-dropdown" ng-click="dropdown()">'+
                                '<div class="ui-dropdown-hd">'+
                                    '<a href="javascript:;" role="button" title="请选择" class="ui-dropdown-current">请选择</a> <i class="vipFont i-arrow-up"></i> <i class="vipFont i-arrow-down"></i>'+
                                '</div>'+
                                '<div class="ui-dropdown-bd">'+
                                    '<ul class="ui-dropdown-menu" >'+
                                        '<li ng-repeat="item in datalist" >'+
                                            '<a href="javascript:;" role="button" value="item.key" ng-click="select()">{{item.value}}</a>'+
                                        '</li>'+
                                    '</ul>'+
                                '</div>'+
                            '</div>'+
                        '</div>',
            scope :{
                name : '@',
                ngModel : "=",
                datalist : "=",
                empty : "="
            },
            link : function(scope,iElement,iAttrs){
                debugger
                var $elem = $(iElement);

                var dependents = scope.dependents ? scope.dependents.split(',') : false;
                var parentScope = scope.$parent;
                scope.name = scope.name || 'multi-select-' + Math.floor(Math.random() * 900000 + 100000);
                
                scope.$watch('empty',function(newValue, oldValue, scope){
                    debugger
                    if(newValue != oldValue){
                        if( newValue == true){
                            $elem.find('.ui-dropdown').removeClass("z-ui-dropdown-selected");
                            $elem.find(".ui-dropdown-current").text('请选择');      
                        }

                         
                    }
                })
                
                scope.$watch('ngModel',function(newValue,oldValue,scope){
                    console.log('----------enter watch-------------');
                    debugger
                    if(newValue != oldValue){
                         scope.$root.$broadcast('selectUpdate', {
                                    // 将变动的菜单的name属性广播出去,便于依赖于它的菜单进行识别
                                    name: scope.name
                                });
                        for(var item in scope.datalist){
                            if(scope.datalist[item].key === newValue){
                                $elem.find(".ui-dropdown-current").text(scope.datalist[item].value);   
                                
                            }
                        }
                        
                    }
                })

                scope.dropdown = function(){
                    $elem.find('.ui-dropdown').toggleClass("z-ui-dropdown-open");

                }

                scope.select = function(){
                    // "z-ui-dropdown-selected"
                    debugger
                    $elem.find(".ui-dropdown").addClass("z-ui-dropdown-selected");
                    scope.empty = false;
                    scope.ngModel = scope.datalist[this.$index].key;
                    
                    // iAttrs["ngModel"] = "0"+this.$index;
//                    $elem.find(".ui-dropdown-current").text(scope.datalist[this.$index].value);
                    // scope.datafunc();
                   
                }
            }
        }
    })

在页面中使用

 <label class="ui-label label-margin">标记原因:</label>
                                                    <dropdown ng-model="formData.markReason" datalist="markReasons" name="markreasons-select"></dropdown>

级联事件的响应


 $scope.$on('selectUpdate',function(e,data){
            //TODO:
            debugger
            // console.log(data);
            if(data.name == "risktypes-select"){
                if($scope.formData.riskType == ''){
                    $scope.markReasons2 = [{key : '', value:"全部"}];
                    $scope.formData.markReason = '';
                    return ;
                }
                $scope.formData.markReason = '';
                util.ajax({
                    "url": "XXXX",
                    "type": "post",
                    $http: $http,
                    contentType: "application/x-www-form-urlencoded; charset=utf-8",
                    data: {
                        riskType: $scope.formData.riskType
                    },
                    success: function(res) {                
                        if (!res) {
                            return _showMessage("对不起您没有权限", "信息错误");
                        }else if(res.success == false) {
                            return _showMessage("用户信息不匹配(未查到相关数据!)", "信息错误")
                        }else {     
                            $scope.reasonListSeleted = res.results._defaultResult;
                            debugger
                                    $scope.markReasons2 = [{key : '', value:"全部"}];
                                for (var i = 0; i < $scope.reasonListSeleted.length; i++) {
                                    $scope.markReasons2.push({
                                        key : 
                                        $scope.reasonListSeleted[i].reasonId,
                                        value : 
                                        $scope.reasonListSeleted[i].reasonDesc
                                    });
                                }
                        };
                    }
                });
            }
        })

相关注意点

  • 指令绑定参数时,最好不要是基本数据类型
    指令在进行值绑定的时候,传入的值最好是非基本类型。譬如绑定一个字符串类型riskTypengModel上,ngModel需要双向数据绑定。
<dropdown ng-model="riskType" datalist="riskTypes">

这样的绑定会出现一定的问题
最好在controller中传入的参数的对象的属性,如下

<dropdown ng-model="obj.riskType" datalist="riskTypes">

在这个示例中datalist绑定的数据是数组类型,并非是简单数据类型,所以直接绑定即可。

扩展

后期又对下拉框进行优化,支持disable,搜索筛选选项等功能。

    app.directive('searchdropdown',['$document','$filter','$timeout',function($document,$filter,$timeout){
        return {
            restrict : 'EA',
            replace: true,
            template : '<div class="ui-form-item-group">'+
                            '<div class="ui-dropdown" >'+
                                '<div class="ui-dropdown-hd">'+
                                    '<input placeholder="请选择,输入可筛选" class="ui-dropdown-search" ng-model="searchData" style="border: none;font: inherit;" ng-show="searchable && searching">'+
                                    '<a href="javascript:;" role="button" title="请选择" class="ui-dropdown-current" ng-show="!(searchable && searching) "  ng-click="dropdown()">请选择</a> <i class="vipFont i-arrow-up"  ng-click="dropdown()"></i> <i class="vipFont i-arrow-down"  ng-click="dropdown()"></i>'+
                                '</div>'+
                                '<div class="ui-dropdown-bd | filter:searchData">'+
                                    '<ul class="ui-dropdown-menu" >'+
                                        '<li ng-repeat="item in datalist | filter:{text:searchData}" >'+
                                            '<a href="javascript:;" role="button" value="item.value" ng-click="select($event)">{{item.text}}</a>'+
                                        '</li>'+
                                    '</ul>'+
                                '</div>'+
                            '</div>'+
                        '</div>',
            scope :{
                ngModel : "=",
                datalist : "=",
                disabled : "=",
                searchable : "="
            },
            controller : function($scope){
                $scope.name = $scope.name || 'dropdown-' + Math.floor(Math.random() * 900000 + 100000);
                $scope.searching = false;
                $scope.myComparator = function (expected, actual) {
                    return angular.equals(expected.toLowerCase(), actual.toLowerCase());
                }
            },
            link : function(scope,iElement,iAttrs){
                
                var $elem = $(iElement);
               
                for(var item in scope.datalist){
                    if(scope.datalist[item].value === scope.ngModel){
                        $elem.find(".ui-dropdown-current").text(scope.datalist[item].text);   
                        $elem.find(".ui-dropdown").addClass("z-ui-dropdown-selected");
                    }
                }
                if(scope.disabled == true){
                    $elem.find(".ui-dropdown").addClass("z-ui-dropdown-disable");
                }

                $elem.find('.ui-dropdown-search').bind('focus',function(event){
                    
                    console.log('ui-dropdown-search is on focus');
                    $elem.find('.ui-dropdown').addClass("z-ui-dropdown-open");
                    event.stopPropagation();
                })

                // $elem.find(".ui-dropdown-hd").bind('blur',function(){
                //     alert('blur');
                // })

                // $elem.find('.ui-dropdown-search').bind('blur',function(event){
                    
                //     console.log('ui-dropdown-search is on blur');
                //     scope.$apply(function(){
                //         scope.searching = false;      
                //     })
                // })
                
                scope.dropdown = function(){
                    console.log("ui-dropdown trigger dropdown event");
                    if(scope.disabled)
                        return ;
                    $elem.find('.ui-dropdown').toggleClass("z-ui-dropdown-open");
                    if($elem.find('.ui-dropdown').hasClass("z-ui-dropdown-open")){
                        console.log("---------" + scope.name +"------bind $document event -----------");
                        // 对于这种问题,jQuery的解决方案是使用事件绑定的命名空间。即在事件名称后添加 .something 来区分自己这部分行为逻辑范围。  
                        $(document).bind("click."+scope.name, function(event) {
                            console.log("-------go into $document click"+scope.name+"-------");
                            var judge = $(event.target).closest($elem).length > 0;
                            // if(!$elem.find('.ui-dropdown').hasClass("z-ui-dropdown-open")){
                            //     console.log("---------" + scope.name +"------unbind $document event -----------");
                            //     $(document).unbind("click."+scope.name);
                            // }
                            if(!judge){
                                if(scope.searchable && scope.searching)  {
                                    scope.$apply(function(){
                                        scope.searching = false;   
                                    })
                                }
                                $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-open");   
                                $(document).unbind("click."+scope.name);
                            }
                        });
                    }else{
                         console.log("---------" + scope.name +"------unbind $document event -----------");
                        $(document).unbind("click."+scope.name);
                    }
                    if(scope.searchable){
                        scope.searching = !scope.searching;
                        // if(scope.searching){
                            scope.searchData = scope.ngModel;
                            var timer = $timeout(
                                function() {
                                    $elem.find('.ui-dropdown-search').focus();
                                    console.log( "Timeout executed", Date.now() );
                            },100);

                            scope.$on("$destroy",function( event ) {

                                $timeout.cancel( timer );
                            });
                        // }
                    }
                }

                scope.select = function($event){
                    
                    $elem.find(".ui-dropdown").addClass("z-ui-dropdown-selected");
                    var temp = $filter('filter')(scope.datalist,scope.searchData);
                    console.log(temp);
                    scope.ngModel = temp[this.$index].value;
                    $elem.find(".ui-dropdown-current").text(temp[this.$index].text)
                    $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-open");  
                    scope.searching = false;
                    console.log(scope.searching);
                }
                
                scope.$watch('ngModel',function(newValue,oldValue,scope){
                     var isHitted = false;
                    if(newValue != oldValue){
                        for(var item in scope.datalist){
                            if(scope.datalist[item].value === newValue){
                                $elem.find(".ui-dropdown-current").text(scope.datalist[item].text);   
                                $elem.find(".ui-dropdown").addClass("z-ui-dropdown-selected");
                                isHitted = true;
                            }
                        }
                        if(!isHitted){
                            $elem.find(".ui-dropdown-current").text('请选择');   
                            $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-selected");
                        }
                    }
                })

                // $(document).bind("click."+scope.name, function(event) {
                //             console.log("-------go into $document click-------");
                //             var judge = $(event.target).closest($elem).length > 0;
                //             if(!judge){
                //                 if(scope.searchable && scope.searching)  {
                //                     scope.$apply(function(){
                //                         scope.searching = false;   
                //                     })
                //                 }
                //                 $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-open");   
                //             }
                //         });


                // $document.bind("click", function(event) {
                //     var judge = $(event.target).closest($elem).length > 0;
                //     if(!judge){
                //         if(scope.searchable && scope.searching)  {
                //             // $elem.find('.ui-dropdown-search').blur();
                //             // console.log('manually trigger blur');
                //             scope.$apply(function(){
                //                 scope.searching = false;   
                //             })
                //         }
                //         $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-open");   
                //     }
                // });

                // $(document).one("click", function(event) {
                //     var judge = $(event.target).closest($elem).length > 0;
                //     if(!judge){
                //         if(scope.searchable && scope.searching)  {
                //             // $elem.find('.ui-dropdown-search').blur();
                //             // console.log('manually trigger blur');
                //             scope.$apply(function(){
                //                 scope.searching = false;   
                //             })
                //         }
                //         $elem.find(".ui-dropdown").removeClass("z-ui-dropdown-open");   
                //     }
                // });

            }
        }
    }]) 
simple-select.gif search-select.gif disbaled.png

AngularJS中实现无限级联动菜单

AngularJS中实现无限级联动菜单

directive('multiLevelSelect', ['$parse', '$timeout', function ($parse, $timeout) {
 
    // 利用闭包,保存父级scope中的所有多级联动菜单,便于取值
    var selects = {};
 
    return {
 
        restrict: 'CA',
 
        scope: {
            // 用于依赖声明时指定父级标签
            name: '@name',
 
            // 依赖数组,逗号分割
            dependents: '@dependents',
 
            // 提供具体option值的函数,在父级change时被调用,允许同步/异步的返回结果
            // 无论同步还是异步,数据应该是[{text: 'text', value: 'value'},]的结构
            source: '=source',
 
            // 是否支持控制选项,如果是,空值的标签是什么
            empty: '@empty',
 
            // 用于parse解析获取model值(而非viewValue值)
            modelName: '@ngModel'
        },
 
        template: ''
            // 使用ng-show而非ng-if,原因上文已经提到
            + '<option ng-show="empty" value="">{{empty}}</option>'
            // 使用朴素的ng-repeat
            + '<option ng-repeat="item in items" value="{{item.value}}">{{item.text}}</option>',
 
        require: 'ngModel',
 
        link: function (scope, elem, attr, model) {
 
            var dependents = scope.dependents ? scope.dependents.split(',') : false;
            var parentScope = scope.$parent;
            scope.name = scope.name || 'multi-select-' + Math.floor(Math.random() * 900000 + 100000);
 
            // 将当前菜单的getValue函数封装起来,放在闭包中的selects对象中方便调用
            selects[scope.name] = {
                getValue: function () {
                    return $parse(scope.modelName)(parentScope);
                }
            };
 
            // 保存初始值,原因上文已经提到
            var initValue = selects[scope.name].getValue();
 
            var inited = !initValue;
            model.$setViewValue('');
 
            // 父级标签变化时被调用的回调函数
            function onParentChange() {
                var values = {};
                // 获取所有依赖的菜单的当前值
                if (dependents) {
                    $.each(dependents, function (index, dependent) {
                        values[dependent] = selects[dependent].getValue();
                    });
                }
 
                // 利用闭包判断io造成的异步过期
                (function (thenValues) {
 
                    // 调用source函数,取新的option数据
                    var returned = scope.source ? scope.source(values) : false;
 
                    // 利用多层闭包,将同步结果包装为有then方法的对象
                    !returned || (returned = returned.then ? returned : {
                        then: (function (data) {
                            return function (callback) {
                                callback.call(window, data);
                            };
                        })(returned)
                    }).then(function (items) {
 
                        // 防止由异步造成的过期
                        for (var name in thenValues) {
                            if (thenValues[name] !== selects[name].getValue()) {
                                return;
                            }
                        }
 
                        scope.items = items;
 
                        $timeout(function () {
 
                            // 防止由同步(严格的说也是异步,注意事件队列)造成的过期
                            if (scope.items !== items) return;
 
                            // 如果有空值,选择空值,否则选择第一个选项
                            if (scope.empty) {
                                model.$setViewValue('');
                            } else {
                                model.$setViewValue(scope.items[0].value);
                            }
 
                            // 判断恢复初始值的条件是否成熟
                            var initValueIncluded = !inited && (function () {
                                for (var i = 0; i < scope.items.length; i++) {
                                    if (scope.items[i].value === initValue) {
                                        return true;
                                    }
                                }
                                return false;
                            })();
 
                            // 恢复初始值
                            if (initValueIncluded) {
                                inited = true;
                                model.$setViewValue(initValue);
                            }
 
                            model.$render();
 
                        });
                    });
 
                })(values);
 
                 
            }
 
            // 是否有依赖,如果没有,直接触发onParentChange以还原初始值
            !dependents ? onParentChange() : scope.$on('selectUpdate', function (e, data) {
                if ($.inArray(data.name, dependents) >= 0) {
                    onParentChange();
                }
            });
 
            // 对当前值进行监听,发生变化时对其进行广播
            parentScope.$watch(scope.modelName, function (newValue, oldValue) {
                if (newValue || '' !== oldValue || '') {
                    scope.$root.$broadcast('selectUpdate', {
                        // 将变动的菜单的name属性广播出去,便于依赖于它的菜单进行识别
                        name: scope.name
                    });
                }
            });
 
        }
    };
}]);

angularJS中组件复用和封装

https://www.zybuluo.com/lxjwlt/note/331587

http://www.jb51.net/article/58229.htm

http://www.tuicool.com/articles/QfyMna

http://www.tuicool.com/articles/fqiI73M

相关文章

  • AngularJS Dropdown指令相关的总结

    Dropdown组件 自己封装的一个简单的dropdown的组件 在页面中使用 级联事件的响应 相关注意点 指令绑...

  • AngularJS 指令总结

    22、ng-options 使用数组元素填充下拉列表 23、设置为只读 24、ng-selected 属性的表达式...

  • Angular相关指令

    AngularJS 指令 本教程用到的 AngularJS 指令 : 指令 描述 ng-app 定义应...

  • AngularJS 参考手册

    AngularJS 指令 本教程用到的 AngularJS 指令 : 过滤器解析 AngularJs 过滤器。 A...

  • AngularJS指令小结

    刚刚接触AngularJS,总结了一些关于AngularJS的基本指令。 1、 ng-bind-html 类似于h...

  • AngularJS (2)

    AngularJS 指令 AngularJS 指令是扩展的 HTML 属性,带有前缀 ng-。 ng-app 指令...

  • Angular基础学习-01

    本节包括:AngularJS表达式、AngularJS指令、AngularJS模型、AngularJS的Scope...

  • 15. Angular的内置指令(过滤器)

    AngularJS中自定义指令处理 以ng开头的指令都是内置指令。 内置指令是AngularJS已经处理,使用内置...

  • AngularJS 指令

    AngularJS 指令 背景 什么是AngularJS指令? 一、内置指令 二、自定义 概念 模块对象app,提...

  • AngularJS使用

    AngularJS实现MVC AngularJS模块化Module AngularJS指令系统 AngularJS...

网友评论

      本文标题:AngularJS Dropdown指令相关的总结

      本文链接:https://www.haomeiwen.com/subject/kxnirttx.html