zoukankan      html  css  js  c++  java
  • AngularJS中实现下拉搜索框

    先上效果,带有搜索功能:

    image_1bvhghr9g1pe55u0eh583fmdg29.png-10.8kB

    前端JS框架使用的是AngularJS.

    一、从后台获取树形数据

    获取到的树形结构是这个样子的,数组对象:

    image_1bvhgmd6u1f4l1nd66t31bo416e09.png-103.3kB

    至于后台如何达到此效果,详情见:
    Java递归获取树父节点下的所有树子节点

    二、前端JS与html

    • js中获取到树形数据
    /**
     * 文件名:organizationcontroller.js
     * 路径:项目名/WebContent/xxx/scripts/controllers/platform/organizationcontroller.js
     */
    
    //获取区域树
    $scope.getDivisionTree=function () {
        divisionInfoService.getDivisionInfoTree().then(function (data) {
            if (data && data.statusCode == 200) {
                $scope.divisionTree = [{
                    code: -1,
                    name: '全国',
                    children: data.result
                }];
                $scope.division = $scope.division || -1;
            }
        }, function (error) {
            tipService.popup.error(error);
        });
    };
    
    /*页面初始化*/
    $scope.getDivisionTree();
    
    • html页面中使用数据
    <!--
        文件名:organizationform.html
        路径:项目名/WebContent/xxx/views/platform/projectsetting/organizationform.html
    -->
    
    <label>所在区域</label>
    <div>
        <search-box search-data="divisionTree" ng-search-model="organization.divisionCode" search-change="divisionChange(item)" search-value-text="code" search-label-name="name" ng-required="true" can-choose-parent="true">
        </search-box>
    </div>
    

    那么这个<search-box>标签是什么玩意?
    标签里面的参数又是什么意思?是通过什么定义的?请继续往后看,在四、指令详细介绍。

    先来简单说一下<search-box>标签中的各个属性是什么意思:
    search-data 表示数据源。在一、从后台获取树形数据中,不是从后台查到了整个树形结构嘛,然后在js中获取到树形数据中,使用$scope.divisionTree这个变量接收了数据源,因此,html中的search-data,对应的就是divisionTree

    ng-search-model AngularJS的特色之一,数据双向绑定。将当前的数据,与scope中的数据进行绑定。简单来说,只要页面的内容发生变化,那么对应的js中的变量值也就发生变化。在这里,ng-search-model的值是:organization.divisionCode; 但为什么model是code? 获取到的属性,除了code,还有idshortCodename等等,为什么这里的model偏偏是code?

    search-value-text 那是因为:search-value-text="code";

    search-label-name 同理,为什么效果图上,显示的是name? 因为:search-label-name="name";

    ng-required="true" 必选;

    can-choose-parent="true" 可以选择父节点;

    search-change="divisionChange(item)" 当值产生变动。

    三、树形结构的html与css

    <!--
        文件名:searchbox.html
        路径:项目名/WebContent/xxx/scripts/directives/searchbox/searchbox.html
    -->
    
    <div class="search-box-main">
        <script type="text/ng-template" id="items_search.html">
            <div ui-tree-handle>
                <div class="tree-node-content" ng-click="node(this,item,$event)">
                    <a class="btn handletools expand" data-nodrag ng-click="toggle(this)">
                        <span class="fa fa-fw" ng-class="{'fa-plus-square-o': collapsed, 'fa-minus-square-o': !collapsed}" ng-show="item.children.length"></span>
                    </a>
                    <span class="itemTitle">{{item[ngLabelName]}} </span>
                </div>
                <ol ui-tree-nodes="options" ng-model="item.children" ng-class="{hidden: collapsed}">
                    <li ng-repeat="item in item.children | filter:ngText" class="ui-tree-child" ui-tree-node collapsed="true" ng-include="'items_search.html'" ng-show="visible(item)" data-nodrag="true">
                    </li>
                </ol>
            </div>
        </script>
        <div class="search-box-input-context clearfix">
            <div class="search-icon search-down"><i class=" glyphicon glyphicon-chevron-down"></i></div>
            <div class="search-box-input"><input type="text" readonly class="search-box-input-text" ng-model="ngTempText" name="code" ng-disabled="ngDisable"/></div>
        </div>
        <div class="search-box-pop">
            <div class="search-box-input-context clearfix" ng-show="list&&list.length>0">
                <div class="search-icon search-clear"><i class=" glyphicon glyphicon-remove"></i></div>
                <div class="search-box-input"><input class="search-box-input-text" ng-model="ngText"/></div>
            </div>
            <div class="search-tree">
                <div class="search-tree-content">
                    <div ui-tree="options">
                        <ol ui-tree-nodes data-ng-model="list" id="tree">
                            <li ng-repeat="item in list  | filter:ngText" class="ui-parent" ui-tree-node ng-include="'items_search.html'" ng-show="visible(item)" data-nodrag="true" >
                            </li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
        <div class="error" ng-show="ngRequired&&!ngTempText">
            <small class="error" ng-show="ngRequired&&!ngTempText">该项必填</small>
        </div>
    </div>
    
    
    /*
        文件名:searchbox.css
        路径:项目名/WebContent/xxx/scripts/directives/searchbox/searchbox.css
    */
    
    .search-box-main {
        position: relative;
    }
    
    .search-box-main .search-box-input-context {
        overflow: hidden;
        height: 30px;
        border: 1px solid #ccc;
        -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0) !important;
        -moz-box-shadow: 0 0 0 rgba(0, 0, 0, 0) !important;
        box-shadow: 0 0 0 rgba(0, 0, 0, 0) !important;
    }
    
    .search-box-main .search-box-input-context .search-icon {
        float: right;
         20px;
        height: 100%;
        line-height: 34px;
    }
    
    .search-box-main .search-box-input-context .search-icon:hover {
        opacity: 0.5;
    }
    
    .search-box-main .search-box-input-context .search-box-input {
        overflow: hidden;
    }
    
    .search-box-main .search-box-input-context .search-box-input .search-box-input-text {
         100%;
        display: inline-block;
        height: 20px;
        line-height: 20px;
        margin: 5px 0;
        padding: 0 0 0 5px;
        border: none;
        outline: none;
    }
    
    .search-box-main.active .search-box-input-context, .search-box-main.focus .search-box-input-context {
        outline: 0;
        border: 1px solid #66afe9;
        -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
        box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
    }
    
    .search-box-pop {
        display: none;
        position: absolute;
         100%;
        overflow: hidden;
        z-index: 999999;
        padding-top: 40px;
    }
    
    .search-box-main.active .search-box-pop {
        display: block;
        border: 1px solid #66afe9;
        background: #fff;
    }
    
    .search-box-main .angular-ui-tree-handle {
        border: none;
        cursor: pointer;
        padding: 0;
    }
    
    .search-box-main .angular-ui-tree-handle .handletools.expand {
        padding: 0;
        position: static;
        vertical-align: middle;
    }
    
    .search-box-main .angular-ui-tree-handle .itemTitle {
        vertical-align: middle;
    
        white-space: nowrap;
    }
    
    .search-box-main .search-box-input-context .search-clear .glyphicon {
        display: none;
    }
    
    .search-box-main .search-box-input-context:hover .search-clear .glyphicon {
        display: block;
        vertical-align: middle;
        line-height: 30px;
        text-align: center;
    }
    
    .search-box-main.search-disabled .search-box-input-context:hover .search-clear {
        display: none;
    }
    
    .search-box-main.search-disabled .search-box-input-context .search-icon:hover {
        opacity: 1;
    }
    
    .search-box-main.search-disabled .search-box-input-context .search-box-input .search-box-input-text {
        background-color: #eee;
    }
    
    .search-box-main.search-disabled .search-box-input-context {
        background: #EEEEEE;
    }
    
    .search-box-main .angular-ui-tree-empty {
        background: #66AFE9;
        border: none;
        min-height: 15px;
    }
    
    .search-box-pop > .search-box-input-context {
        margin: 5px;
        position: absolute;
        top: 0;
        left: 0;
        z-index: 2;
    }
    
    .search-box-pop > .search-tree {
        height: 100%;
        overflow: auto;
    }
    
    .search-box-pop > .search-tree .search-tree-content li {
        word-break: keep-all;
        word-wrap: normal;
    }
    
    .search-box-pop .tree-node-content {
        white-space: nowrap;
        text-align: left;
    }
    
    .search-box-pop .angular-ui-tree-handle {
        overflow: visible;
    }
    
    #dataCentersTree .search-box-height {
        height: 200px;
    }
    
    #dataCentersTree #options {
        overflow-y: auto;
        height: 195px;
    }
    
    #dataCentersTree .search-box-pop {
        padding-top: 5px;
    }
    
    #dataCentersTree .retract {
        padding-left: 20px;
    }
    
    #dataCentersTree .handletools {
        padding: 2px 4px;
    }
    

    四、指令

    /**
     * 文件名:searchbox.js
     * 路径:项目名/WebContent/xxx/scripts/directives/searchbox/searchbox.js
     */
    
    /*
     * <search-box ng-search-model="inspectionKnowledgeBase.eqTypeCode" search-change="fn()" search-value-text="typeCode" search-label-name="typeName" search-data="service.equipmentTypes" name="code" required></search-box>
    
     * search-data:当前数据源;
     * search-label-name:当前下拉列表显示名称(在数据源中值的名称),默认情况下为name;
     * search-value-text:当前下拉列表返回的值的名称(在数据源中值的名称),默认情况下为空;
     * ng-search-model:数据绑定对象,当search-value-text为空时,返回当前model在数据源中的对象;
     * can-choose-parent="true":是否可以点击父节点,默认为false。
     * ng-required='true';是否必填;
     * search-change="fn()";内容去选择后的回调
     * */
     
    cooleadCloud.directive('searchBox', ['$templateCache', '$http', '$compile', '$window', '$rootScope', '$timeout', '$safeapply', '$cookieStore',
        function ($templateCache, $http, $compile, $window, $rootScope, $timeout, $safeapply, $cookieStore) {
            return {
                restrict: 'AE',
                templateUrl: 'scripts/directives/searchbox/searchbox.html',
                transclude: true,
                scope: {
                    ngSearchData: "=searchData",
                    ngSearchModel: "=ngSearchModel",
                    ngLabelName: "@searchLabelName",
                    ngValueText: "@searchValueText",
                    ngCanChooseParent: "=canChooseParent",
                    ngChange: "&searchChange",
                    ngRequired: "=ngRequired",
                    ngDisable: "=ngDisabled",
                    ngEmpty: "=ngNotEmpty"
                },
                replace: false,
                link: function ($scope, element, attrs) {
                    $scope.ngTempText = "";
                    $scope.ngEmpty = false;
                    $scope.isChoosed = true;
                    $scope.ngLabelName = $scope.ngLabelName || 'name';
                    var valText = angular.copy($scope.ngValueText), tempText = angular.copy($scope.ngLabelName);
                    var validateName = function (item, name) {
                        var te = null;
                        if (item) {
                            if (item.hasOwnProperty(name)) {
                                te = item[name];
                            } else {
                                te = item;
                            }
                        }
                        return te;
                    }
                    var iteatorValue = function (items, nv) {
                        var item;
    
                        if (items) {
                            for (var i = 0; i < items.length; i++) {
                                item = items[i];
                                if (nv == validateName(item, valText)) {
                                    var text = validateName(item, tempText);
                                    $scope.ngTempText = text;
                                    if (text) {
                                        $scope.ngEmpty = true;
                                    }
                                } else {
                                    if (item && item.hasOwnProperty('children')) {
                                        var childrens = item.children;
                                        if (childrens && childrens.length > 0) {
                                            iteatorValue(childrens, nv);
                                        }
                                    }
                                }
                            }
                        }
                        return;
                    }
    
                    function filterData(nv) {
                        var tempList = angular.copy($scope.list);
                        if (tempList) {
                            var nItem;
                            for (var i = 0; i < tempList.length; i++) {
                                nItem = tempList[i];
                                if (nv == validateName(nItem, valText)) {
                                    var text = validateName(nItem, tempText);
                                    $scope.ngTempText = text;
                                    if (text) {
                                        $scope.ngEmpty = true;
                                    }
                                } else {
                                    if (nItem && nItem.hasOwnProperty('children')) {
                                        iteatorValue(nItem.children, nv);
                                    }
                                }
                            }
                        }
    
                    }
    
                    $scope.$watch("ngText", function (nv, ov) {
                        if (typeof nv === "undefined") {
                            return;
                        }
                        $("#tree").hide();
                        var li = $("#tree  li");
                        angular.forEach(li, function (item) {
                            var span = $(item).find(".itemTitle");
                            for (var i = 0; i < span.length; i++) {
                                if ($(span[i]).text().indexOf(nv) !== -1) {
                                    $(item).show();
                                    break;
                                } else {
                                    $(item).hide();
                                }
                            }
                        });
                        $("#tree").show();
                    });
    
                    if ($scope.ngSearchData) {
                        $scope.list = angular.copy($scope.ngSearchData);
                    }
                    /**
                     * add by likw 2016-05-31
                     *  监听数据源改变。
                     */
                    $scope.$watch("ngSearchData", function (nv, ov) {
                    	$scope.ngTempText = "";
                    	$scope.list = angular.copy(nv);
                    });
       
                    var ngIndex = 0;
                    $scope.$watch('ngSearchData', function (nV, oV) {
                        if ((nV && nV.length > 0) && !ngIndex) {
                            ngIndex++;
                            $scope.list = angular.copy(nV);
                            filterData($scope.ngSearchModel);
                        }
                    });
                    var $element = $(element);
                    var $main = $('.search-box-main', $element);
                    var $searchIcon = $(".search-down", $element);
                    var $searchClear = $(".search-clear", $element);
                    var $searchInput = $(".search-box-input-text", $element);
                    $scope.$watch('ngSearchModel', function (nV, oV) {
                        if (!nV) {
                            $scope.ngTempText = "";
                            return;
                        }
                        if (nV != oV) {
                            $scope.ngTempText = "";
                            filterData(nV);
                        }
                    });
    
    
                    if ($scope.ngDisable) {
                        $main.addClass('search-disabled');
                    }
    
                    /*点击清空*/
                    $searchClear.off('click').on('click', function () {
                        var closest = $(this).closest('.search-box-main');
                        if (closest.hasClass('search-disabled')) {
                            return;
                        }
                        $safeapply($scope, function () {
                            $scope.ngText = '';
                        });
    
                    });
                    /*点击下拉事件*/
                    $searchIcon.off('click').on('click', function () {
                        var closest = $(this).closest('.search-box-main');
                        if (closest.hasClass('search-disabled')) {
                            return;
                        }
                        $main.addClass('active');
                        var $searchPop = closest.children('.search-box-pop').eq(0);
                        var $treeCon = $searchPop.children('.search-tree').eq(0);
                        var h = $treeCon.outerHeight(true);
                        if (h > 237) {
                            $searchPop.height(200);
                        }
                    });
                    $element.isSetHeight = false;
                    $searchInput.off('click').on('click', function () {
    
                        var closest = $(this).closest('.search-box-main');
                        if (closest.hasClass('search-disabled')) {
                            return;
                        }
    
                        $safeapply($scope, function () {
                            $scope.ngText = '';
                        });
                        $main.addClass('active');
                        var $searchPop = closest.children('.search-box-pop').eq(0);
                        var $treeCon = $searchPop.children('.search-tree').eq(0);
                        var h = $treeCon.outerHeight(true);
                        if (h > 237) {
                            $searchPop.height(200);
                            $element.isSetHeight = true;
                        }
                    });
    
                    /*非当前区域点击事件*/
                    $('body').on('click', function (e) {
                        var $target = $(e.target);
                        var $pTarget = $target.closest('.search-box-main');
                        if (!$pTarget[0]) {
                            $main.removeClass('active');
                        }
                    })
                    /*树状结构 开始*/
                    $scope.visible = function (item) {
                        return !($scope.query && $scope.query.length > 0
                        && item.title.indexOf($scope.query) == -1);
                    };
                    $scope.toggle = function (scope) {
                        scope.toggle();
                    };
                    $scope.node = function (scope, item, $event) {
                        var isChoose = false;
                        $scope.isChoosed = true;
                        if ($scope.ngCanChooseParent) {
                            isChoose = true;
                        } else {
                            isChoose = !scope.hasChild()
                        }
                        if (isChoose && !angular.element($event.target).hasClass("fa")) {
                            $scope.ngSearchModel = validateName(item, valText);
                            $scope.ngTempText = validateName(item, tempText);
                            $main.removeClass('active');
                            $scope.ngChange({
                                item: item
                            });
                            $scope.isChoosed = false;
    
                            if ($scope.ngTempText) {
                                $scope.ngEmpty = true;
                            }
                            //是项目,项目树修改时,设置到cookies
                            if (item.shortCode && item.fullCode && item.divisionCode) {
                                $cookieStore.put('defaultProject', item);
                            }
                        }
    
                        var $target = $($event.target).closest(".expand");
                        if (!$element.isSetHeight && !isChoose && !scope.collapsed && $target && $target[0]) {
                            var $searchPop = $target.closest('.search-box-pop').eq(0);
                            var $treeCon = $searchPop.children('.search-tree').eq(0);
                            setTimeout(function () {
                                var h = $treeCon.outerHeight(true);
                                if (h > 237) {
                                    $searchPop.height(200);
                                    $element.isSetHeight = true;
                                }
                            }, 500);
                        }
                    };
                    /*树状结构 结束*/
                }
            };
        }]);
    

    最后,别忘了页面中引入相关的js和css.

  • 相关阅读:
    手写简易SpringMVC框架,包含@PathVariable
    高并发下,如何保证接口的幂等性?
    JAVA判断奇偶数
    多线程ForkJoin-分治思想
    websocket简单使用
    Git使用教程:最详细、最傻瓜、最浅显、真正手把手教!(转载学习)
    linux配置java环境变量(详细)
    java缓存技术的介绍(转载)
    java 多态性详解及常见面试题
    oracle数据库基础知识总结(一)
  • 原文地址:https://www.cnblogs.com/VitoYi/p/7880549.html
Copyright © 2011-2022 走看看