AutoComplete应该不是很陌生了,网上也有好多开源的js。今天主要的不是研究Autocomplete这个js的实现。今天主要讲的是将这个js做成一插件。那么今天主要用到的

演示性例子

做好了上面准备工作,那我们来开始我们的代码编写。

   1:  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   2:  <html xmlns="http://www.w3.org/1999/xhtml">
   3:  <head>
   4:      <title></title>
   5:      <script src="../Scripts/jquery-1.9.1.js" type="text/javascript"></script>
   6:      <!-- 这是从jquery-UI-官网上下载的-->
   7:      <script src="../Scripts/jquery-ui.custom.js" type="text/javascript"></script>
   8:      <link href="../Styles/jquery-ui.css" rel="stylesheet" type="text/css" />
   9:      <script type="text/javascript">
  10:          $(function () {
  11:              var arry = ["cnblogs", "博客园", "hankskfc"];
  12:              $("#Text1").autocomplete({
  13:                  source: arry
  14:              });
  15:          });
  16:      </script>
  17:  </head>
  18:  <body>
  19:      <input id="Text1" type="text" />
  20:  </body>
  21:  </html>

下面是运行结果图:

相信这个应该看着官网的都可以写出来。或者可以参考院里面的一篇文章:jQuery UI Autocomplete 体验

这个自己作为练习倒是可以玩玩的,真正要用到项目中去总显得有点臃肿或者说让人用的不爽。我们希望在适量的配置下,只需要对“<input id="Text1" type="text" />”这个添加少量“属性”就可以自动具备上述功能。

思考与探索

首先我们从“数据”提供方面思考。在此引用下jQuery UI Autocomplete 体验 下的东西。

jQuery UI Autocomplete主要支持字符串Array、JSON两种数据格式。

普通的Array格式没有什么特殊的,如下:

   1:  ["cnblogs","博客园","囧月"]


对于JSON格式的Array,则要求有:label、value属性,如下:

   1:  [{label: "博客园", value: "cnblogs"}, {label: "囧月", value: "囧月"}] 

其中label属性用于显示在autocomplete弹出菜单,而value属性则是选中后给文本框赋的值。

如果没有指定其中一个属性则用另一个属性替代(即value和label值一样),如下:

   1:  [{label: "cnblogs"}, {label: "囧月"}]
   2:  [{value: "cnblogs"}, {value: "囧月"}]

如果label和value都没有指定,则无法用于autocomplete的提示。

另外需要注意,对于从服务器端输出的JSON的key必须用双引号,如下:

   1:  [{"label": "博客园", "value": "cnblogs"}, {"label": "囧月", "value": "囧月"}]

否则可能会出现parsererror错误。

囧月”博主总结的很好,很强大。好了我们知道了autocomplete的支持数据类型,联想下实际项目中一般以List<T>形式得到数据库搜索出来的数据或者其他集合形式这里不一一列举了。

那么这里就有个问题怎么样才可以准换成它支持的Json格式呢,你可以每次都自己手动转。这样会用会让人很不爽,有种砸键盘的感觉。想必大家用过mvc这一类框架,里面Model层的话都会在列上打上一个“特定的标签”-Attribute,这里就引出了另一个技术“反射”。那么就呈上源码:

Attribute

   1:   public class AutoCompleteCustomAttribute : Attribute
   2:      {
   3:          public string ColName { get; private set; }
   4:          public AutoCompleteCustomAttribute(string colName)
   5:          {
   6:              ColName = colName;
   7:          }
   8:      }

   1:  public class AutoCompleteLabelAttribute : Attribute
   2:      {
   3:      }

   1:   public class AutoCompleteValueAttribute : Attribute
   2:   {
   3:   }

