使用a4j:支持更新模型和视图,为下一个按钮/提交操作做好准备

时间:2011-05-16 22:23:13

标签: jsf richfaces seam ajax4jsf

问题

我们有一个基于swing的前端用于企业应用程序,现在正在为它实现一个(现在更简单)JSF / Seam / Richfaces前端。

某些页面包含的字段在编辑时应导致其他字段因此而更改。我们需要立即向用户显示此更改(即他们不必按下按钮或任何其他内容)。

我已成功使用h:commandButton并将onchange="submit()"添加到导致其他字段更改的字段中。这样,表单提交在他们编辑字段时发生,其他字段也会因此而更新。

这在功能上运行良好,但特别是当服务器处于显着负载(经常发生)时,表单提交可能需要很长时间,我们的用户在此期间继续编辑字段,然后在响应时恢复<{1}}请求已呈现。

为了解决这个问题,我希望能够实现以下目标:

  1. 在编辑字段后,如果需要,仅处理该字段,并且仅重新呈现仅修改字段(以便用户进行的任何其他编辑)在此期间不要迷路)。
  2. 按下按钮后,所有字段将被正常处理并重新呈现。
  3. (不稳定)解决方案

    好吧,我认为首先展示一下我的页面可能是最容易的。请注意,这只是一个摘录,有些页面会有很多字段和许多按钮。

    onchange="submit()"

    更改<a4j:form id="mainForm"> ... <a4j:commandButton id="calculateButton" value="Calculate" action="#{illustrationManager.calculatePremium()}" reRender="mainForm" /> ... <h:outputLabel for="firstName" value=" First Name" /> <h:inputText id="firstName" value="#{life.firstName}" /> ... <h:outputLabel for="age" value=" Age" /> <h:inputText id="age" value="#{life.age}"> <f:convertNumber type="number" integerOnly="true" /> <a4j:support event="onchange" ajaxSingle="true" reRender="dob" /> </h:inputText> <h:outputLabel for="dob" value=" DOB" /> <h:inputText id="dob" value="#{life.dateOfBirth}" styleClass="date"> <f:convertDateTime pattern="dd/MM/yyyy" timeZone="#{userPreference.timeZone}" /> <a4j:support event="onchange" ajaxSingle="true" reRender="age,dob" /> </h:inputText> ... </a4j:form> 的值会导致age的值在模型中更改,反之亦然。我使用dobreRender="dob"来显示模型中更改的值。这很好。

    我也在使用全局队列来确保AJAX请求的排序。

    但是,在我点击页面上的其他位置或按Tab键或其他内容之前,reRender="age,dob"事件不会发生。当用户输入一个值onchange,然后按age 而不点击页面上的其他位置或按Tab键时,这会导致问题。

    calculateButton事件似乎确实首先发生,因为我可以看到onchange的值发生了变化,但是当执行dob请求时,这两个值会被还原。

    所以,最后,问题是:有没有办法确保模型和视图在calculateButton请求发生之前完全更新,以便它不会还原它们?既然我使用的是AJAX队列,为什么还没有发生呢?

    解决方法

    有两种策略可以解决这个限制,但它们都需要在facelet代码中膨胀,这可能会让其他开发人员感到困惑并导致其他问题。

    解决方法1:使用a4j:支持

    此策略如下:

    1. calculateButton属性添加到ajaxSingle="true"
    2. 将带有calculateButton属性的a4j:support标记添加到ajaxSingle="true"
    3. 第一步确保firstName不会覆盖calculateButtonage中的值,因为它不再处理它们。不幸的是,它的副作用是它不再处理dob。添加第二步以通过在按下firstName之前处理firstName来抵消此副作用。

      请注意,虽然可能有20多个字段,例如calculateButton。填写表单的用户可能会向服务器发出20多个请求!就像我之前提到的那样,这也可能让其他开发人员感到困惑。

      解决方法2:使用进程列表

      感谢@DaveMaple和@MaxKatz建议这个策略,它如下:

      1. firstName属性添加到ajaxSingle="true"
      2. calculateButton属性添加到process="firstName"
      3. 第一步实现与第一个解决方法相同,但具有相同的副作用。这一次,第二步确保在按下时calculateButton处理firstName

        同样,请记住,虽然可能有20多个字段(如calculateButton)包含在此列表中。就像我之前提到的那样,这也可能让其他开发人员感到困惑,特别是因为列表必须包含一些字段而不包括其他字段。

        年龄和DOB设置者和获取者(以防它们是问题的原因)

        firstName

4 个答案:

答案 0 :(得分:0)

你的bean的范围是什么?当执行该按钮时,它是一个新请求,如果您的bean在请求范围内,那么之前的值将消失。

答案 1 :(得分:0)

  

所以,最后,问题是:有没有办法确保模型和视图在进行calculateButton请求之前完全更新,以便它不会还原它们?

您可以执行的操作是在您启动ajax请求之前禁用提交按钮,直到您的ajax请求完成为止。这将有效地阻止用户按下提交按钮,直到它结算:

<a4j:support 
    event="onblur" 
    ajaxSingle="true" 
    onsubmit="jQuery('#mainForm\\:calculateButton').attr('disabled', 'disabled');" 
    oncomplete="jQuery('#mainForm\\:calculateButton').removeAttr('disabled');" />

这种方法另外有用的一件事是,如果您在最终用户身上显示“ajaxy”图像,这样他们就会直观地了解即将发生的事情。您也可以在onsubmit方法中显示此图像,然后将其隐藏在oncomplete。

修改 问题可能只是您需要将process属性添加到a4j:commandButton。此属性按ID指定应参与模型更新阶段的组件:

<a4j:commandButton 
    id="calculateButton" 
    value="Calculate" 
    action="#{illustrationManager.calculatePremium()}" 
    process="firstName"        
    reRender="mainForm" />

答案 2 :(得分:0)

我想我可以提供另一种解决方法。

我们应该在js级别上有两个标志:

var requestBlocked = false;
var requestShouldBeSentAfterBlock = false;

h:inputText元素阻止模糊的ajax请求:

<h:inputText id="dob" onblur="requestBlocked = true;" ...
如果a4j:commandButton为false,

requestBlocked会发送请求:

<a4j:commandButton id="calculateButton"
    onkeyup="requestShouldBeSentAfterBlock = requestBlocked;
        return !requestBlocked;" ...
如果a4j:support为真,

requestShouldBeSentAfterBlock会发送请求:

<a4j:support event="onchange" 
    oncomplete="requestBlocked = false; 
        if (requestShouldBeSentAfterBlock) { 
            requestShouldBeSentAfterBlock = false;
            document.getElementById('calculateButton').click();
        }" ...

因为oncomplete块在重新渲染所有需要的元素之后工作,所以事情将以所需的顺序工作。

答案 3 :(得分:0)

看起来有一个原因在于getter / setter中的某个地方。 例如,其中一种可能性:当没有ajaxSingle=true和点击计算按钮时。所有值都设置为模型,因此调用setAgesetDateOfBirth。可能会发生这种情况,以便在setDateOfBirth之前调用setAge

仔细观察setAge方法,它实际上将日期重置为年初,即使日期已经过了正确的年份。

我建议先简化逻辑。例如,为年份和出生日分别设置了断开连接的字段,并检查问题是否仍然可以重现,以找到重现所需的最低条件。

同样从用户体验的角度来看,常见的做法是执行以下操作,因此一旦用户停止输入,事件就会触发:

<a4j:support event="onkeyup"
             ajaxSingle="true"
             ignoreDupResponses="true"
             requestDelay="300"
             reRender="..."/>