前言:之前分享了那么多bootstrap组件的使用经验,这篇博主打算研究下JS组件的扩展和封装,我们来感受下JQuery为我们提供$.Extend的神奇,看看我们怎么自定义自己的组件,比如我们想扩展一个$("#id").MyJsControl({})做我们自己的组件,我们该如何去做呢,别急,我们慢慢来看看过程。

一、扩展已经存在的组件

1、需求背景

很多时候,我们使用jquery.ajax的方式向后台发送请求,型如

$.ajax({
type: "post",
url: "/User/Edit",
data: { data: JSON.stringify(postdata) },
success: function (data, status) {
if (status == "success") {
toastr.success('提交数据成功');
$("#tb_aaa").bootstrapTable('refresh');
}
},
error: function (e) {
},
complete: function () {
} });

这种代码太常见了,这个时候我们有这样一个需求:在自己调用ajax请求的时候,我们不想每次都写error:function(e){}这种代码,但是我们又想让它每次都将ajax的错误信息输出到浏览器让用户能够看到。怎么办呢?

2、实现原理

要想实现以上效果其实并不难,我们可以将$.ajax({})封装一层,在封装的公共方法里面定义error对应的事件即可。确实,这样能达到我们的要求,但是并不完美,原因很简单:1)在jquery的基础上面再封装一层,效率不够高;2)需要改变调用者的习惯,每次调用ajax的时候需要按照我们定义的方法的规则来写,而不能直接用原生的$.ajax({})这种写法,这是我们不太想看到。

既然如此,那我们如何做到既不封装控件,又能达到以上要求呢?答案就是通过我们的$.extend去扩展原生的jquery.ajax

其实实现起来也并不难,通过以下一段代码就能达到我们的要求。

(function ($) {
//1.得到$.ajax的对象
var _ajax = $.ajax;
$.ajax = function (options) {
//2.每次调用发送ajax请求的时候定义默认的error处理方法
var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}
//3.如果在调用的时候写了error的处理方法,就不用默认的
if (options.error) {
fn.error = options.error;
}
if (options.success) {
fn.success = options.success;
}
if (options.beforeSend) {
fn.beforeSend = options.beforeSend;
}
if (options.complete) {
fn.complete = options.complete;
}
//4.扩展原生的$.ajax方法,返回最新的参数
var _options = $.extend(options, {
error: function (XMLHttpRequest, textStatus, errorThrown) {
fn.error(XMLHttpRequest, textStatus, errorThrown);
},
success: function (data, textStatus) {
fn.success(data, textStatus);
},
beforeSend: function (XHR) {
fn.beforeSend(XHR);
},
complete: function (XHR, TS) {
fn.complete(XHR, TS);
}
});
//5.将最新的参数传回ajax对象
_ajax(_options);
};
})(jQuery);

如果没接触过jquery里面$.extend这个方法的童鞋可能看不懂以上是什么意思。好,我们首先来看看jquery API对$.extend()方法是作何解释的。

什么意思呢?我们来看官方的两个例子就知道了

栗子一:

var settings = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
$.extend(settings, options);

结果:

settings == { validate: true, limit: 5, name: "bar" }

栗子二:

var empty = {};
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var settings = $.extend(empty, defaults, options);

结果:

settings == { validate: true, limit: 5, name: "bar" }
empty == { validate: true, limit: 5, name: "bar" }

以上的两个简单例子就说明extend()方法作用就是合并另个对象,有相同的则覆盖,没有相同的则添加。就是这么简单。

了解了$.extend()的作用,我们就能大概看懂上面那个扩展jquery.ajax的实现了吧。主要的步骤分为:

1)定义默认的error处理方法。

var fn = {
error: function (XMLHttpRequest, textStatus, errorThrown) {
toastr.error(XMLHttpRequest.responseText, '错误消息', { closeButton: true, timeOut: 0, positionClass: 'toast-top-full-width' });
},
success: function (data, textStatus) { },
beforeSend: function (XHR) { },
complete: function (XHR, TS) { }
}

2)判断用户在调用$.ajax({})的时候是否自定了error:function(){},如果定义过,则使用用户定义的,反之则用默认的error处理方法。

3)使用$.extend()将error默认处理方法传入$.ajax()的参数中。我们看options参数时包含$.ajax()方法里面所有的参数的,然后用默认的fn去扩展它即可。

