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. windows各种程序中文显示乱码又找不到原因时

    我电脑上的各种程序,如xshell,Navicat for MySQL都不正常显示中文,该软件的编码,utf-8,gbk,gb2312来回切换好几回,没一次正常,最好解决办法如下       进入控制 ...

  2. 【windows核心编程】IO完成端口(IOCP)复制文件小例

    1.演示内容 文件复制 2.提要 复制大文件时,使用FILE_FLAG_NO_BUFFERING标志 同时需要注意: 读写文件的偏移地址为 磁盘扇区 的整数倍 读写文件的字节数为 磁盘扇区 的整数倍 ...

  3. 网站繁简切换的JS遇到的一个BUG

    公司打算进入台湾市场,最近开发了繁体版本的网站,数据库里的信息全是简体,除了网页上固定的文字手动翻译了,文章内容标题都不是繁体. 于是在网上找了一段比较流行的繁简切换的JS实现了,不过后来却发现,有些 ...

  4. Chapter4:表达式

    左值和右值 当一个对象被用作右值的时候,用的是对象的值(内容),当对象被用作左值的时候,用的是对象的身份(在内存中的位置). 一个重要的原则是需要右值的地方可以用左值来代替,但是不能把右值当作左值使用 ...

  5. MFC学习20160718(GetModuleFileName&&GetAppDataPath)

    1.标题栏设置 一.对话框标题栏内容为静态 直接在对话框属性“General”的“Caption”中修改. 二.对话框标题栏内容为动态生成的 在对应对话框的初始化函数OnInitDialog()中添加 ...

  6. CSS定位(CSS定位概述、相对定位、绝对定位、浮动)

    CSS 定位属性 CSS 定位属性允许你对元素进行定位. 属性 描述 position 把元素放置到一个静态的.相对的.绝对的.或固定的位置中. top 定义了一个定位元素的上外边距边界与其包含块上边 ...

  7. 《Genesis-3D开源游戏引擎-FQA常见问题解答》2014年01月10号版本

    1.Genesis-3D开源游戏引擎主要面向哪些用户人群?有限制吗? 1.我们的引擎没有限制,只要您想了解和使用我们的引擎,就可以加入Genesis-3D的大家庭.2.我们的主要用户群是各个相关的企业 ...

  8. Apache Maven 入门

    Apache Maven 入门篇 ( 上 ) Apache Maven 入门篇 ( 下 ) ~$mvn archetype:generate -DgroupId=com.mycompany.hello ...

  9. work_7

    1. 理解C++变量的作用域和生命周期 a) 用少于10行代码演示你对局部变量的生命周期的理解 局部变量分为动态局部变量和静态局部变量,其共同点为作用域均为定义它的函数体或语句块,其不同点为其生命周期 ...

  10. Java设计模式系列之单例模式

    单例模式的定义 一个类有且仅有一个实例,并且自行实例化向整个系统提供.比如,多程序读取一个配置文件时,建议配置文件时,建议配置文件封装成对象.会方便操作其中的数据,又要保证多个程序读到的是同一个配置文 ...