使用jsf2.0ajax库调用一个方法

calling a method with jsf 2.0 ajax library

本文关键字:一个 方法 jsf2 0ajax 调用 使用      更新时间:2023-09-26

我正在开发一个应用程序,用户将在该应用程序中评估一件艺术品,并在进行过程中填写评审表。他们可能会在表单上花费大量时间,所以我想自动为用户保存,比如每5分钟保存一次。我希望我可以使用javascript并设置5分钟的计时器,然后让它通过ajax基本上执行整个表单,我可以将数据保存到数据库中,以防用户断开连接等。不幸的是,我似乎找不到任何方法来做到这一点。用数据更新模型不是问题,但我不知道如何让它调用方法(类似于提交时的操作)。我不想也不需要它重新呈现任何东西,只需要调用一个方法来保存数据。我该怎么做?

实施解决方案的问题

我尝试实现隐藏命令链接的解决方案,但我得到了一些非常奇怪的行为。我不确定是什么原因造成的。首先,介绍了实施的一些背景。Form#1创建一个bean(无作用域)并将其放入flash中,然后重定向到Form#2。Form#2是我写的一个大表单,我想在这里实现自动保存。表单#2有一个ViewScoped bean。在这个bean的PostConstruct中,它从flash中检索值,并填充一个属性字段。到目前为止还不错。这在没有javascript的情况下非常有效。我可以按命令按钮提交表格,一切都很好。然而,当我引入javascript时,当它执行时,我从变量中得到一个空指针异常,该变量本应由PostConstruct从flash中填充。这个javascript是如何干扰的?一旦我用对象填充了视图作用域bean的属性,它是否从flash作用域中删除应该无关紧要,对吧?仅供参考,如果我只删除javascript代码,并保留所有其他内容,当我按下按钮提交时,它会恢复正常工作。

表格#1

    <h:form>
    ... bunch of form objects ...
    <h:commandButton "Start New" action="#{someRequestScopedBean.someMethod"/>
    </h:form>

someRequestScopedBean的代码。方法:

    public String someMethod() {
        // bunch of logic here
        FacesContext.getCurrentInstance()
            .getExternalContext()
                .getFlash()
                    .put("myFlashObj", myFlashObj);
        return "form2?faces-redirect=true";
    }

表单2:中使用的视图范围bean

    @ManagedBean
    @ViewScoped
    public class someViewScopedBean { 
        //bunch of properties here 
        @PostConstruct
        public void initialize() {
            this.myObject = (MyObject) FacesContext.getCurrentInstance()
                           .getExternalContext()
                           .getFlash()
                           .get("myFlashObject");
        public void saveDraft() { 
            // save to database
        }
    }

表格2页码:

    <h:outputScript library="javax.faces" name="jsf.js"/>
    <h:form id="myForm">
       ... whole bunch of fields here ... 
       ... real button for user to submit ... 
       <h:commandButton value="Submit myForm" 
                        action="#{someViewScopedBean.save}" />
       ... hidden button for auto-save by javascript ... 
       <h:commandLink id="hiddenSaveDraft" style="display: none;" 
                      action="#{someViewScopedBean.saveDraft}" >
              <f:ajax execute="@form" />
       </h:commandLink> 
       <script>
           function saveDraft() {
                document.getElementById('qForm:hiddenSaveDraft').onclick();
                window.setTimeout('saveDraft()',15000);
           }
           saveDraft();
       </script>
    </h:form>

由于您写下了Javascript作为标记之一,我将假设您在应用程序中包含客户端Javascript代码。

首先使用隐藏的CCD_ 3或隐藏的<h:inputText/>(在需要保存有关某个变量的一些信息的情况下,我使用后者)

在您提交的表格中添加以下内容之一:

<h:commandButton style="display:none" id="clickme">
  <f:ajax execute="@form">
</h:commandButton>
<h:inputText style="display:none" id="changeme">
  <f:ajax execute="@form">
</h:inputText>

在您的Javascript代码中,添加此代码以单击或更改:

setInterval(function() { 
  document.getElementById("clickme").onclick();
}, 3000); // update every 3 seconds
setInterval(function() { 
  document.getElementById("changeme").onchange();
}, 3000); // update every 3 seconds

两者都会很好用。只要确保它们在您正在更新的表单中即可。

我找到了最终的解决方案,以及第一次实现时出现问题的原因。这与javascript何时被启动有关。脚本代码是在我放置<script></script>块的确切位置激发的,那是在页面完全加载之前,可能是在DOM完成之前。这导致了各种恶劣的情况,包括@PostConstruct的重复调用。

我通过使用javascript事件侦听器在页面完全加载时启动来修复它。这一点很重要,因为我使用的是facelets模板,并且我没有访问<h:body onload=属性的权限。监听器是一个有用且优雅的解决方案。脚本块可以放置在页面中的任何位置。以下是脚本块的样子:

    <script>
    function saveDraft() {
        document.getElementById('qForm:saveDraft').onclick();       
        window.setTimeout(saveDraft,300000);
    }
    function initSaveTimer(e) {
        window.setTimeout(saveDraft,300000);
    }
    this.addEventListener("load",initSaveTimer,true);
    </script>

这将每5分钟调用一次隐藏按钮来保存表单。