组件组成:HTML模板 + JS文件
组件原理:HTML模板就是规定组件由哪些元素构成,这些元素是什么结构的,既组件是长啥样的;JS文件是用来声明和定义组件功能的,相当于JSP文件中<script>标签中JS代码的功能;
联想搜索:用户在输入框中输入关键字,并自动向后端发送异步请求,得到请求回来的数据后,以列表的形式展示在搜索框下方,用户通过鼠标悬停或者上下键选择选项,鼠标点击或者Enter键实现搜索并列表消失的功能,功能如下图:
代码部分:
- HTML模板:
1 <span class="relative"> 2 <input type="text" placeholder="请输入关键字..." ng-model="inputModel" ng-change="method.timeflash()" ng-blur="method.closeSearch()" ng-focus="method.searchData()" ng-keydown="method.keyDownEvent($event)"> 3 <i class="iconfont searchBtn"></i> 4 <span class="search-list" ng-show="vm.dataList.length > 0" id="forhide"> 5 <a href="javascript:void(0)" ng-repeat="data in vm.dataList | limitTo:10" 6 ng-mouseover="method.mouseOverEvent($index)" ng-click="method.enterKeyEvent(data)">{{data.value}}</a> 7 </span> 8 </span>
注解:输入框对象$scope.inputModel用于保存输入的关键字,输入框提供change、blur、focus、keydown事件的监听。
change事件:当输入框内容变化时,触发计时器,400毫秒后开始搜索;
blur事件:搜索框失焦时,清空并隐藏数据列表;
focus事件:输入框获得焦点时,直接搜索;
keydown事件:按键监听事件,监听上下键及Enter事件,上下键选择列表中的数据项,Enter键做用户指定操作。
- CSS类:
1 .relative{position: relative!important;} 2 .absolute{position: absolute!important;} 3 4 .search-list { 5 position: relative; 6 width: 100%; 9 background: #fff; 10 border: solid 1px #5093e1; 11 z-index: 10; 12 } 13 14 .search-list a { 15 width: 100%; 16 height: 30px; 17 line-height: 30px; 18 float: left; 19 color: #666; 20 display: block; 21 font-size: 13px; 22 padding: 0 10px; 23 text-decoration: none; 24 } 25 26 .search-list a:hover { 27 color: #666; 28 } 29 30 .search-list .selected { 31 background: #5093e1; 32 color: #fff!important; 33 }
注解:数据列表相对于搜索框相对布局。
- JS文件
1 APP.directive('search', ['Postman','$timeout', function (Postman,$timeout) { 2 return { 3 scope: {// 传入的参数 4 inputModel: '=',// 输入框对象 5 enterKeyEvent: '='// 用户指定操作 6 }, 7 restrict: 'E',// 封装成Element(元素)类型 8 templateUrl: 'directive/search/search.html',// 模板路径 9 replace: false, 10 link: function ($scope) { 11 var timer; 12 var selectListIndex;// 数据列表索引 13 $scope.vm = {};// 模板视图对象 14 $scope.method = {};// 模板方法对象 15 16 function initParams(){// 初始化参数 17 $scope.vm = { 18 selectedData: {},// 选中数据对线 19 dataList: []// 数据列表 20 }; 21 $scope.method.enterKeyEvent = $scope.enterKeyEvent;//将传过来的用户指定的方法赋值给组件 22 } 23 24 function init() {// 程序初始化执行部分 25 initParams(); 26 } 27 28 $scope.method.searchData = function () { // 根据关键字搜索联想词 29 if ($scope.inputModel === undefined) {// 为空时不做搜索 30 return; 31 } 32 if ($scope.inputModel.length > 0) {// 非空时异步请求联想词 33 var promise = Postman.httpGet('data/fuzzy', {// Postman为自己封装的Http请求服务,这里用Get方法请求数据 34 keyWord: $scope.inputModel}); 37 promise.then(function (data) { 38 if (data && data.code === 0) { 39 $scope.vm.dataList = data.data;// 请求回来的数据赋值给视图对象中的dataList数组 40 selectListIndex = -1;// 索引置为-1,不选中任何项 41 $('#forhide').show();// 显示列表 42 } 43 }); 44 } 45 }; 46 47 $scope.method.keyDownEvent = function ($event) { 48 $event.stopPropagation();// 阻止其他按键事件冒泡 49 var listArry = $('#forhide>a'); 50 switch ($event.keyCode) { 51 case 38:// 'UP' 52 selectListIndex--; 53 if (selectListIndex >= 0 && selectListIndex < listArry.length) { 54 for (var i = 0; i < listArry.length; i++) { 55 listArry.eq(i).removeClass('selected');// 清除选中样式 56 if (i === selectListIndex) { 57 listArry.eq(i).addClass('selected');// 添加选中样式 58 $scope.inputModel = listArry.eq(i).text();// 将选中项的文本赋值给输入框 59 } 60 } 61 } else if (selectListIndex < 0) {// 第0项跳转到最后一项 62 selectListIndex = listArry.length - 1; 63 listArry.eq(0).removeClass('selected'); 64 listArry.eq(selectListIndex).addClass('selected'); 65 $scope.inputModel = listArry.eq(selectListIndex).text(); 66 } 67 break; 68 case 40:// 'Down' 69 selectListIndex++; 70 if (selectListIndex >= 0 && selectListIndex < listArry.length) { 71 for (var i = 0; i < listArry.length; i++) { 72 listArry.eq(i).removeClass('selected'); 73 if (i === selectListIndex) { 74 listArry.eq(i).addClass('selected'); 75 $scope.inputModel = listArry.eq(i).text(); 76 } 77 } 78 } else if (selectListIndex >= listArry.length) {// 最后一项跳转到第0项 79 selectListIndex = 0; 80 listArry.eq(listArry.length - 1).removeClass('selected'); 81 listArry.eq(selectListIndex).addClass('selected'); 82 $scope.inputModel = listArry.eq(selectListIndex).text(); 83 } 84 break; 85 case 13:// Enter键触发用户指定操作 86 $scope.method.enterKeyEvent($scope.vm.dataList[selectListIndex]); 87 $scope.method.closeSearch();// 关闭联想词列表 88 break; 89 } 90 }; 91 92 $scope.method.mouseOverEvent = function (index) {// 鼠标悬停事件:获取悬停数据项的索引,并添加选中样式 93 selectListIndex = index; 94 var listArry = $('#forhide>a'); 95 for (var i = 0; i < listArry.length; i++) { 96 listArry.eq(i).removeClass('selected'); 97 if (i === selectListIndex) { 98 listArry.eq(i).addClass('selected'); 99 $scope.inputModel = listArry.eq(i).text(); 100 } 101 } 102 }; 103 104 $scope.method.timeflash = function () {// 定时器控制,400ms后才发送请求 105 $timeout.cancel(timer);// 关闭400ms内的上一个计时器,400ms后认为用户停止输入,即输入完成,开始搜索,从而减少多余的请求次数 106 timer = $timeout(function () { 107 $scope.method.searchData();// 发送搜索请求 108 }, 400); 109 }; 110 111 $scope.method.closeSearch = function () {// 关闭联想词列表 112 $scope.vm.dataList = []; 113 $('#forhide').hide(); 114 }; 115 116 init();// 程序入口
122 } 123 }; 124 }]);
- 使用样例:
1 <search input-model="inputBox" enter-key-event="search"></search>
传递组件需要的两个参数:inputModel和enterKeyEvent。"search"为用户指定的操作、方法。