通过以上三步就能够实现对$.ajax()方法里面error默认处理方法。这样扩展,对于我们使用者来说完全感觉不到变化,我们仍然可以$.ajax({});这样去发送ajax请求,如果没有特殊情况,不用写error处理方法。

3、组件扩展的意义

使用组件扩展,能够帮助我们在原有组件上面增加一些和我们系统业务相关的处理需求,而在使用时,还是和使用原生组件一样去调用,免去了在组件上面再封装一层的臃肿。

二、扩展自己组件

上面通过$.extend()方法扩展了$.ajax()的error事件处理方法。下面我们来封装一个自己的组件试试,功能很简单,但比较有说明性。我们就以select这个组件为例,很多情况下,我们的select里面的option都是需要从数据库里面取数据的,所以一般的做法就是发送一个ajax请求,然后在success方法里面拼html。现在我们就封装一个select远程取数据的方法。

1、代码实现以及使用示例

先上干货吧,将写好的整出来:

(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;
$.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) { }
};
})(jQuery);

先来看看我们自定义组件如何使用:

用法一:通过URL远程取数据并初始化

首先定义一个空的select

<select id="sel_search_plant" class="form-control"></select>

然后初始化它

$(function(){
$('#sel_search_plant').combobox({
url: '/apiaction/Plant/Find',
valueField: 'TM_PLANT_ID',
textField: 'NAME_C'
});
})

参数很简单,就不一一介绍了。很简单有木有~~

用法二:取值和设置

var strSelectedValue = $('#sel_search_plant').combobox("getValue");
$('#sel_search_plant').combobox("setValue", "aaa");

其实对于简单的select标签,博主觉得这里的getValu和SetValue意义不大,因为直接通过$('#sel_search_plant').val()就能解决的事,何必要再封一层。这里仅仅是做演示,试想,如果是封装成类似select2或者multiselect这种组件,getValue和setValue的意义就有了,你觉得呢?

2、代码详细讲解

上面的实现代码,如果您一眼就能看懂,证明您是经常封组件的大虾了,下面的就不用看了。如果看不懂,也没关系,我们将代码拆开详细看看里面是什么鬼。

(1)首先看看我们最常看到的如下写法:

(function ($) {
//....封装组件逻辑
})(jQuery);

初初看到这种用法,博主也是狂抓,这是什么鬼嘛,四不像啊。使用多了之后才知道原来这就是一个匿名函数的形式。将它拆开来看如下:

var fn = function($){
//.....组件封装逻辑
};
fn(jQuery);

也就是说这种写法就表示先定义一个方法,然后立即调用这个方法,jQuery相当于实参。打开jquery.js的原文件可以看到,jQuery是这个文件里面的一个全局变量。

(2)定义自己的组件的代码:

$.fn.combobox = function (options, param) {

};

习惯这种写法的应该知道,这个就表示向jquery对象添加自定义方法,比如你想使用文章开始的

$("#id").MyJsControl({})

这种用法,你就可以这样定义

$.fn.MyJsControl=function(options){}

(3)

options = $.extend({}, $.fn.combobox.defaults, options || {});

这一句,看过上文的朋友应该还记得extend这么一个方法吧,怎么样,又来了你。这句话其实就没什么好说的了,合并默认参数和用户传进来的参数。

(4)默认参数列表

$.fn.combobox.defaults = {
url: null,
param: null,
data: null,
valueField: 'value',
textField: 'text',
placeholder: '请选择',
onBeforeLoad: function (param) { },
onLoadSuccess: function () { },
onChange: function (value) { }
};

如果用户没有传参,就用默认的参数列表。如果你够细心,你会发现博主之前分享的其他bootstrap组件的js文件里面都有这么一个default参数列表。我们随便找两个:

bootstrap上传组件

bootstrap table组件

基本都是这么些用法。这样来看,是否也可以自己封一个js组件~~

