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. MyBatis association的两种形式——MyBatis学习笔记之四

    一.嵌套的resultMap 这 种方法本质上就是上篇博文介绍的方法,只是把教师实体映射从association元素中提取出来,用一个resultMap元素表示.然后 association元素再引用 ...

  2. VelocityTracker简单用法

    VelocityTracker顾名思义即速度跟踪,在android中主要应用于touch event, VelocityTracker通过跟踪一连串事件实时计算出 当前的速度,这样的用法在androi ...

  3. 指针和引用的比较(P105)

    指针和引用的比较? 虽然使用引用和指针都可间接访问另一个值,但它们之间有两个重要区别. 第一个区别在于引用总是指向某个对象:定义引用时没有初始化是错误的. 第二个重要区别则是赋值行为的差异:给引用赋值 ...

  4. (转)我是如何在SQLServer中处理每天四亿三千万记录的

    首先声明,我只是个程序员,不是专业的DBA,以下这篇文章是从一个问题的解决过程去写的,而不是一开始就给大家一个正确的结果,如果文中有不对的地方,请各位数据库大牛给予指正,以便我能够更好的处理此次业务. ...

  5. 取消本地SVN文件夹与服务器关联

    问题:之前建了一个SVN代码库,同步了代码上去,但中途发现建库时的规则搞错了,就把服务器上的库给删了重建,然后改变本地代码的svn服务器关联地址,但使用Relocate一直报错. 错误有两种情况:1. ...

  6. 32位和64位dll判断

    如何判断一个dll文件是32位还是64位? 1. 开发中经常会使用到VC的一个工具 Dependency Walker用depends.exe打开dll,文件名前有64标示的即为64位. 但是这个方式 ...

  7. jar,war,ear区别及java基础杂七八

    jar,war,earqu区别 这三种文件都可以看作是java的压缩格式,其实质是实现了不同的封装: jar--封装类war--封装web站点ear--封装ejb.它们的关系具体为:jar:      ...

  8. HTML5每日一练之details展开收缩标签的应用

    details标签的出现,为我们带来了更好的用户体验,不必为这种收缩展开的效果再编写JS来实现.注:目前仅Chrome支持此标签. details有一个新增加的子标签——summary,当鼠标点击su ...

  9. crontab 定时任务格式

    如下内容节选自<Linux Crontab 定时任务 命令详解> 用crontab -e 添加要执行的命令 添加的命令必须以如下格式: * * * * * /command path 前五 ...

  10. HDU 5753 Permutation Bo (推导 or 打表找规律)

    Permutation Bo 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5753 Description There are two sequen ...