反射实现ToJson

   1:  public static class ConvertLib
   2:      {
   3:          public static string ConvertToAutoJson<T>(IList<T> instance) where T : new()
   4:          {
   5:              const string result = "[{0}]";
   6:              var type = typeof(T);
   7:              var sb = new StringBuilder();
   8:              foreach (var ins in instance)
   9:              {
  10:                  var label = string.Empty;
  11:                  var value = string.Empty;
  12:                  var custom = string.Empty;
  13:                  sb.Append("{");
  14:                  #region Body
  15:                  foreach (var prop in type.GetProperties())
  16:                  {
  17:                      var attrLabels = prop.GetCustomAttributes(typeof(AutoCompleteLabelAttribute), true);
  18:                      var hasLabel = attrLabels.Length > 0;
  19:                      if (hasLabel)
  20:                      {
  21:                          label = prop.GetValue(ins, null).ToString();
  22:                      }
  23:   
  24:                      var attrValues = prop.GetCustomAttributes(typeof(AutoCompleteValueAttribute), true);
  25:                      var hasValue = attrValues.Length > 0;
  26:                      if (hasValue)
  27:                      {
  28:                          value = prop.GetValue(ins, null).ToString();
  29:                      }
  30:   
  31:                      var attrCustoms = prop.GetCustomAttributes(typeof(AutoCompleteCustomAttribute), true);
  32:                      var hasCustom = attrCustoms.Length > 0;
  33:                      if (!hasCustom) continue;
  34:                      var attr = attrCustoms[0] as AutoCompleteCustomAttribute;
  35:                      if (attr != null)
  36:                      {
  37:                          custom += "\""+attr.ColName + "\":\"" + prop.GetValue(ins, null) + "\",";
  38:                      }
  39:                  }
  40:                  sb.Append("\"label\":\"" + label + "\",");
  41:                  sb.Append("\"value\":\"" + value + "\"");
  42:                  if (!string.IsNullOrEmpty(custom))
  43:                  {
  44:                      custom = custom.Remove(custom.Length - 1);
  45:                      sb.Append(", " + custom + "");
  46:                  } 
  47:                  #endregion
  48:                  sb.Append("},");
  49:              }
  50:              sb = sb.Remove(sb.Length - 1, 1);
  51:              return string.Format(result, sb);
  52:          }
  53:      }

主要思想:遍历集合的每一个T类型的属性,然后看看每个属性是否包含我们设定的“三个特性”,有则按照格式拼接字符串。

(注意:AutoCompleteLabelAttribute,AutoCompleteValueAttribute按照要的json格式只能出现一次,然而AutoCompleteCustomAttribute可以出现多次。还有要注意的是引号问题即40,41,37行,为此我花费了一天查问题郁闷呢!!)

   1:  //我们就可以这样讲一个集合格式化成我们要的Json字符串。
   2:  //妈妈再也不用担心转Json了~~
   3:  var json = ConvertLib.ConvertToAutoJson(T);

问题二

上文中调用转Json中有个问题:你必须知道要转类型即上文的”T”。由于不知道T,我们要么建立好多个一般处理文件,每个一般处理文件都是向上文一样调用。这又让我们用起来不爽了,凭什么每次要对某个业务自动提示就创建一个一般处理程序呢?很不爽呢~~。

那么我们只好在想一些其他的办法,仔细想想MVC Route的添加。都是在一个类似Global.asax文件添加路径,能不能就这个提供一种思路呢?

无论创建多少个一般处理程序,代码结构基本相似,就缺个知道要处理关于那个业务的搜索。那我们可以写个容器来管理我们要求的业务和执行代码的映射,我们只需要在开始开启网站的时候注册下(Global.asax)。

代码如下:

interface

   1:  public interface IHandleAutoJson
   2:      {
   3:          string GetAutoJson(string term);
   4:      }
 

容器类

   1:   public class AutoCompleteConfig
   2:      {
   3:          /// <summary>
   4:          /// 用来保存配置文件的数据字典eg:["nickname":"product"]
   5:          /// </summary>
   6:          private static readonly Dictionary<string, Type> AutoCompleteDict = new Dictionary<string, Type>();
   7:   
   8:          private static readonly object LockObj = new object();
   9:   
  10:          public static Type GetHandler(string nickName)
  11:          {
  12:              Type typ = null;
  13:              if (AutoCompleteDict.ContainsKey(nickName))
  14:              {
  15:                  typ = AutoCompleteDict[nickName];
  16:              }
  17:              return typ;
  18:          }
  19:   
  20:          public static void AddAutoCompleteHandler(string nickName, Type typ)
  21:          {
  22:              lock (LockObj)
  23:              {
  24:                  if (AutoCompleteDict.ContainsKey(nickName))
  25:                  {
  26:                      AutoCompleteDict[nickName] = typ;
  27:                  }
  28:                  else
  29:                  {
  30:                      AutoCompleteDict.Add(nickName, typ);
  31:                  }
  32:              }
  33:   
  34:          }
  35:   
  36:          public static void RemoveAutoCompleteHandler(string nickName)
  37:          {
  38:              lock (LockObj)
  39:              {
  40:                  AutoCompleteDict.Remove(nickName);
  41:              }
  42:          }
  43:      }

代码很简单,就是对容器的Add,Remove,Select操作进行简单的封装。

然后是注册代码:

   1:  AutoCompleteConfig.AddAutoCompleteHandler("product", typeof(AutoCompleteProduct));

