一步一步封装自己的HtmlHelper组件BootstrapHelper(三)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件。和上篇不同的是,这篇的有几个组件需要某些js文件的支持。
BootstrapHelper系列文章目录
C#进阶系列――一步一步封装自己的HtmlHelper组件:BootstrapHelper
C#进阶系列――一步一步封装自己的HtmlHelper组件:BootstrapHelper(二)
C#进阶系列――一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
一、NumberBoxExtensions
NumberBoxExtensions是一个基于bootstrap风格的数字文本框,基于之前博主介绍的自增器数字组件spinner去做的封装,不了解spinner组件的园友可以看看//www.jb51.net/article/88490.htm里面介绍的第二个组件。
通过之前的介绍我们知道,自增组件spinner的初始化不需要写任何的js代码,直接通过配置html里面的data属性即可实现它的初始化,这样给我们的封装就带来了很大的方便,我们只需要将常用的初始化参数作为扩展方法的参数传进来,然后在后台变成相应的data属性返回到前端。
废话不多说,先将封装的源码摆上来。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions { public static class NumberBoxExtensions { /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id) { return NumberTextBox(html, id, null, null, null, null, null); } /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">文本框的value值</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value) { return NumberTextBox(html, id, value, null, null, null, null); } /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="value">文本框的value值</param> /// <param name="min">自增长的最小值</param> /// <param name="max">自增长的最大值</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, object value, int? min, int? max) { return NumberTextBox(html, null, value, min, max, null, null); } /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">文本框的value值</param> /// <param name="min">自增长的最小值</param> /// <param name="max">自增长的最大值</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max) { return NumberTextBox(html, id, value, min, max, null, null); } /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">文本框的value值</param> /// <param name="min">自增长的最小值</param> /// <param name="max">自增长的最大值</param> /// <param name="step">每次自增的数字</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max, int? step) { return NumberTextBox(html, id, value, min, max, step, null); } /// <summary> /// 生成数字文本框 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">文本框的value值</param> /// <param name="min">自增长的最小值</param> /// <param name="max">自增长的最大值</param> /// <param name="step">每次自增的数字</param> /// <param name="rule">自增规则</param> /// <returns>返回数字文本框</returns> public static MvcHtmlString NumberTextBox(this BootstrapHelper html, string id, object value, int? min, int? max, int? step, SpinningRule? rule) { TagBuilder tag = new TagBuilder("div"); tag.MergeAttribute("class", "input-group spinner"); tag.MergeAttribute("data-trigger", "spinner"); System.Text.StringBuilder sb = new System.Text.StringBuilder(); //sb.Append("<input type='text' class='form-control text-center' value='1' data-min='-10' data-max='10' data-step='2' data-rule='quantity'>"); sb.Append("<input type='text' class='form-control text-center' "); if (!string.IsNullOrEmpty(id)) { sb.Append("id='").Append(id).Append("' "); } if (value != null) { sb.Append("value='").Append(value.ToString()).Append("' "); } else { sb.Append("value='1' "); } if (min != null) { sb.Append("data-min='").Append(min).Append("' "); } if (max != null) { sb.Append("data-max='").Append(max).Append("' "); } if (step != null) { sb.Append("data-step='").Append(step).Append("' "); } if (rule != null) { sb.Append("data-rule='").Append(rule.ToString()).Append("' "); } else { sb.Append("data-rule='quantity' "); } sb.Append("/>"); sb.Append("<span class='input-group-addon'>"); sb.Append("<a href='javascript;;' class='spin-up' data-spin='up'><i class='fa fa-caret-up'></i></a>"); sb.Append("<a href='javascript:;' class='spin-down' data-spin='down'><i class='fa fa-caret-down'></i></a>"); sb.Append("</span>"); tag.InnerHtml = sb.ToString(); return MvcHtmlString.Create(tag.ToString()); } } public enum SpinningRule { defaults, currency, quantity, percent, month, day, hour, minute, second, } }
除去id、value,我们自增组件初始化需要的参数主要有min、max、step、rule等,分别对应组件的data-min、data-max、data-step、data-rule等参数。
使用相当简单,首先在对应的页面引用相关的js和css文件,然后在cshtml里面这样调用即可:
代码如下:
@Bootstrap.NumberTextBox(null, "1", 1, 10, 2, null)
得到结果:
这样比我们每次都去copy一大段html代码要方便多了吧,你有没有一丝心动呢~~
二、DateTimeBoxExtensions
有了上面的数字组件作为基础,接下来就是封装时间组件了。博主同样打算使用data属性去做初始化,可是找了半天文档,datatimepicker里面没有自带data属性初始化的方式,没办法,只有博主自己去做data属性的初始化了。
1、初始方案
我们新建一个文件:bootstrap-datetimepicker-helper.js。里面的代码如下
$(function () { var datetimedefault = { locale: 'zh-CN', //中文化 }; $.each($(".date"), function (index, item) { var data = $(item).data(); var param = $.extend({}, datetimedefault, data || {}); $(item).datetimepicker(param); }); });
然后html代码这样写
<div class='input-group date' data-format="YYYY-MM-DD" data-maxDate="2017-01-10" data-minDate="2010-01-10"> <input type='text' class="form-control" /> <span class="input-group-addon"> <span class="glyphicon glyphicon-calendar"></span> </span> </div>
貌似都没有问题,起初博主也以为没有任何问题。可是事与愿违!这里有一点博主没有考虑到,那就是Jquery里面的data()方法取到的属性名会全部转小写,也就是说,我们html里面写的data-maxDate,可是通过data()方法得到的结果却变成了maxdate,如下图:
然后在初始化datetimepicker的时候就报了js异常。此法行不通。
2、改进方案
既然上面的方法行不通,那我们就要改进啊,data()方法是否存在什么参数可以让它不转小写的?找了一圈,没找到啥答案。最后没办法,博主打算自行改造,于是js代码变成了这样:
$(function () { var datetimedefault = { format: 'YYYY-MM-DD',//日期格式化,只显示日期 locale: 'zh-CN', //中文化 maxDate: '2017-01-01',//最大日期 minDate: '2010-01-01', //最小日期 viewMode: 'days', defaultDate: false, disabledDates: false, enabledDates: false, }; $.each($(".date"), function (index, item) { var data = $(item).data(); $.each(data, function (key, value) { for (i in datetimedefault) { if (key == i.toLowerCase()) { datetimedefault[i] = value; break; } } }); //var param = $.extend({}, datetimedefault, data || {}); $(item).datetimepicker(datetimedefault); }); });
原理就是比较data()方法得到的结果和datetimedefault的属性名转小写后进行对比,如果相同,则将html里面的data属性覆盖默认的属性。调试了几遍,基本没发现什么问题。
代码写成这样,确实可以解决我们上面的问题,但是需要我们的datetimedefault变量里面包含足够多的默认参数,这样才能达到覆盖的目的,当然项目中一般修改的就那么几个参数,这里只需要加上一些我们经常需要变化的默认属性即可。
好了,有上面的作为理论基础,我们的DataTimeBox也就好封装了。直接上代码吧。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions { public static class DateTimeBoxExtensions { /// <summary> /// 生成日期控件 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <returns>返回呈现日期控件的html标签</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id) { return DateTimeBox(html, id, null, null, null, null, null, null); } /// <summary> /// 生成日期控件 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <returns>返回呈现日期控件的html标签</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value) { return DateTimeBox(html, id, value, null, null, null, null, null); } /// <summary> /// 生成日期控件 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <param name="format">显示日期的格式</param> /// <param name="maxDate">日期的最小值</param> /// <param name="minDate">日期的最大值</param> /// <returns>返回呈现日期控件的html标签</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value, string format, string maxDate, string minDate) { return DateTimeBox(html, id, value, format, maxDate, minDate, null, null); } /// <summary> /// 生成日期控件 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">文本框标签的id</param> /// <param name="value">文本框标签的默认值</param> /// <param name="format">显示日期的格式</param> /// <param name="maxDate">日期的最小值</param> /// <param name="minDate">日期的最大值</param> /// <param name="viewMode">日期控件的浏览模式</param> /// <param name="showClear">是否显示清空按钮</param> /// <returns>返回呈现日期控件的html标签</returns> public static MvcHtmlString DateTimeBox(this BootstrapHelper html, string id, object value, string format, string maxDate, string minDate, string viewMode, bool? showClear) { TagBuilder tag = new TagBuilder("div"); tag.MergeAttribute("class", "input-group date"); if (!string.IsNullOrEmpty(format)) { tag.MergeAttribute("data-format", format); } if (!string.IsNullOrEmpty(maxDate)) { tag.MergeAttribute("data-maxDate", maxDate); } if (!string.IsNullOrEmpty(minDate)) { tag.MergeAttribute("data-minDate", minDate); } if (!string.IsNullOrEmpty(viewMode)) { tag.MergeAttribute("data-viewMode", viewMode); } if (showClear!=null) { tag.MergeAttribute("data-showClear", showClear.ToString()); } System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append("<input type='text' class='form-control'"); if(!string.IsNullOrEmpty(id)) { sb.Append("id='").Append(id).Append("' "); } if (value != null) { sb.Append("value='").Append(value.ToString()).Append("' "); } sb.Append("/>").Append("<span class='input-group-addon'>") .Append("<span class='glyphicon glyphicon-calendar'></span>") .Append("</span>"); tag.InnerHtml = sb.ToString(); return MvcHtmlString.Create(tag.ToString()); } } }
然后我们的cshtml页面只需要引用我们的js和css
<link href="~/Content/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css" rel="stylesheet" /> <script src="~/Content/bootstrap-datetimepicker/js/moment-with-locales.js"></script> <script src="~/Content/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js"></script> <script src="~/Content/bootstrap-datetimepicker/js/bootstrap-datetimepicker-helper.js"></script>
然后直接使用
<div class="col-xs-3"> @Bootstrap.DateTimeBox("starttime", null, null, null, null, null, null) </div> <div class="col-xs-3"> @Bootstrap.DateTimeBox("endtime", null, null, null, null, null, null) </div>
得到结果
三、TextareExtensions
关于textarea文本域的封装相对来说是比较简单的,因为本身它的结构和TextBox就差不多,我们直接给出封装的源码吧。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions { public static class TextareExtensions { /// <summary> /// textarea文本域 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id) { return TextAreaBox(html, id, null, null, null, null); } /// <summary> /// textarea文本域 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass) { return TextAreaBox(html, id, value, cssClass, null, null); } /// <summary> /// textarea文本域 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <param name="rows">行数</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass, int? rows) { return TextAreaBox(html, id, value, cssClass, rows, null); } /// <summary> /// textarea文本域 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">id</param> /// <param name="value">value</param> /// <param name="cssClass">样式</param> /// <param name="rows">行数</param> /// <param name="cols">列数</param> /// <returns>html标签</returns> public static MvcHtmlString TextAreaBox(this BootstrapHelper html, string id, object value, string cssClass, int? rows, int? cols) { TagBuilder tag = new TagBuilder("textarea"); tag.AddCssClass("form-control"); if (!string.IsNullOrEmpty(id)) { tag.MergeAttribute("id", id); } if (value != null) { tag.MergeAttribute("value", value.ToString()); } if (!string.IsNullOrEmpty(cssClass)) { tag.AddCssClass(cssClass); } if (rows != null) { tag.MergeAttribute("rows", rows.ToString()); } if (cols != null) { tag.MergeAttribute("cols", cols.ToString()); } return MvcHtmlString.Create(tag.ToString()); } } }
只支持几个最简单的参数rows和cols,如果你的项目有一些特殊的需求,比如需要初始化成富文本编辑框,你也可以自己去改进。
使用方法
<div> @Bootstrap.TextAreaBox("id", "", "", 3, 5) </div>
这里有一个问题要提一下,就是我们直接这样写,得到的结果却是:
貌似我们的cols属性没有起作用,原来,只要你的标签加了class='form-control'样式,那它就会在满撑整个div,解决的方法也很简单,比如我们可以在div上面做些处理:
代码如下:
<div> @Bootstrap.TextAreaBox("", "", "", 3, 5) </div>
既然cols属性没有起作用,其实参数里面的cols可以考虑去掉。
四、SelectExtensions
又到了纠结的select下拉框。为什么说它纠结呢,因为封装它的时候要考虑很多问题,比如:
<optgroup>和<option>如何处理静态的option值如何传到后台是封装原始的select还是封装成基于某些组件(比如select2)的select方法和事件该如何处理等等
后来转念一想,封装的目的是为了什么?不就是为了使用起来更加方便吗,如果封得太死,使用起来能够方便吗?后来所幸直接封装成最简单的select算了。博主打算这样做:
如果是静态的option,直接写原生的select标签;如果是动态的option,则传递对应的url到后台,取到数据后生成option。封装的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace BootstrapExtensions { public static class SelectExtensions { /// <summary> /// 返回select标签 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">标签id</param> /// <returns>select标签</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id) { return SelectBox(html, id, null, null, null, null, null, null); } /// <summary> /// 返回select标签 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">标签id</param> /// <param name="value">标签选中值</param> /// <returns>select标签</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value) { return SelectBox(html, id, value, null, null, null, null, null); } /// <summary> /// 返回select标签 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">标签id</param> /// <param name="value">标签选中值</param> /// <param name="cssClass">标签样式</param> /// <returns>select标签</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass) { return SelectBox(html, id, value, cssClass, null, null, null, null); } /// <summary> /// 返回select标签 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">标签id</param> /// <param name="value">标签选中值</param> /// <param name="cssClass">标签样式</param> /// <param name="url">请求数据的url</param> /// <param name="textField">显示字段</param> /// <param name="valueField">值字段</param> /// <returns>select标签</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass, string url, string textField, string valueField) { return SelectBox(html, id, value, cssClass, url, null, textField, valueField); } /// <summary> /// 返回select标签 /// </summary> /// <param name="html">扩展方法实例</param> /// <param name="id">标签id</param> /// <param name="value">标签选中值</param> /// <param name="cssClass">标签样式</param> /// <param name="url">请求数据的url</param> /// <param name="param">请求的参数</param> /// <param name="textField">显示字段</param> /// <param name="valueField">值字段</param> /// <param name="multiple">是否多选</param> /// <returns>select标签</returns> public static MvcHtmlString SelectBox(this BootstrapHelper html, string id, object value, string cssClass, string url, string param, string textField, string valueField, bool multiple = false) { TagBuilder tag = new TagBuilder("select"); tag.AddCssClass("form-control"); if (!string.IsNullOrEmpty(id)) { tag.MergeAttribute("id", id); } if (value != null) { tag.MergeAttribute("value", value.ToString()); } if (!string.IsNullOrEmpty(cssClass)) { tag.AddCssClass(cssClass); } if (!string.IsNullOrEmpty(url)) { tag.MergeAttribute("data-url", url); } if (!string.IsNullOrEmpty(param)) { tag.MergeAttribute("data-param", param); } if (!string.IsNullOrEmpty(valueField)) { tag.MergeAttribute("data-value-field", valueField); } if (!string.IsNullOrEmpty(textField)) { tag.MergeAttribute("data-text-field", textField); } if (multiple) { tag.MergeAttribute("multiple", "multiple"); } return MvcHtmlString.Create(tag.ToString()); } } }
然后前端使用js去初始化,需要有一个js文件utility.combobox.js:
(function ($) { //1.定义jquery的扩展方法combobox $.fn.combobox = function (options, param) { if (typeof options == 'string') { return $.fn.combobox.methods[options](this, param); } //2.将调用时候传过来的参数和default参数合并 options = $.extend({}, $.fn.combobox.defaults, options || {}); //3.添加默认值 var target = $(this); target.attr('valuefield', options.valueField); target.attr('textfield', options.textField); target.empty(); var option = $('<option></option>'); option.attr('value', ''); option.text(options.placeholder); target.append(option); //4.判断用户传过来的参数列表里面是否包含数据data数据集,如果包含,不用发ajax从后台取,否则否送ajax从后台取数据 if (options.data) { init(target, options.data); } else { //var param = {}; options.onBeforeLoad.call(target, options.param); if (!options.url) return; if (typeof options.param == "string") { options.param = JSON.parse(options.param); } $.getJSON(options.url, options.param, function (data) { init(target, data); }); } function init(target, data) { $.each(data, function (i, item) { var option = $('<option></option>'); option.attr('value', item[options.valueField]); option.text(item[options.textField]); target.append(option); }); options.onLoadSuccess.call(target); } target.unbind("change"); target.on("change", function (e) { if (options.onChange) return options.onChange(target.val()); }); } //5.如果传过来的是字符串,代表调用方法。 $.fn.combobox.methods = { getValue: function (jq) { return jq.val(); }, setValue: function (jq, param) { jq.val(param); }, load: function (jq, url) { $.getJSON(url, function (data) { jq.empty(); var option = $('<option></option>'); option.attr('value', ''); option.text('请选择'); jq.append(option); $.each(data, function (i, item) { var option = $('<option></option>'); option.attr('value', item[jq.attr('valuefield')]); option.text(item[jq.attr('textfield')]); jq.append(option); }); }); } }; //6.默认参数列表 $.fn.combobox.defaults = { url: null, param: null, data: null, valueField: 'value', textField: 'text', placeholder: '请选择', onBeforeLoad: function (param) { }, onLoadSuccess: function () { }, onChange: function (value) { } }; //这一段是新加的,在页面初始化完成之后调用初始化方法 $(document).ready(function () { $('select').each(function () { var $combobox = $(this); $.fn.combobox.call($combobox, $combobox.data()); }) }); })(jQuery);
这个js文件来源于博主之前的一篇文章//www.jb51.net/article/92595.htm
然后前端调用
<div class="col-xs-2"> @Bootstrap.SelectBox("sel", null, null, "/Home/GetDept", null, "Name", "Id") </div>
五、总结
至此,我们BootstrapHelper第一个版本的封装基本算是完成了,主要封装的组件有如下几个:
当然还有大家最喜欢源码分享了。源码地址
如果你觉得本文能够帮到你,请帮忙推荐吧。