JS组件系列——封装自己的JS组件的更多相关文章

  1. JS组件系列——封装自己的JS组件,你也可以

    前言:之前分享了那么多bootstrap组件的使用经验,这篇博主打算研究下JS组件的扩展和封装,我们来感受下JQuery为我们提供$.Extend的神奇,看看我们怎么自定义自己的组件,比如我们想扩展一 ...

  2. JS组件系列——又一款MVVM组件:Vue(二:构建自己的Vue组件)

    前言:转眼距离上篇 JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查) 已有好几个月了,今天打算将它捡起来,发现好久不用,Vue相关技术点都生疏不少.经过这几个月的时间,Vue ...

  3. 原生JS面向对象思想封装轮播图组件

    原生JS面向对象思想封装轮播图组件 在前端页面开发过程中,页面中的轮播图特效很常见,因此我就想封装一个自己的原生JS的轮播图组件.有了这个需求就开始着手准备了,代码当然是以简洁为目标,轮播图的各个功能 ...

  4. JS组件系列——两种bootstrap multiselect组件大比拼

    前言:今天继续来看看bootstrap的另一个组件:multiselect.记得在项目开始之前,博主项目组几个同事就使用哪些js组件展开过讨论,其中就说到了select组件,由于项目的整体风格使用的b ...

  5. C#组件系列———又一款日志组件:Elmah的学习和分享

    前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了“规矩”,今天还是来写一篇.最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛.一如既往,园子里每年这个时候 ...

  6. C#组件系列——又一款日志组件:Elmah的学习和分享

    前言:好久没动笔了,都有点生疏,12月都要接近尾声,可是这月连一篇的产出都没有,不能坏了“规矩”,今天还是来写一篇.最近个把月确实很忙,不过每天早上还是会抽空来园子里逛逛.一如既往,园子里每年这个时候 ...

  7. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)

    前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...

  8. JS组件系列——Bootstrap文件上传组件:bootstrap fileinput

    前言:之前的三篇介绍了下bootstrap table的一些常见用法,发现博主对这种扁平化的风格有点着迷了.前两天做一个excel导入的功能,前端使用原始的input type='file'这种标签, ...

  9. JS组件系列——分享自己封装的Bootstrap树形组件:jqTree

    前言:之前的一篇介绍了下如何封装自己的组件,这篇再次来体验下自己封装组件的乐趣.看过博主博客的园友应该记得之前分享过一篇树形菜单的使用JS组件系列——Bootstrap 树控件使用经验分享,这篇里面第 ...

随机推荐

  1. Support Vector Machine (3) : 再谈泛化误差(Generalization Error)

    目录 Support Vector Machine (1) : 简单SVM原理 Support Vector Machine (2) : Sequential Minimal Optimization ...

  2. PAMI 2010 Context-aware saliency detection

    This is a highly-cited paper. The context aware saliency proposed based on four principles, which ca ...

  3. 研究SVM时安装的一些工具的方法

    本文是个人存档,不介绍研究SVM相关内容. 1.bamboo在fedora19下 哪一行编译时报错,就注释掉 php插件不用装 提示ERROR: libcrfpp.so.0: cannot open ...

  4. 用贝赛尔曲线把图片, 按钮, label 绘成圆 或圆角矩形

    //创建圆形遮罩,把用户头像变成圆形 /* *CGPointMake(35, 35)  是绘图的中心点,  如果想把控件居中绘圆, 一般用控件的中心点,   radius 是圆半径   startAn ...

  5. APS.net controller

    在controller 路由配置为: routes.MapRoute( name:"Default", url:"{controller}/{action}/{id}&q ...

  6. hihoCoder #1199 : Tower Defense Game ——(树型dp)

    题目链接:https://hihocoder.com/problemset/problem/1199. 题意:一棵以1为根的树,每个点有一个p值和q值,到这个点需要当前分数大于等于p,然后消耗掉(p- ...

  7. Thomas Brinkhoff 基于路网的移动对象生成器的使用

    Thomas Brinkhoff 基于路网的移动对象生成器的使用 网站:http://iapg.jade-hs.de/personen/brinkhoff/generator/ 各个移动对象在路网中进 ...

  8. 网络编程——URL编程

    URL:是统一资源定位器的简称,它表示Internet某一资源的地址.通过URL我们可以访问Internet上的各种网络资源,比如最常见的www,ftp站点.浏览器通过解析给定的URL可以在网络上查找 ...

  9. ffmpeg 音频转换: use ffmpeg convert the audio from stereo to mono without changing the video part

    To convert the audio from stereo to mono without changing the video part, you can use FFmpeg: ffmpeg ...

  10. js算出生日是当年第多少天

    var year, month, day, monthSum = 0; var arr = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, ...