然后我们的只要建立一个一般处理程序,要让它和AutoCompleteConfig发生关系只需要一个工厂就行。

   1:   public class AutoCompleteFactory
   2:      {
   3:          public static IHandleAutoJson GetHandler(string nickName)
   4:          {
   5:              IHandleAutoJson json = null;
   6:              var typ = AutoCompleteConfig.GetHandler(nickName);
   7:              if (typ != null && !typ.IsAbstract && typeof(IHandleAutoJson).IsAssignableFrom(typ))
   8:              {
   9:                  var obj = Activator.CreateInstance(typ) as IHandleAutoJson;
  10:                  json = obj;
  11:              }
  12:              return json;
  13:          }
  14:      }

这样我们的调用形式就变成了:

   1:   var handler = AutoCompleteFactory.GetHandler(nickName);//这里是业务对应别名,也是唯一的。
   2:   if (handler!=null)
   3:   {
   4:        json = handler.GetAutoJson(term);
   5:   }

到这里数据提供应该算差不多了,接下来就应该是js块了。

Js调用形式思考

我们希望的形式只是在“<input id="Text1" type="text" />”添加少量的属性,那这个该如何实现呢。

我们需要哪些属性呢?马上想到的是nickName,因为上文中提到是对应于某个业务的。还有没有呢,还缺个class=“autoC”(这里的可以随便取)。

然后我们只需要:

   1:  $(".autoC").each(function () {.....});

将每个需要的text都绑定autocomplete就可以了。

下面就是js代码:

   1:  //用于动态加载css和javascript
   2:  $.extend({
   3:      includePath: '',
   4:      include: function (file) {
   5:          var files = typeof file == "string" ? [file] : file;
   6:          for (var i = 0; i < files.length; i++) {
   7:              var name = files[i].replace(/^\s|\s$/g, "");
   8:              var att = name.split('.');
   9:              var ext = att[att.length - 1].toLowerCase();
  10:              var isCSS = ext == "css";
  11:              var tag = isCSS ? "link" : "script";
  12:              var attr = isCSS ? " type='text/css' rel='stylesheet' " : " language='javascript' type='text/javascript' ";
  13:              var link = (isCSS ? "href" : "src") + "='" + $.includePath + name + "'";
  14:              if ($(tag + "[" + link + "]").length == 0) $(document.head).append("<" + tag + attr + link + "></" + tag + ">");
  15:          }
  16:      }
  17:  });

用于window onload时候动态绑定每一“<input id="Text1" type="text" />”

   1:  //需要在window.onload时候绑定
   2:  //每个要添加 textbox需要添加属性 class="autoC" nickName="product"
   3:  var BindAutoComplete = (function () {
   4:      var scriptPath = JsConfig.GetConfig("baseUrl") + "Scripts/";
   5:      var stylePath = JsConfig.GetConfig("baseUrl") + "Styles/";
   6:      var handlePath = JsConfig.GetConfig("baseUrl") + "Handle/";
   7:      var bindData = function () {
   8:          $.include([scriptPath + 'jquery-ui.custom.js', stylePath + 'jquery-ui.css']);
   9:   
  10:          $(".autoC").each(function () {
  11:              var nickName = $(this).attr("nickName");
  12:              var url = handlePath + 'AutoComplete.ashx?nick=' + nickName;
  13:              $(this).autocomplete({
  14:                  source: url,
  15:                  minLength: 1,
  16:                  select: function (event, ui) {
  17:                      this.value = ui.item.value;
  18:                  }
  19:              });
  20:          });
  21:      };
  22:   
  23:      return {
  24:          BindData: bindData
  25:      };
  26:  })();

Jsconfig:主要发布到IIS上可能有虚拟路径问题,从而添加了这个。

   1:  //JS配置全局静态类
   2:  var JsConfig = (function () {
   3:      var constants = {
   4:          baseUrl: "http://localhost:3694/"
   5:      };
   6:      var returnObj = {
   7:          GetConfig: function (name) {
   8:              return constants[name];
   9:          }
  10:      };
  11:      return returnObj;
  12:  })();

页面调用

   1:  <script src="../Scripts/jquery-1.9.1.js" type="text/javascript"></script>
   2:  <script src="../JsConfig.js" type="text/javascript"></script>
   3:  <script src="../Scripts/AddScript.js" type="text/javascript"></script>
   4:  <script src="../Scripts/AutoComplete/BindAutoComplete.js" type="text/javascript"></script>
   5:  <script type="text/javascript">
   6:     $(function () {
   7:           BindAutoComplete.BindData();
   8:      });
   9:  </script>

好了一个Autocomplete 插件做好了。

源码整理:

