javascript的MVC三层架构(案例之分页插件)
作者:田想兵 博客地址:http://www.cnblogs.com/tianxiangbing
最近很少写博文,一是比较忙,二是没啥心情,好,言归正传,今天的主题是MVC版的javascript结构,做程序的,可能对MVC会有较深刻的印象,就是Model-View-Control,中文的意思就是模型-视图-控制器,这好像已经是一个很成熟的结构了,后来又有些在它基础上的拓展,有兴趣的可以摆渡一下。所以它基本上适应任何情况下的编程,今天我们就要用它来实现js版的一个分页控件。
首先,我们先明确每一层是做什么的:
View也就是视图层,在这里面我们会去初始化一些html元素;
Model模型层,我一直认为这一层存在的意义不大,因为通常我们new一个js对象的时候,都会初始化它的一些变量,很少会去单独写个方法去设置它,所以就把Model改成发送ajax请求了;
Control控制器,这里就是一些业务逻辑了,这里我们可以再分出一个事件层来;
Event事件层,处理html事件。
中途停了一天,接着写,不知道为啥,最近一直都没有办法集中精力,可能是因为儿童节吧,今天还要加班, 可悲的码农啊,题外话不说了,继续码字。
现在我们来分析下需求,分页控件,无非就是对数据的一个分组显示,所以它一定会有pagesize(每页条数)和count(总条数) 这两个属性,当然也有人喜欢把所有数据返回过来给前端来分页,不过分页的目的之一,就是为了减轻数据量,一次批量返回也不是不行,具体情况具体分析吧!有了count和pagesize后,我们就可以算出总页码数了。
这个很简单,就是整除有余的话就多一页,否则取整数部分。
我们先看下效果图,不然的话,脑子里没有一个结构,也是无法下手的。请观看下图:
接着该MVC三层结构出场了,我们先在view(视图层)初始化一些必要的HTML元素:
var _self=this;
var _class={
page:function(args){
var _html='\
<div class="pager">\
<a class="firstPage" href="javascript:void(0);">首页</a>\
<a class="prePage" href="javascript:void(0);">上一页</a>\
<span class="inputPage">第<input type="text" value="'+ _self.currentIndex +'" class="txt_curIndex" name="txt_curIndex"/>页/<i>'+_self.sumPage+'</i>页</span>\
<a class="nextPage" href="javascript:void(0);">下一页</a>\
<a class="lastPage" href="javascript:void(0);">末页</a>\
</div>\
';
_self.content.html(_html);
_self.event("bind",args);
}
};
return _class[method](args);
}
在这里,我又调用了事件层,来给这些HTML元素绑定相应的事件,这里大概有五个事件,就是上一页,下一页,首页,未页,及跳转。这些事件,其本质就是改变页码数,好,那我们就写个请求页码数的方法:
var _self=this;
var _class={
go:function(args){
return $.ajax({
url:_self.ajaxUrl,
dataType:"json",
async:true,
data:args[0],
success:function(data){
args[1](data);
_self.cpu("change",data);
},
type:"GET",
error:function(data){
alert("json格式不正确")
}
});
}
};
return _class[method](args);
}
这是个ajax请求,放在model层下面,有两个参数,一个是ajax需要传的参数{page:1} args[0],另一个是外部的一个回调方法,用来格式化内容的,这个跟分页控件没有半毛钱关系,所以就当作回调。好,接着我们在控制器层里就调用这个方法就行了,刚才说到有五个事件,这样就对应了五个控制器:
var _self=this;
var _class={
change:function(data){
var arr = data;
if (data.count>0){
_self.count=data.count;
_self.sumPage= parseInt( _self.count % _self.pageSize >0 ? _self.count / _self.pageSize+1 : _self.count / _self.pageSize);
_self.view("page");
}
},
jump:function(args){
var input = parseInt($("[name='txt_curIndex']",_self.content).val());
if (input>0 && input <= _self.sumPage){
_self.currentIndex = input;
_self.ajaxArgs.page = _self.currentIndex-1;
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
}
},
prev:function(args){
if (_self.currentIndex > 1){
_self.currentIndex--;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
next:function(args){
if (_self.currentIndex < _self.sumPage - 1){
_self.currentIndex++;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
last:function(args){
_self.currentIndex = _self.sumPage;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
},
first:function(args){
_self.currentIndex = 1;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
};
return _class[method](args);
}
};
数一数,是不是五个,我勒个去,作者数学不好,竟然是他妹的六个,竟然多了个change,好啦,把chage这个放model里去吧,他的作用就是计算一些变量的值。
最后事件层只需要调用Control控制层相对应的方法就行了:
var _self=this;
var pager = $("div.pager",_self.content);
var _class={
bind:function(args){
$("[name='txt_curIndex']",_self.content).keydown(function(e){
if (e.keyCode==13){
_self.cpu("jump",args);
}
});
$(".prePage",_self.content).click(function(){
_self.cpu("prev",args);
});
$(".nextPage",_self.content).click(function(){
_self.cpu("next",args);
});
$(".lastPage",_self.content).click(function(){
_self.cpu("last",args);
});
$(".firstPage",_self.content).click(function(){
_self.cpu("first",args);
});
}
};
return _class[method](args);
},
这样是不是就完了呢,你猜?没错,恭喜你,猜对了,还没有结束,因为我们还没有看到入口,一般情况我们都喜欢定义一个名为init的方法来初始化,这次的情况也很一般,所以定义init吧:
var _self = this;
_self.currentIndex = ops.currentIndex;
_self.ajaxArgs = $.extend( ops.ajaxArgs,{page:this.currentIndex-1});
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
},
整个分页插件的代码如下:
this.currentIndex = 1;
this.count = ops.count;
this.pageSize = ops.pageSize||10;
this.sumPage = 1;
this.content = ops.content;
this.ajaxUrl = ops.url;
this.returnFunc=ops.returnFunc||new Function();
};
Pager.prototype={
init:function(ops){
var _self = this;
_self.currentIndex = ops.currentIndex;
_self.ajaxArgs = $.extend( ops.ajaxArgs,{page:this.currentIndex-1});
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
},
view:function(method,args){
var _self=this;
var _class={
page:function(args){
var _html='\
<div class="pager">\
<a class="firstPage" href="javascript:void(0);">首页</a>\
<a class="prePage" href="javascript:void(0);">上一页</a>\
<span class="inputPage">第<input type="text" value="'+ _self.currentIndex +'" class="txt_curIndex" name="txt_curIndex"/>页/<i>'+_self.sumPage+'</i>页</span>\
<a class="nextPage" href="javascript:void(0);">下一页</a>\
<a class="lastPage" href="javascript:void(0);">末页</a>\
</div>\
';
_self.content.html(_html);
_self.event("bind",args);
}
};
return _class[method](args);
},
event:function(method,args){
var _self=this;
var pager = $("div.pager",_self.content);
var _class={
bind:function(args){
$("[name='txt_curIndex']",_self.content).keydown(function(e){
if (e.keyCode==13){
_self.cpu("jump",args);
}
});
$(".prePage",_self.content).click(function(){
_self.cpu("prev",args);
});
$(".nextPage",_self.content).click(function(){
_self.cpu("next",args);
});
$(".lastPage",_self.content).click(function(){
_self.cpu("last",args);
});
$(".firstPage",_self.content).click(function(){
_self.cpu("first",args);
});
}
};
return _class[method](args);
},
model:function(method,args){
var _self=this;
var _class={
go:function(args){
return $.ajax({
url:_self.ajaxUrl,
dataType:"json",
async:true,
data:args[0],
success:function(data){
args[1](data);
_self.model("change",data);
},
type:"GET",
error:function(data){
console.dir(data)
alert("json格式不正确")
}
});
},
change:function(data){
var arr = data;
if (data.count>0){
_self.count=data.count;
_self.sumPage= parseInt( _self.count % _self.pageSize >0 ? _self.count / _self.pageSize+1 : _self.count / _self.pageSize);
_self.view("page");
}
}
};
return _class[method](args);
},
cpu:function(method,args){
var _self=this;
var _class={
jump:function(args){
var input = parseInt($("[name='txt_curIndex']",_self.content).val());
if (input>0 && input <= _self.sumPage){
_self.currentIndex = input;
_self.ajaxArgs.page = _self.currentIndex-1;
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
}
},
prev:function(args){
if (_self.currentIndex > 1){
_self.currentIndex--;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
next:function(args){
if (_self.currentIndex < _self.sumPage - 1){
_self.currentIndex++;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
last:function(args){
_self.currentIndex = _self.sumPage;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
},
first:function(args){
_self.currentIndex = 1;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
};
return _class[method](args);
}
};
OK,最后我们回过头来看下,这个结构的优劣点,优点了,就是更方便于扩展,可以无限制的往下加,层次分得较清明,劣点是,层次过深,效率会低一些,而且看着不爽,所以我给他定位为,管理系统业务逻辑较多时使用,一般的JS效果插件还是使用扁平结构的好。今天就写到这吧,谢谢您的观看,最后的台词是:如果你有任何的疑问都不要来问我,请反复阅读本文。也可以加入我的QQ群与其他人讨论,本文的DEMO会放在群共享里。我的群号有5678537,70210212,闭幕.