请跟我来--使用Ext+llano快速搞定CRUD
先看看我重构后的代码是这样:
function(){
Ext.BLANK_IMAGE_URL="http://www.cnblogs.com/resources/default/s.gif";
Ext.QuickTips.init();
var member_meta_def =[ 'id',
{fieldLabel:'姓名',name: 'Name',type: 'string',allowBlank: false,minLength:3,maxLength:8,anchor:'90%',width:50},
{fieldLabel:'邮件',name: 'Email',type: 'string',anchor:'90%',width:80},
{fieldLabel:'手机',name: 'Mobile',type: 'string',anchor:'90%',width:80},
{fieldLabel:'毕业年份',name: 'GradYear',type: 'int',anchor:'90%',width:20},
{fieldLabel:'登记日期',name: 'sCreatedDate', type: 'date',dateFormat: 'Y-m-d H:i:s', anchor:'60%',width:30}
];
var member_meta_rec= Ext.data.Record.create(member_meta_def);
var new_member=new member_meta_rec({
Name: 'hh',Email: 'xhhy',Mobile: '136',GradYear:2008,
sCreatedDate: new Date()});
CRUD({entityName:'member',showEditBox:true,
list_id:'list1',edit_id:'edit1',list_title:'list',edit_title:'edit',list_width:800,list_height:400,editt_width:300,
confirmShowColumName:'Name',
meta:member_meta_def,facade:'facade.aspx',sortFld:'Name',sortDir:'ASC',
new_record:new_member});
}
);
可以实际从数据库模糊查询符合条件的记录,比上个例子增加了分页处理!
还可以直接在列表中直接修改内容,更像传统C/S软件了。
关键说明如下,让我们先从简单的后台说起:
1)实体对象
前台后后台交换的数据定义,按照llano的要求,数据表以T开头,视图以V开头,代码大致如下:
{
using Castle.ActiveRecord;
using llano.data;
using Nullables;
[ActiveRecord("app_Members")]
public class TMember : Entity
{
private string _Name;
private string _Mobile;
private string _Email;
private NullableInt32 _GradYear;
private int _Score;
public TMember()
: base()
{
}
public TMember(string username, string mb)
: this()
{
this._Name = username;
this._Mobile = mb;
}
[Property(Length = 10)]
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
}
使用llano的好处是她天生就支持Ext,当你直接打印一个Entity的实例时,实际就会输出它的JSON字符串,当你要输出一个数组时,一句话就能搞定,看看下面的示例就明白了:
Dictionary<String, object> args = new Dictionary<string, object>();
args.Add("Mobile", "%9%");
int total = dao.GetTotalCnt(sql, args);
TMember[] ps = dao.Load(sql, args, 0, 2);
Console.WriteLine(JsonHelper<TMember>.SerializeArray(total, ps));
2)后台服务对象
在上一个例子中,facade.aspx已经把思路说得很清楚了。在实际项目中通常有上百张表,所以我们就把那段方法判断得代码提到了基类中,而继承的服务类就重写查询处理部分代码即可:
ILog logger = LogManager.GetLogger(typeof(MemberMgr));
public override Dictionary<String, object> MakeSearchWhere(NameValueCollection args)
{
string keyword = "";
Dictionary<String, object> rt = new Dictionary<string, object>();
try
{
keyword = args["query"].ToString();
if (keyword.IndexOf("=") > 0)
SearchByFileds(keyword, rt);
else
SearchByKeyWords(keyword, rt);
}
catch
{
SearchByKeyWords("", rt);
}
//logger.Info(rt["sql"]);
return rt;
}
public void SearchByFileds(string flds, Dictionary<String, object> map) {
Dictionary<String, object> args = new Dictionary<string, object>();
String sql = "sInvalid=0";
Dictionary<String, string> fs = GetQueryArgs(flds);
foreach (string pname in fs.Keys) {
sql += " and ";
if ("GradYear".Equals(pname)) {
sql += "GradYear = :GradYear";
args.Add("GradYear", int.Parse(fs["GradYear"]));
}
else if ("Name".Equals(pname)) {
sql += "Name like = :Name";
args.Add("Name", "%" + fs["Name"] + "%");
}
else {
sql += pname + " = :" + pname;
args.Add(pname, fs[pname]);
}
}
map.Clear();
map.Add("sql", sql);
map.Add("args", args);
}
}
单元测试代码就很容易懂了:
public void TC01_Query() {
IMemberMgr service = (IMemberMgr)container["serviceMember"];
NameValueCollection ns = new NameValueCollection();
ns.Clear();
ns.Add("s_method", "list");
ns.Add("query", "GradYear=2007");
Console.WriteLine(service.Execute(ns));
ns.Clear();
ns.Add("s_method", "list");
ns.Add("query", "679");
Console.WriteLine(service.Execute(ns));
ns.Clear();
ns.Add("s_method", "list");
ns.Add("start", "0");
ns.Add("limit", "3");
Console.WriteLine(service.Execute(ns));
}
[Test]
public void TC02_Execute() {
IMemberMgr service = (IMemberMgr)container["serviceMember"];
NameValueCollection ns = new NameValueCollection();
string data = "{\"Name\":\"demo\",\"Mobile\":\"13922666720\",\"Email\":\"lsj@21cn.com\",\"GradYear\":2007,\"Score\":0}";
ns.Clear();
ns.Add("s_method", "save");
ns.Add("id", "");
ns.Add("data", data);
Console.WriteLine(service.Execute(ns));
ns.Clear();
ns.Add("s_method", "delete");
ns.Add("id", service.GetEntityDao().Load("Name='demo'", null)[0].id);
Console.WriteLine(service.Execute(ns));
}
结果大致如下:
......
{"totalCount":"50","data":[{"Name":"Name40","Mobile":"13922666740","GradYear":2000,"Score":0,"id":"0171abecb19a472ea2aec903bf18329d","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false},{"Name":"Name3","Mobile":"1392266673","GradYear":2003,"Score":0,"id":"029eb0082beb4285a69ec731d662b99d","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false},{"Name":"Name6","Mobile":"1392266676","GradYear":2006,"Score":0,"id":"0bd959917c6e436b9d77a35de6858dd2","sCreatedDate":"2008-01-03 20:00:46","sLastModifiedDate":"2008-01-03 20:00:46","sInvalid":false}]}
{"success":"True","data":{"Name":"demo","Mobile":"13922666720","Email":"lsj@21cn.com","GradYear":2007,"Score":0}}
{"success":"True","data":"0eaad2dea35244cb8a16657fb147cb98"}
可以看到,这正式Ext要的数据格式。
3)前端重用函数
下面这些例子仅仅用于说明原理,实际项目需要改造才能让客户满意。
首先是生成编辑板:
var rt=meta;
Ext.apply(rt,{id:entityName+'_'+meta.name});
if(true==meta.ex_IsSelect){
Ext.apply(rt,{
xtype:'combo',hiddenName:meta.name, triggerAction: 'all', editable: false,
store: new Ext.data.SimpleStore({
fields: ['retrunValue', 'displayText'],
data: meta.ex_Data}),
valueField :"retrunValue",displayField: "displayText",mode: 'local',
forceSelection: true, blankText:'请选择',emptyText:'请选择'
});
}
else if(meta.type==="string"){
if(meta.maxLength>50)
Ext.apply(rt,{xtype:'textarea'});
if(meta.ex_IsPassword===true)
Ext.apply(rt,{inputType:'password'});
}
else if(meta.type==="date")
Ext.apply(rt,{xtype:'datefield', format:meta.dateFormat });
else if(meta.type==="int"||meta.type==="float")
Ext.apply(rt,{xtype:'numberfield'});
else if(meta.type==="bool")
Ext.apply(rt,{xtype:'checkbox'});
return rt;
}
function createFormFields(entityName,metas){
var rt=[];
for(var p in metas){
if(typeof metas[p] == "string"||metas[p]['fieldLabel']==null||metas[p]['ex_IsViewField']===true) continue;
var def=createXFileldCfg(entityName,metas[p]);
rt.push(def);
}
return rt;
}
生成列表函数也类似:
var rt={
header: meta.fieldLabel,dataIndex:meta.name,
width:meta.width,
editor: new Ext.form.TextField({allowBlank: meta.allowBlank})
};
if(true==meta.ex_IsSelect){
Ext.apply(rt,{editor: new Ext.form.ComboBox({
xtype:'combo',hiddenName:meta.name, triggerAction: 'all', editable: false,
store: new Ext.data.SimpleStore({
fields: ['retrunValue', 'displayText'],
data: meta.ex_Data}),
valueField :"retrunValue",displayField: "displayText",mode: 'local',
forceSelection: true, blankText:'请选择',emptyText:'请选择'})
});
}
else if(meta.type==="date")
rt= Ext.apply(rt,{
renderer: formatDate,format:meta.dateFormat,
editor: new Ext.form.DateField({
disabledDays: meta.disabledDays,
disabledDaysText: meta.disabledDaysText
})
});
else if(meta.type==="int"||meta.type==="float")
rt= Ext.apply(rt,{
minValue:meta.minValue,maxValue:meta.maxValue,
editor: new Ext.form.NumberField()});
else if(meta.type==="bool")
rt= Ext.apply(rt,{renderer: formatBoolean, editor: new Ext.form.Checkbox({boxLabel :'是'})});
return rt;
}
function createColumns(metas){
var rt=[];
for(var p in metas){
if(typeof metas[p] == "string"||metas[p]['fieldLabel']==null||metas[p]['width']===0) continue;
var def=createXColumnCfg(metas[p]);
rt.push(def);
}
return rt;
}
其他代码请看例子,看scripts/Global.js即可!
运行说明:
1) llano集成了castle,所以你如果没有安装则先去下载:
2)在showme.Model--test.cs修改数据库连接字符串并运行测试案例创建数据表和示例数据
3)如果vs2005要运行Web(vs2008可直接运行),则要求:
Service Pack 1 can be found at http://msdn2.microsoft.com/en-us/vstudio/bb265237.aspx
Web Project support can be found at http://msdn2.microsoft.com/en-us/asp.net/aa336618.aspx
注意安装Service Pack 1要几个小时,请不要以为死机!
好了,手也打累了,下周再给大家讲讲ext和llano使用的注意事项。
今天就到这吧,祝各位周么愉快!
alex