上篇文章我们感觉到用Ext组件做界面是很强大和灵活的,同时我们也发现了很多地方我们在硬编码,比如列表的列头设置,还有编辑框各字段等,在实际项目中一定让人十分疲惫!有时可能因为不注意少打一个分号而跟踪半天等。所以接下下我想介绍一下使用元数据定义,用Ext自带的灵活方法动态生成合适的列表和编辑框的做法,这在以前的C/S架构下一直是程序员追求的,在如今Ext利用js的动态灵活特性成为可能了。
先看看我重构后的代码是这样:
Ext.onReady(

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开头,代码大致如下:
namespace demo.data


{
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字符串,当你要输出一个数组时,一句话就能搞定,看看下面的示例就明白了:
String sql = "Mobile like :Mobile";
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已经把思路说得很清楚了。在实际项目中通常有上百张表,所以我们就把那段方法判断得代码提到了基类中,而继承的服务类就重写查询处理部分代码即可:

public class MemberMgr : BaseService<TMember, TMember>, IMemberMgr
{
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);

}
}
单元测试代码就很容易懂了:
[Test]

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)前端重用函数
下面这些例子仅仅用于说明原理,实际项目需要改造才能让客户满意。
首先是生成编辑板:

function createXFileldCfg(entityName,meta)
{
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;
}
生成列表函数也类似:

function createXColumnCfg(meta)
{

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,所以你如果没有安装则先去下载:
Installer version: CastleProject-1.0-RC3.msi
下载测试驱动并安装
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