BroFramework 中集成了 JQuery UI,因此,我们通常采用其 dialog 组件来开发 AJAX 表单。
对于多数 AJAX 表单来说,基本流程如下:
- 创建一个 dialog,并初始化大小、按钮等属性,多数情况下,仅包含“确定”、“关闭”两个按钮
- 加载一个 AJAX 表单,加载完成后显示 dialog
- 触发确定按钮后,执行 ajax 提交并回显
如下面的两个例子所示:
/**
 * 示例1:AJAX 表单提交
 */
function createFolder(url) {
    var id = "dlgCmsCreateFolder";
    var dlg = $j("#"+id);
    if ( dlg.length == 0 ) {
        $j("<div id='"+id+"' class='body'></div>").appendTo("body");
        dlg = $j("#"+id);
        dlg.dialog({
            autoOpen:false, modal:true, title:"创建文件夹", width:420, resizable:false, 
            buttons: {
                "提交": function() {
                    var fm = $j("#fmCreateFolder");
                    showProcessingDlg();
                    $j.post(fm[0].action, fm.serialize(), function(msg){
                        hideProcessingDlg();
                        showAjaxMsg(msg, function(){
                            dlg.dialog("close");
                            try { parent.reloadNode($j("#folderId")[0].value); }catch(e){}
                            location.reload();
                        })
                    })
                },
                "关闭": function() { $j(this).dialog("close"); }
            } // end of buttons
        }) // end of dlg.dialog
    }
    dlg.load( url, function(msg){
        if (/{"error"/.test(msg)) return;
        dlg.dialog('open');
        $j("#fmCreateFolder #name").focus()
    });
}
/**
 * 示例2:AJAX 附件上传
 */
function createFile(url) {
    var id = "dlgCmsCreateFile";
    var dlg = $j("#"+id);
    if ( dlg.length == 0 ) {
        $j("<div id='"+id+"' class='body'></div>").appendTo("body");
        dlg = $j("#"+id);
        dlg.dialog({
            autoOpen:false, modal:true, title:"上传附件", width:450, resizable:false, 
            buttons: {
                "上传": function() {
                    if ( $j("#fmCreateFile").find(":file").val() ) {
                        if ( $j.browser.fileApiSupported ) {
                            $j("#fmCreateFile").submit();
                        } else {
                            showProcessingDlg(function(){
                                $j("#fmCreateFile").submit();
                                return false;
                            });
                            $j(this).dialog("close");
                        }
                    }
                },
                "关闭": function() { $j(this).dialog("close"); }
            } // end of buttons
        }) // end of dlg.dialog
    }
    dlg.load( url, function(msg){
        if (/{"error"/.test(msg)) return;
        dlg.dialog('open');
        // AJAX 上载,如果支持XMLHttpRequest Level 2,则显示上传进度
        $j("#fmCreateFile").ajaxForm({
            dataType: 'json',
            beforeSend : function() {
                if ( $j.browser.fileApiSupported ) {
                    dlg.closest("div.ui-dialog").find(".ui-dialog-titlebar, .ui-dialog-buttonpane, .ui-dialog-titlebar-close, .ui-resizable-handle").hide();
                    $j("#fmCreateFile").html(' <br/><div class="progressbar"><div class="progress-label" style="float:right; margin-top:5px">Uploading...</div></div><br/> ');
                    $j("#fmCreateFile .progressbar").progressbar({value: 0});
                }
            },
            uploadProgress: function(event, position, total, percentComplete) {
                $j("#fmCreateFile .progressbar").progressbar( "value", percentComplete );
                $j("#fmCreateFile .progress-label").text( percentComplete + '%' );
            },
            complete: function(xhr) {
                if ( $j.browser.fileApiSupported ) {
                    dlg.closest("div.ui-dialog").find(".ui-dialog-titlebar, .ui-dialog-buttonpane, .ui-dialog-titlebar-close, .ui-resizable-handle").show();
                    dlg.dialog("close");
                }
            }
        })
    });
}
上面的代码乍一看,麻烦得很,尤其是每次写AJAX表单时,总是要拷贝粘贴大量代码,因此,BroFWK提供了一个简化的API:$.formDialog,简化后的代码如下所示:
/**
 * 示例1:AJAX 表单提交
 */
function createFolder(url) {
    $j.formDialog({
        modal:true, title:"创建文件夹", width:420, resizable:false, 
        id: "dlgCmsCreateFolder", url: url,
        "submit.close": true, "submit.reload": true,
        "submit.after": function() {
            try {
                parent.reloadNode($j("#folderId").val());
            } catch(e) {}
        }
    })
}
/**
 * 示例2:AJAX 附件上传
 */
function createFile(url) {
    $j.formDialog({
        modal:true, title:"上传附件", width:450, resizable:false, 
        id: "dlgCmsCreateFile", url: url, cache: false,
        "submit.close": true, "submit.reload": true,
        "submit.before": function() {
            return $j("#fmCreateFile").find(":file").val();
        }
    })
}
和简化前的代码示例相比,代码量整整减少了2/3。
下面对 $.formDialog 做个简单的介绍:
- 封装了jquery ui的dialog组件,仅接收一个 options 参数,参数中的内容除了下面的注释所写的内容外,全部和 jqui 的 dialog 兼容
- 参数 options 说明:
 @param options.id  HTML容器的ID,如果不存在,则会创建一个DIV
 @param options.html  表单的HTML文本内容,当容器不存在、并创建容器时将本参数的值放到 DIV 中
 @param options.url  表单的URL,通过AJAX加载
 @param options.cache  是否保存表单URL的Cache,默认为true
 @param options.progressbar  如果有附件域、且浏览器支持XMLHttpRequest Level 2,是否显示上传进度条,默认为真
 @param options.'buttons.default' 是否生成默认的确定(提交)、关闭(取消)按钮,默认为 true,即如果没有按钮Ok、Close按钮,则自动生成
 @param options.'buttons.OK'  默认确定按钮的名称,默认取i18n配置 jqui.dalog.button.OK,即“确定”,如果为 false 则不显示
 @param options.'buttons.CLOSE' 默认关闭按钮的名称,默认取i18n配置 jqui.dalog.button.CLOSE,即“关闭”,如果为 false 则不显示
 @param options.'submit.ajax'  是否采用ajax的方式提交,默认为true。
 如果提交后,需要根据上传的数据渲染或者重定向到其他页面(表单的 target 属性设为 _blank),则应设为false,此时ProcessingDlg显示3秒后自动关闭。
 @param options.'submit.close' 提交成功后是否自动关闭
 @param options.'submit.reload' 提交成功后是否自动刷新当前页面
 @param options.'submit.before' 点击“确定”前的事件,接收参数 form,如果返回 false 则中断提交
 @param options.'submit.after' 点击“确定”并且ajax提交成功后的事件,接收参数 message(服务器返回的对象)
 @params options.buttons   jqui dialog 的按钮参数,用法不变,但其回调函数中可以调用 this.submit()、this.close()、this.form() 来直接提交、关闭、获取表单对象
- 需要注意的是,附件上传使用的是 jqeury 插件 ajaxForm,由于浏览器的支持程度不同,因此在控制器中需要采用下面的写法来接收附件、返回结果:
 def importExcel() {
 // 解析 Excel 文件
 Map result = bropen.toolkit.utils.office.MSExcelUtils.readAll(request)
 
 // 处理数据
 if ( !result.error ) {
 ....
 }
 
 // 渲染结果
 if ( isAjax() ) {
 render(result as JSON)
 } else {
 render(text:"<textarea>${result?.encodeAsJSON()}</textarea>", contentType:"text/html", encoding:"UTF-8")
 }
 }
 控制器中,还可以通过 request.getFileMap() 来获得所有上传的附件,或者使用默认的 attachmentService.save 将附件保存到 Domain 实体中。
 此外,提交过程默认为ajax的,如果上传附件后需要渲染一个预览界面,则应该设置 ‘submit.ajax’ 参数为 false,即禁用 ajax 方式提交表单。