最近需要做一個動態表單管理,因為以前沒什么經驗,所以自己做了一個小demo研究了一下,現將自己的整體思路再從頭順一下,也順便記錄一下這次的學習經驗,呵呵
1、 首先,要想實現動態表單的管理,自己首先想到的是不就是執行sql語句對數據庫表的字段進行增刪改嘛,后來想想並不這么簡單,因為要關乎到以后用戶添加數據時表單的動態生成,以及還要生成動態的js表單驗證,所以我首先新建了一張表記錄了某張表所有字段信息,如下:
CREATE TABLE `sys_user_fields_info` (
`field_id` varchar(255) NOT NULL DEFAULT '' COMMENT '字段編號',
`table_name` varchar(255) NOT NULL DEFAULT '' COMMENT '表名稱',
`is_key` int(11) NOT NULL DEFAULT '0' COMMENT '0:非主鍵 1:主鍵',
`field_name` varchar(20) NOT NULL DEFAULT '' COMMENT '字段名',
`field_name_cn` varchar(20) NOT NULL DEFAULT '' COMMENT '字段中文名',
`field_type` varchar(10) NOT NULL DEFAULT '' COMMENT '字段類型',
`min_length` int(11) DEFAULT NULL COMMENT '最小字段長度',
`max_length` int(11) DEFAULT NULL COMMENT '最大字段長度',
`decimal_point` int(11) DEFAULT NULL COMMENT '小數點',
`is_null` int(11) NOT NULL DEFAULT '0' COMMENT '0:可空 1:不可空',
`default_val` varchar(50) DEFAULT '' COMMENT '默認值',
`context` varchar(255) DEFAULT '' COMMENT '描述',
`data_format` varchar(255) DEFAULT 'text' COMMENT '數據要求格式',
`display` varchar(50) NOT NULL DEFAULT 'text' COMMENT '頁面顯示方式',
`display_context` varchar(255) DEFAULT NULL COMMENT '頁面顯示方式對應內容',
`on_form` int(11) NOT NULL DEFAULT '0' COMMENT '是否在界面顯示標志:0顯示 1不顯示',
`state` int(11) NOT NULL DEFAULT '0' COMMENT '0、已啟用 1、已禁用 2、已刪除',
`create_date` datetime DEFAULT NULL COMMENT '創建日期'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='表字段信息';
2、 有了這些字段,缺點是增加了一張表,對數據庫的操作變多了,但是對數據表及后續表單的生成及驗證是有好處的,若只有一個原始表並且直接操作生不成表單或者只能生成最簡單的表單,並不能滿足用戶的需求,好了,有了上面這張表跟需要動態生成的表就可以完成基本的表元數據的管理了,就是執行一些sql語句而已,字段因為要求做成可以做成一次添加多個字段,所以界面做成了動態生成記錄行讓用戶輸入字段信息,最后一次全部保存,保存的時候我的做法是刪除了字段信息表中該表所有字段信息重新添加,而需要維護的表也是刪除了字段重新建的(這點不好,因為如果表中有數據的話那數據已經丟失了,可以考慮將drop column改成update,但是update的時候也有一個問題就是,如果字段原來的類型是varchar,現在改成了int那數據也會出問題的,還可以考慮先將數據導出,再導進來,這樣也可以會出現數據格式不兼容的問題,總之這個要看具體場合采取對應的解決方案了,因為時間緊,現在做成了刪除)。
完成對數據表的元數據的維護后,接下來就到了重點的地方了,那就是表單的生成了,表單的生成主要是借助於上面這張表,表中記錄了字段的類型、長度、是否可空、界面顯示方式、時候生成表單等一系列信息,借助這個就可以生成表單了,也不是很難吧,呵呵,有句話說得好那就是“難事必做於易”,再困難的事到最后也是由一些簡單的問題組合而成的,因為現在用的是struts2做的,所以在界面上就需要進行一系列判斷了,代碼如下:
<form id="addUserForm" class="form-horizontal">
<table cellspacing=10 cellpadding=0 border=0 width=100% height=100%>
<tbody id="tby">
<s:iterator id="field" status="st" value="fieldList">
<s:if test="#field.onForm==0">
<s:if test='#field.fieldName!="user_id"'>
<tr>
<td align="right" id="<s:property value="#field.fieldName"/>_nameCn"><s:property value="#field.fieldNameCn"/>:</td>
<td>
<!-- 顯示方式為text -->
<s:if test='#field.display=="text"'>
<input type="text" name="<s:property value="#field.fieldName"/>" size="20" class="input"/>
</s:if>
<!-- 顯示方式為radio-->
<s:elseif test='#field.display=="radio"'>
<input type="radio" name="<s:property value="#field.fieldName"/>" value='<s:property value="#field.displayContext.substring(0,1)"/>' class="input" checked="checked"/><s:property value="#field.displayContext.substring(2,3)"/>
<input type="radio" name="<s:property value="#field.fieldName"/>" value='<s:property value="#field.displayContext.substring(4,5)"/>'/><s:property value="#field.displayContext.substring(6,7)"/>
</s:elseif>
<!-- 顯示方式為date-->
<s:elseif test='#field.display=="date"'>
<input type="text" name="<s:property value="#field.fieldName"/>" size="20" class="Wdate input" onfocus="WdatePicker()" readonly="readonly"/>
</s:elseif>
<!-- 顯示方式為textarea-->
<s:elseif test='#field.display=="textarea"'>
<textarea rows="3" cols="19" name="<s:property value="#field.fieldName"/>" class="input"></textarea>
</s:elseif>
<!-- 顯示方式為select-->
<s:elseif test='#field.display=="select"'>
<select name="<s:property value="#field.fieldName"/>" class="input" type="select">
<option value="" selected="selected">---請選擇---</option>
<s:iterator id="add" status="st" value="#field.displayContext.split(';')">
<option value="<s:property value="add"/>"><s:property value="add"/></option>
</s:iterator>
</select>
</s:elseif>
<!-- 顯示方式為checkbox,取值跟顯示問題-->
<s:elseif test='#field.display=="checkbox"'>
<s:iterator id="pre" status="st" value="#field.displayContext.split(';')">
<input type="checkbox" <s:if test="#st.index==0">class="input"</s:if> name='<s:property value="#field.fieldName"/>' value='<s:property value="pre"/>'/><s:property value="pre"/>
</s:iterator>
</s:elseif>
<!-- 如果非空,添加*號標注 -->
<s:if test="#field.isNull==1">
<font color="red">*</font>
</s:if>
</td>
</tr></s:if></s:if></s:iterator></tbody></table> </form>
可以生成不同種類的表單,如下:
3、現在生成表單了,但是用戶在添加數據時還要攔截非法數據,這就要生成動態驗證代碼了,這還得需要借助於上面的表,我的做法是在生成表單的時候在每個表單域的后面都有幾個隱藏域用來記錄該表單域的驗證規則,使用id標記,我這里是根據字段的name值再加上相應的標志動態生成的標記,如下:
<!-- 驗證規則 :begin -->
<!-- 1:是否為空 --><input type="hidden" id="<s:property value="#field.fieldName"/>_isNull" value="<s:property value="#field.isNull"/>" />
<!-- 2:類型 -->
<input type="hidden" id="<s:property value="#field.fieldName"/>_type" value="<s:property value="#field.fieldType"/>" />
<!-- 3:最小長度 --><input type="hidden" id="<s:property value="#field.fieldName"/>_minLength" value="<s:property value="#field.minLength"/>" />
<!-- 4:最大長度 -->
<input type="hidden" id="<s:property value="#field.fieldName"/>_maxLength" value="<s:property value="#field.maxLength"/>" />
<!-- 5:數據要求格式 -->
<input type="hidden" id="<s:property value="#field.fieldName"/>_dataFormat" value="<s:property value="#field.dataFormat"/>" />
<!-- 驗證規則 :end -->
4、現在既然有驗證規則了,那保存的時候只需要取出隱藏表單域的值然后再跟實際值比較就可以了,如下://表單驗證
function validateAddUserForm(){
var isTrue = true;
var info = "";
$(".input").each(function(){
//表單域左邊的中文提示內容
var nameCn = $("#" + $(this).attr("name") + "_nameCn").html();
var display = nameCn.substring(0,nameCn.length-1);
var inputName = $(this).attr("name");//表單域name
var inputType = $(this).attr("type");
var inputValue = $.trim($(".input[name='"+inputName+"']").val());//表單域value
if(inputType=="radio"||inputType=="checkbox"){
inputValue = $(":"+inputType+"[name='" + inputName + "'][checked]").val();
}else if(inputType=="select"){
inputValue = $.trim($(inputType + "[name='" + inputName + "'] option[selected]").val());
}
//驗證規則
var isNull = $("#" + inputName + "_isNull").val();//是否為空
var type = $("#" + inputName + "_type").val();//數據要求格式
var dataFormat = $("#" + inputName + "_dataFormat").val(); var minLength = $("#" + inputName + "_minLength").val();//最小長度
var maxLength = $("#" + inputName + "_maxLength").val(); //驗證是否為空
if(isNull==1){//1不允許空
if((inputValue=="")||(inputValue==undefined)){
isTrue = false;
info += display + "不能為空!" + "\n";
}
}
if(isTrue){//驗證整數(int)、小數(double)、email(email)、電話(phone)
if(inputValue!=""){
if(dataFormat=="int"){
if((!$.isNumeric(inputValue))||(!($.isNumeric(inputValue))&&(inputValue.indexOf(".")!=-1))){
isTrue = false;
info += display + "須為整數!\n";
}
}else if(dataFormat=="double"){
if(!$.isNumeric(inputValue)){
isTrue = false;
info += display + "須為小數!\n";
}
}else if(dataFormat=="email"){
if(!inputValue.match(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/)){
isTrue = false;
info += display + "格式不正確!\n";
}
}else if(dataFormat=="phone"){
if(!inputValue.match(/^1[3|4|5|8][0-9]\d{4,8}$/)){
isTrue = false;
info += display + "格式不正確!\n ";
}
}
}
}
if(isTrue){//以上驗證全通過,再去驗證長度
var inputLength = inputValue.length;
if(((inputLength<minLength)||(inputLength>maxLength))&&(type!="datetime")){
isTrue = false;
info += display + "長度須在(" + minLength + "," +maxLength+ ")之間!\n";
}
}
});
if(!isTrue){
alert(info);}
return isTrue;}
5、現在基本功能已經完成了,但是現在還有一個遺留的問題就是查詢動態表數據的時候,因為字段都是動態生成的,查詢的時候不能查詢指定的某些字段,你也不知道表中都有哪些字段,只能查詢所有字段,而且字段信息表中存有字段的中文信息,所以說查詢出所有的字段是沒問題,但是如果字段太多,頁面樣式肯定會出問題,這就需要限制只顯示某幾列字段,再添加一個查看詳細功能。
6、還有一個問題是,動態生成表單時,因為后台不可能知道前台都有生成了哪些字段,只能是用戶添加數據並通過驗證后,在后台獲取所有表單的name以及這些name所對應的值,我現在還不清楚使用struts2怎么獲取,所以先用了request獲取,然后將name跟value對應好后添加到數據表中。
Enumeration enumr = request.getParameterNames();
while (enumr.hasMoreElements()) {
String param = enumr.nextElement().toString();
String[] values = request.getParameterValues(param);
StringBuffer buf = new StringBuffer();
if (values.length == 1) {
buf.append(values[0].toString());
} else if (values.length > 1) {
for (String value : values) {
buf.append(value).append(";");
}
}
params.put(param, buf.toString());}