我有一个div
,应该表现得像JQuery dialog
。在此div
中,我输入了一些数据,尝试对其进行验证,并将其保存到数据库中。用户点击时会打开对话框我遇到了3个问题:
1。 JQuery ID的选择器
基本上我们知道如果我们试图找到
<xp:div id="addingDialog">
</xp:div>
使用像这样var dialog = $('#addingDialog')
的JQuery选择器 - 它会给我们没有结果,因为XPage中的id是动态计算的。所以我决定使用类(styleClasses,如果你愿意)声明它,就像这样
<xp:div styleClass="addingDialog">
</xp:div>
JQuery是这样的:var dialog = $('.addingDialog')
。似乎工作,虽然不是很好。
2。点击应该验证它的按钮并制作所有后端内容并不起作用:(
不幸的是,当我点击&#34;添加&#34;按钮(保存的按钮)没有任何反应 - 即使输入正确,即使验证失败并且没有保存任何内容,对话框也会关闭。
所以我找到了一个解决方案 - 不要使用对话框和JQuery。
但这不是正确的解决方案,至少不是这种情况。但即便出现另一个问题 - 验证
有2个对话框:一个用于添加,另一个用于编辑(JQuery不允许只有一个具有不同按钮功能的对话框),两个都有验证,我必须输入一些东西进行编辑才能添加新!最初我认为XPage中的验证工作原理是这样的 - 当用户在相应的 div 检查中点击对应按钮(添加或编辑)所有 inputText 时如果它是正确的 - 验证成功并且后端行动发生。问题是 - 如何让它像这样工作?结果是页面上的每个inputText检查。我不希望它像这样工作(截图不使用JQuery)This is what I see on a page。如果我在编辑对话框中输入任何值,请单击&#34; +添加部分&#34;一切正常。也许完全是因为对话中的这个saving actions
没有发生?因为我只打开了一个对话框,但验证&#34;看到&#34;其他隐藏的输入是空的,所以验证失败了吗?这是我的代码
<xp:div styleClass="dialogAddPart">
<xp:table>
<xp:tr>
<xp:td><xp:label value="Title:" /></xp:td>
<xp:td>
<xp:inputText
styleClass="doc_field_textinput" id="input_part_title" type="text" size="40" disableClientSideValidation="true" required="true">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
</xp:inputText>
<xp:message id="message15" for="input_part_title"
rendered="true" showDetail="false" showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="Total:"/></xp:td>
<xp:td>
<xp:inputText id="input_tsnb_all"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true" size="40" >
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message2" for="input_tsnb_all"
rendered="true" showDetail="false" showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="build-works"/></xp:td>
<xp:td>
<xp:inputText type="text" size="40" id="input_tsnb_build_work"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message3"
for="input_tsnb_build_work" rendered="true" showDetail="false"
showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="equipment"/></xp:td>
<xp:td>
<xp:inputText id="input_tsnb_equipment"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true" size="40" >
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message5" for="input_tsnb_equipment"
rendered="true" showDetail="false" showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="other costs"/></xp:td>
<xp:td>
<xp:inputText type="text" size="40" id="input_tsnb_other_costs"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true" >
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message6"
for="input_tsnb_other_costs" rendered="true" showDetail="false"
showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="including tax"/></xp:td>
<xp:td>
<xp:inputText type="text" size="40" id="input_tsnb_pir"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message7" for="input_tsnb_pir"
rendered="true" showDetail="false" showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td><xp:label value="return sum"/></xp:td>
<xp:td>
<xp:inputText type="text" size="40"
id="input_tsnb_return"
disableClientSideValidation="true"
styleClass="doc_field_textinput" required="true">
<xp:this.validators>
<xp:validateRequired
message="#{javascript:return('This field is required')}">
</xp:validateRequired>
</xp:this.validators>
<xp:this.converter>
<xp:convertNumber pattern="0.000"></xp:convertNumber>
</xp:this.converter>
</xp:inputText>
<xp:message id="message8"
for="input_tsnb_return" rendered="true" showDetail="false"
showSummary="true"
style="font-style:italic;background_field-color:rgb(217,234,235);border-color:rgb(102,102,102)">
</xp:message>
</xp:td>
</xp:tr>
<xp:tr>
<xp:td colspan="2" style="padding-top: 15px">
<xp:button id="save_part_btn" value="+Add part" style="float:right;">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:
//Backend code
}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:td>
</xp:tr>
</xp:table>
</xp:div>
用于编辑的代码是相同的,期望使用相应的部分(后端)检索值,并且id具有前缀_edit
。保存更改按钮还没有任何后端操作。
我的JQuery是:
$(document).ready(function() {
/*
Ignore it
$('.partTableContent').hide();
$('.expandButton').click(function() {
// .parent() selects the A tag, .next() selects the P tag
$(this).closest('tr').next(' tr').find('div.partTableContent').slideToggle(750);
});
*/
var dialogAddPartDiv = $('.dialogAddPart');
$('.addButton').click(function()
{
dialogAddPartDiv.dialog('open');
});
dialogAddPartDiv.dialog(
{
create: function (event, ui) {
$(".ui-corner-all").css('border-bottom-right-radius','8px');
$(".ui-corner-all").css('border-bottom-left-radius','8px');
$(".ui-corner-all").css('border-top-right-radius','8px');
$(".ui-corner-all").css('border-top-left-radius','8px');
$(".ui-dialog").css('border-bottom-left-radius','0px');
$(".ui-dialog").css('border-bottom-right-radius','0px');
$(".ui-dialog").css('border-top-left-radius','0px');
$(".ui-dialog").css('border-top-right-radius','0px');
$('.ui-dialog-titlebar-close').css('margin', '-25px -20px 0px 0px').css('border', 'solid 2px').css('border-radius', '15px').css('border-color', '#05788d');
$('.ui-dialog-titlebar-close').css('width', '25px').css('height', '25px');
},
autoOpen: false,
modal: true,
beforeClose : function(event)
{
if(!confirm("Part won't be saved. Continue"))
{
return false;
}
else
{
}
},
width:600,
resizable: false
});
var dialogEditPartDiv = $('#dialogEditPart');
$('.editButton').click(function()
{
dialogEditPartDiv.dialog('open');
});
dialogEditPartDiv.dialog(
{
create: function (event, ui) {
$(".ui-corner-all").css('border-bottom-right-radius','8px');
$(".ui-corner-all").css('border-bottom-left-radius','8px');
$(".ui-corner-all").css('border-top-right-radius','8px');
$(".ui-corner-all").css('border-top-left-radius','8px');
$(".ui-dialog").css('border-bottom-left-radius','0px');
$(".ui-dialog").css('border-bottom-right-radius','0px');
$(".ui-dialog").css('border-top-left-radius','0px');
$(".ui-dialog").css('border-top-right-radius','0px');
$('.ui-dialog-titlebar-close').css('margin', '-25px -20px 0px 0px').css('border', 'solid 2px').css('border-radius', '15px').css('border-color', '#05788d');
$('.ui-dialog-titlebar-close').css('width', '25px').css('height', '25px');
},
autoOpen: false,
modal: true,
beforeClose : function(event)
{
if(!confirm("Changes won't be saved. Continue?"))
{
return false;
}
else
{
}
},
width:600,
resizable: false
});
});
希望问题很清楚。我只想打开对话框,验证输入并最终执行后端代码。事实上,我希望它被隐藏起来并成为对话,而不是在页面上看到丑陋的div。感谢
答案 0 :(得分:1)
你所要求的并不是一件小事,需要大量的JSF理解 - 阶段监听,部分执行,部分刷新,faces-config.xml,JavaScript等。
如果你想要实现它,你必须承受很多并继续阅读...
通过faces-config.xml
配置阶段侦听器。 faces-config.xml
将如下所示:
<faces-config>
<lifecycle>
<phase-listener>demo.ValidationPhaseListener
</phase-listener>
</lifecycle>
...
在开始之前,我们首先创建一个Helper类,以便共享一个常用的方法 - 以及其他几个 - 将在以后的其他地方使用:
package demo;
public enum Helper {
;
private static final String ON_SUCCESS_REFRESH_ID_PARAM = "onSuccessRefreshId";
public static void setResponseErrorHeader(FacesContext facesContext, PhaseId phaseId) {
HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
response.setHeader("Application-Error", phaseId.toString());
}
public static void applyOnSuccessRefreshId(FacesContext facesContext) {
if (!AjaxUtil.isAjaxPartialRefresh(facesContext)) {
throw new UnsupportedOperationException();
}
String refreshId = (String) facesContext.getExternalContext().getRequestParameterMap().get(ON_SUCCESS_REFRESH_ID_PARAM);
if (refreshId != null) {
((FacesContextEx) facesContext).setPartialRefreshId(refreshId);
}
}
public static void applyOnSuccessRefreshId(FacesContext facesContext, ActionEvent event) {
if (!AjaxUtil.isAjaxPartialRefresh(facesContext)) {
throw new UnsupportedOperationException();
}
Parameter param = getComponentParam(event.getComponent(), ON_SUCCESS_REFRESH_ID_PARAM);
if (param != null) {
((FacesContextEx) facesContext).setPartialRefreshId(param.getValue());
}
}
}
验证阶段监听器类将负责设置响应标头值,我们可以在验证阶段失败时查找 。从框架的角度来看,验证错误并不对应于500错误,但始终为200.如果没有这种区别,我们就不知道是否关闭对话框。
package demo;
public class ValidationPhaseListener implements PhaseListener {
private static final long serialVersionUID = 1L;
@Override
public PhaseId getPhaseId() {
return PhaseId.PROCESS_VALIDATIONS;
}
@Override
public void beforePhase(PhaseEvent phaseEvent) {
}
@Override
public void afterPhase(PhaseEvent phaseEvent) {
FacesContext facesContext = phaseEvent.getFacesContext();
if (facesContext.getMessages().hasNext()) {
Helper.setResponseErrorHeader(facesContext, getPhaseId());
}
}
}
为了鼓励重复使用和良好的组织,最好设置一个静态库,该库将加载每个使用对话框的XPage。 在这种特殊情况下,我使用bootstrap模式,因此逻辑最适合。 JS文件是这样的:
var Helper = {
isBadRequest : function(xhr) {
return xhr.getResponseHeader("Application-Error") !== null;
},
jquery : function(query) {
if (typeof query !== "string") {
return query;
}
return $(query.replace(/:/g, "\\3A"));
},
modal : function(id, options, additionalOptions) {
var m = this.jquery(id);
var addOpts = additionalOptions || {};
if ("hide" === options) {
var successRefreshId = m.data("success-cid");
if (addOpts.eventId && addOpts.execId && successRefreshId) {
XSP
.partialRefreshPost(
addOpts.refreshId || null,
{
execId : addOpts.execId,
params : {
"$$xspsubmitid" : addOpts.eventId,
"onSuccessRefreshId" : successRefreshId
},
onComplete : "if (!Helper.isBadRequest(arguments[1].xhr)) { hub.modal('"
+ id
+ "', 'hide', { hide: false }) }",
onError : 'console.log(arguments[0])'
});
return;
}
} else {
if (addOpts.successRefreshId) {
m.data("success-cid", addOpts.successRefreshId);
}
var eventId = m.data("reset-eid");
if (eventId) {
var componentId = m.data("reset-cid");
addOpts.hide = function() {
XSP.partialRefreshPost(componentId || null, {
immediate : true,
execId : eventId,
params : {
"$$xspsubmitid" : eventId
}
});
};
}
}
for (addOpt in addOpts) {
switch (addOpt) {
case "show":
case "shown":
case "hide":
case "hidden":
var evName = addOpt + ".bs.modal";
if (addOpts[addOpt] instanceof Function) {
m.one(evName, addOpts[addOpt]);
} else {
m.off(evName);
}
break;
}
}
m.modal(options);
}
};
我知道,模态方法中有很多事情要做。它是复杂的,以涵盖特定的边缘情况 - 至少对我设计它的方式。你不需要过多关注它。你想知道的是,它与我在XPages一侧定义为我的模态模板的自定义控件一起工作。
模态是通过自定义控件定义的,简单&#34;简单&#34;再利用:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:div
styleClass="modal inmodal ${empty compositeData.animationClass ? 'fade' : 'in'}"
role="dialog">
<xp:this.attrs>
<xp:attr name="id" value="#{compositeData.clientId}" />
<xp:attr name="data-reset-eid" value="#{compositeData.resetEventId}"
rendered="#{not empty compositeData.resetEventId}" />
<xp:attr name="data-reset-cid" value="#{compositeData.resetComponentId}"
rendered="#{not empty compositeData.resetComponentId}" />
<xp:attr name="tabindex" value="-1" />
<xp:attr name="aria-hidden" value="true" />
</xp:this.attrs>
<div class="modal-dialog ${compositeData.size}">
<div
class="modal-content ${empty compositeData.animationClass ? '' : compositeData.animationClass}">
<xp:div styleClass="modal-header">
<xp:callback facetName="header" rendered="${compositeData.headerCustom}" />
<xp:text rendered="${not compositeData.headerCustom}">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">
<xp:text value="${msg.app.close}" />
</span>
</button>
<xp:text tagName="i"
styleClass="fa #{compositeData.headerIcon} modal-icon" rendered="#{not empty compositeData.headerIcon}" />
<xp:panel tagName="h4" styleClass="modal-title"
rendered="#{not empty compositeData.headerTitle}">
<xp:text value="#{compositeData.headerTitle}" />
</xp:panel>
<xp:panel tagName="small" styleClass="font-bold"
rendered="#{not empty compositeData.headerDescription}">
<xp:text value="#{compositeData.headerDescription}" />
</xp:panel>
</xp:text>
</xp:div>
<div class="modal-body">
<xp:callback facetName="body" />
</div>
<xp:div styleClass="modal-footer">
<xp:callback facetName="footer" />
</xp:div>
</div>
</div>
</xp:div>
</xp:view>
其.xsp-config
定义:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<faces-config-extension>
<namespace-uri>http://www.ibm.com/xsp/custom</namespace-uri>
<default-prefix>xc</default-prefix>
</faces-config-extension>
<composite-component>
<component-type>layoutModal</component-type>
<composite-name>layoutModal</composite-name>
<composite-file>/layoutModal.xsp</composite-file>
<composite-extension>
<designer-extension>
<in-palette>true</in-palette>
</designer-extension>
</composite-extension>
<property>
<property-name>clientId</property-name>
<property-class>string</property-class>
<property-extension>
<required>true</required>
</property-extension>
</property>
<property>
<property-name>size</property-name>
<property-class>string</property-class>
<property-extension>
<designer-extension>
<editor>com.ibm.workplace.designer.property.editors.comboParameterEditor</editor>
<editor-parameter>modal-sm
modal-lg</editor-parameter>
</designer-extension>
</property-extension>
</property>
<property>
<property-name>animationClass</property-name>
<property-class>string</property-class>
</property>
<property>
<property-name>headerIcon</property-name>
<property-class>string</property-class>
</property>
<property>
<property-name>headerTitle</property-name>
<property-class>string</property-class>
</property>
<property>
<property-name>headerDescription</property-name>
<property-class>string</property-class>
</property>
<property>
<property-name>headerCustom</property-name>
<property-class>boolean</property-class>
</property>
<property>
<property-name>resetEventId</property-name>
<property-class>string</property-class>
</property>
<property>
<property-name>resetComponentId</property-name>
<property-class>string</property-class>
</property>
</composite-component>
</faces-config>
除了一堆属性之外,它定义的是一系列您想要使用的模式,具体取决于模态的复杂性。如果你能够发现它我为模态定义的id
是固定类型 - 而不是动态的。它不是鼓励使用固定而不是动态,但通过评估所有情况,我意识到固定id在很多情况下也有一些帮助。
是时候通过在页面上使用自定义控件将事物放在一起了。 让我们从链接开始。
我们使用链接&#34;准备&#34;模态。首先,使用手术精确度非常重要,以便既能提高效率,又不必担心使用模态时。 JavaScript发生在客户端,除非状态以某种方式与服务器端同步,否则服务器端不知道这一点。这一点很重要,因为除非明确告诉事件处理程序会因为操作而执行页面的完整刷新 - 这意味着整个页面会像第一次那样重建 - 至少从JavaScript的角度来看,因此失败跟踪开放式模式。
因此,无论何时您想使用模态,并且需要准备其内容,使用部分执行和部分刷新都很重要。实际上,如果你从不使用部分刷新和执行,你应该认真问自己为什么。
<xp:link id="linkOpenDialog">
<xp:eventHandler event="onclick" submit="true"
execMode="partial" refreshMode="partial" refreshId="modalDemoBody"
action="#{myBean.myAction}"
onComplete="Helper.modal('#modal-account', { backdrop: 'static', keyboard: false })" />
</xp:link>
如果你看一下上面的例子,我设置的内容读取1)execMode="partial"
执行操作时忽略页面上的所有其他评估,2)refreshMode="partial" refreshId="modalDemoBody"
执行操作时唯一的元素将在页面上更新的不是整个页面,而是modalDemoBody
及其所有子项(即模态正文内容),3)action="#{myBean.myAction}"
我的方法可能会在显示前准备我的模态, 4)onComplete="Helper.modal('#modal-account'...)"
将被调用的JavaScript方法 - 它在静态库中,还记得吗? - 这实际上会激发模态。如果您不需要准备模态,则可以跳过所有这些步骤,只需将onComplete
中的方法移动到链接的onclick事件(例如<xp:link id="linkOpenDialog" onclick="Helper..."
)
现在是页面上模态的代码。我们使用自定义组件,我们定义了我实际上不会使用的动态ID,以及我们在链接中引用的clientId
。您可以定义其他可选属性,例如标题标题 - 或者您可以通过指定自定义构面完全revisit
整个区域,并且在示例的情况下,resetEventId
(此一个)在底部调用eventHandler并且它是帮助清除表单状态的例子,例如1)你打开模态,2)你提交了内容,3)验证失败,4)你关闭模式,从而使服务器端的表单处于不一致状态,根据您在下一页上单击的操作,可能会咬你。 (仅当保存操作未关闭模态时才会触发此操作)
然后有一个正面 - xp:key="body"
- 和一个页脚 - xp:key="footer"
- 来定义你的按钮。
<xc:layoutModal id="modalDemo" clientId="modal-demo"
headerTitle="Demo" resetEventId="#{id:eventResetForm}">
<xp:this.facets>
<xp:panel xp:key="body" binding="#{modalDemoBody}" id="modalDemoBody">
<!-- Here goes your content -->
</xp:panel>
<xp:text xp:key="footer" disableOutputTag="true">
<button type="button" class="btn btn-white"
data-dismiss="modal">
<xp:text value="Close" />
</button>
<xp:button id="buttonSaveModal" value="Save" styleClass="btn-primary">
<xp:eventHandler event="onclick" submit="true"
execMode="partial" execId="modalDemo" refreshMode="partial"
refreshId="modalDemoBody" actionListener="#{myBean.saveModalDemo}"
onComplete="if (!Helper.isBadRequest(arguments[1].xhr)) { hub.modal('#modal-demo', 'hide', { hide: false }) }">
<xp:this.parameters>
<xp:parameter name="onSuccessRefreshId"
value="#{id:allIsWell}" />
</xp:this.parameters>
</xp:eventHandler>
</xp:button>
<xp:eventHandler id="eventResetForm"
submit="false"
action="#{javascript:myBean.resetForm(modalDemoBody)}" />
</xp:text>
</xp:this.facets>
</xc:layoutModal>
<xp:div id="allIsWell">
</xp:div>
在保存按钮事件处理程序中,我们仔细选择行为。我们再次使用部分执行和刷新。部分执行设置为execMode="partial" execId="modalDemo"
,这意味着验证将仅在页面的模态部分内发生。使用refreshMode="partial" refreshId="modalDemoBody"
进行部分刷新设置,因为我们不想重新加载整个页面,从而破坏了模态状态,但它具体到足以返回表单,如果发生了可能的验证错误。
现在,而不是action
param,我实际上利用了actionListener
,因为它允许我发送请求附加参数(我认为无论如何都可以通过使用javascript来完成签名,例如然后传递参数)。我在这里定义的是一个id的条件行为,我想要作为动作的结果刷新,也就是说,如果验证失败刷新模态的主体,但如果成功,则刷新页面的其他一些组件(在它是<xp:div id="allIsWell">
)的例子。 <xp:parameter name="onSuccessRefreshId"
,其名称与ON_SUCCESS_REFRESH_ID_PARAM
类中的静态字符串Helper
匹配。现在,如果我们的方法也没有错误地评估,那么动作中会发生的是动态更改刷新ID。如果不是,我们也会以稍后可以理解的方式使该操作无效。
public void saveModalDemo(ActionEvent event) {
// Your logic goes here
boolean allIsWell = false;
if (allIsWell) {
Helper.applyOnSuccessRefreshId(facesContext, event);
} else {
Helper.setResponseErrorHeader(facesContext, event.getPhaseId());
}
}
Helper.applyOnSuccessRefreshId
将读取onSuccessRefreshId
参数,并使用新的参数重新路由eventHandler中定义的原始refreshId。你去了,你有条件刷新!
然后,只有这样,我们才会使用onComplete="if (!Helper.isBadRequest(arguments[1].xhr)) { hub.modal('#modal-demo', 'hide', { hide: false }) }"
定义的参数来关闭对话框,这样可以确定一件事是否可以实际关闭对话框。如果它找到响应标题,表示存在错误 - 就像验证错误一样 - 它不会关闭对话框,否则它会。
我写的代码多于解释。我本应该写一篇关于它的博客文章。无论如何,通过选中this link out,您可以获得有关该流程的更多信息。然而,这里的答案是对解决问题的第一次尝试的改进。