关于AutoComplete整合的更多相关文章

  1. sublime 使用总结

    不管你用什么编辑,sublime是首选编辑器,就是sublime淘汰,但已成为标准.例如:atom,几乎等同于sublime,及其他可以几乎调成到sublime操作方式. 一.常用插件 插件搜索地址: ...

  2. Spring3.0 与 MyBatis框架 整合小实例

    本文将在Eclipse开发环境下,采用Spring MVC + Spring + MyBatis + Maven + Log4J 框架搭建一个Java web 项目. 1. 环境准备: 1.1 创建数 ...

  3. discuz论坛与其它网站登录注册整合

    discuz论坛与其它网站登录注册整合 本文以discuz 7.0.0 php版本的论坛与 .net 2.0的网站注册登录整合为类.没有采用uc_center或第三方插件.以另类的方式实现.此方法实现 ...

  4. layui中使用autocomplete.js

    前言 在网站找了一大圈都是问题没有答案,记录记录谨防踩坑 layui版本:layui-v1.0.9_rls a(https://github.com/devbridge/jQuery-Autocomp ...

  5. java ee,包括js,html,jsp等等知识整合

    为了方便修改和后续的包装套路   首先用户访问的页面从web.xml找到 <welcome-file-list> <welcome-file>index.html</we ...

  6. (jQuery插件)autocomplete插件的简单例子

    1.引入相应的js和css,我用到的时候是在jquery-ui的js里面整合的,ui的css 2.先在html上写一个input <input id="tags" class ...

  7. Javascript - ExtJs - 整合百度文章编辑器

    ExtJs - 整合百度文章编辑器(ExtJs UEditor) 第一步:去官网下载最新版本的UEditor,UEditor下载. 第二步:在编辑器根目录创建一个Extjs-Editor.js,录入以 ...

  8. springboot配置server相关配置&整合模板引擎Freemarker、thymeleaf&thymeleaf基本用法&thymeleaf 获取项目路径 contextPath 与取session中信息

    1.Springboot配置server相关配置(包括默认tomcat的相关配置) 下面的配置也都是模板,需要的时候在application.properties配置即可 ############## ...

  9. Ogre 编辑器一(MyGUI+Ogre整合与主界面)

    在查看Ogre例子时,想看材质要里的纹理,着色器代码都需要每个去查找,非常麻烦.也想看更新每个Ogre里的对象后有什么效果.然后看到Compositor组件与粒子组件时,想到能实时编辑着色器代码实时更 ...

随机推荐

  1. 《C++ primer》--第12章

    习题12.7 什么是封装?为什么封装是有用的? 解答: 封装是一种将低层次的元素组合起来形成新的.高层次实体的技术.例如,函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中:类 ...

  2. NumberSeekBar 可任意拖动和自动

    package com.example.numberseekbar; import android.content.Context; import android.content.res.Resour ...

  3. 《Python 学习手册4th》 第十五章 文档

    ''' 时间: 9月5日 - 9月30日 要求: 1. 书本内容总结归纳,整理在博客园笔记上传 2. 完成所有课后习题 注:“#” 后加的是备注内容 (每天看42页内容,可以保证月底看完此书) “重点 ...

  4. C++程序设计之结构体,共用体,枚举和typedef

    [1]结构体的基本功 注意结构体里面可以有很多东西,可以结构体里面包含结构体 #include<iostream> using namespace std; struct Date { i ...

  5. PHP 调用外部程序的几种方式

    /* php 调用python 的代码 // 第一种: // echo passthru('C:/Python34/PY.exe D:/do.py'); // 第二种: // echo exec('C ...

  6. Polymer——Template

    Polymer Template 一.Ta的简介 template是polymer element中一个重要的组成部分,主要有两种使用目的,一是构建Shadow Dom,二是用于数据绑定和视图渲染. ...

  7. Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)

    [题目链接] http://www.tsinsen.com/A1219 [题意] 给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u- ...

  8. 二分+最短路 uvalive 3270 Simplified GSM Network(推荐)

    // 二分+最短路 uvalive 3270 Simplified GSM Network(推荐) // 题意:已知B(1≤B≤50)个信号站和C(1≤C≤50)座城市的坐标,坐标的绝对值不大于100 ...

  9. 读《编写高质量代码-Web前端开发修炼之道》笔记

    第一章 1.Web标准由一系列标准组合而成,核心理念是将网页的结构,样式和行为分离,所以分为三大部分:结构标准,样式标准和行为标准.结构标准包括XML标准,XHTML标准,HTML标准:样式标准指CS ...

  10. Nuttx操作系统

    前几天答辩的时候看到有同学在用,回来后查了点资料. 来源:天又亮了 1  NuttX 实时操作系统 NuttX 是一个实时操作系统(RTOS),强调标准兼容和小型封装,具有从8位到32位微控制器环境的 ...