技术开发 频道

借助 Ajax 自动保存 JSF 表单: 第 2 部分

  修改 JSF 阶段侦听器

  在本系列的 第 1 部分,介绍过一个名为 AutoSaveListener 的类,它可用于处理 Ajax 请求、从 JSF 组件树检索所提交的数据。在本系列中,表单数据会存储于数据存储库中,而非直接打印出来。

  将当前的视图数据保存于存储库

  清单 13 给出了 AutoSaveListener 的 saveCurrentView() 方法,它可将当前 JSF 视图的表单数据存储到存储库中。第一步是调用 getCurrentInstance(),它返回 faces 上下文,然后使用 getViewRoot() 获得 JSF 视图的根元素。之后,saveCurrentView() 会创建新的数据地图并调用 DataMapRepository 类的 saveValues() 方法以将 JSF 组件的值存储到数据地图。为了安全起见,该地图对象会传递给 Collections.unmodifiableMap(),而它反过来会返回包装器地图,此地图可在任何试图修改其状态时抛出异常。这个不可修改的地图用包装器对象的 setDataMap() 方法即可存储到存储库。

  清单 13. 将当前 JSF 视图的表单数据存储到存储库

package autosave;
...
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
...
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class AutoSaveListener implements PhaseListener {
    ...
    
public void saveCurrentView() {
        
// Get the faces context of the current request.
        FacesContext ctx
= FacesContext.getCurrentInstance();
        
// Get the root component of the current view.
        UIViewRoot root
= ctx.getViewRoot();
        
// Create a new data map.
        Map
<String, Object> dataMap = new HashMap<String, Object>();
        
// Store the component values into the data map.
        DataMapRepository.saveValues(root, dataMap);
        
// Make the data map unmodifiable.
        dataMap
= Collections.unmodifiableMap(dataMap);
        
// Get the managed bean instance wrapping the data repository.
        RepositoryWrapper wrapper
= RepositoryWrapper.getManagedBean(ctx);
        
// Store the data map into the repository.
        wrapper.setDataMap(ctx, dataMap);
        
// Stop request processing.
        ctx.responseComplete();
    }
    ...
}

  也许您还记得,在 第 1 部分 中,表单的自动保存不应该干扰应用程序逻辑。数据模型必须要能更新,且自动保存之后不应调用任何动作。保存了输入组件的值之后,saveCurrentView() 必须要停止请求处理以便自动保存数据(即部分填充了的表单的实际用户输入)不会存储到应用程序数据模型。因此,saveCurrentView() 会调用 faces 上下文的 responseComplete() 方法以表示 JSF 请求处理生命周期必须中断。

  处理 JSF 阶段事件

  正如第 1 部分所介绍的,表单数据必须在 JSF 验证阶段,即 JSF 组件的所有值都已经由 JSF 框架转变和验证之后才保存。除了检验 PhaseEvent 事件的阶段 ID 之外,afterPhase() 方法(如 清单 14 所示)会获得 Ajax-Request 头以判断当前请求是否会自动保存表单数据。此头在 AutoSaveScript.js 文件的 submitFormData() 函数中设置,这在第 1 部分中已经介绍过了。如果此头的值为 Auto-Save,那么 afterPhase() 方法就会调用 saveCurrentView() 以将 JSF 组件的验证值存储到数据存储库。

  如果标记此 Ajax 请求的头不存在,那么就表明用户一定是单击了 Web 表单的提交按钮。在这种情况下,afterPhase() 就会检查 faces 上下文是否包含任何消息,如果没有消息就表明由用户提交的数据有效,原因是 JSF 验证阶段过后才会调用 afterPhase()。如果表单数据有效,针对当前用户和视图组合的任何之前保存的数据都会被 clearDataMap() 从存储库中清除。

  清单 14. 侦听 JSF 阶段事件

package autosave;
...
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
...
public class AutoSaveListener implements PhaseListener {
    ...
    
public void afterPhase(PhaseEvent e) {
        
if (!e.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS))
            return;
        FacesContext ctx
= e.getFacesContext();
        Map headers
= ctx.getExternalContext().getRequestHeaderMap();
        
if ("Auto-Save".equals(headers.get("Ajax-Request"))) {
            
// Auto-Save Request. Save data into the repository.
            saveCurrentView();
        }
else {
            
// The user must have clicked the Submit button.
            
if (!ctx.getMessages().hasNext()) {
                
// There are no error messages.
                
// This means the final submitted data is valid and
                
// the temporary auto-saved data is no longer needed.
                RepositoryWrapper wrapper
                    
= RepositoryWrapper.getManagedBean(ctx);
                wrapper.clearDataMap(ctx);
            }
        }
    }
    ...
}
0
相关文章