16

我正在寻找一种将 URL 片段 (#) 中的值注入 bean(JSF) 的方法,就像注入查询参数值一样。我正在使用 Ben Alman 的 Bookmarkable jQuery 插件 ( http://benalman.com/projects/jquery-bbq-plugin/ ) 创建 URL 片段。我希望来自 prettyFaces 的自定义正则表达式模式可以成为解决我的问题的一种方法,但直到现在我一直没有成功。

http://ocpsoft.com/docs/prettyfaces/snapshot/en-US/html_single/#config.pathparams.regext

我想在这里定义我的情况,如果有人有想法,我很想尝试一下。

我正在使用
RichFaces:3.3.3,
Spring:3.0.2.RELEASE,
Hibernate:3.5.3-Final,
JSF:2.0.2-FCS,
PrettyFaces:3.0.1

Web 应用程序生成以下 URL,其中参数列在哈希 (#) 之后。这个想法是有一个基于 ajax 的 Bookmarkable URL。所以每次我点击一个改变系统状态的元素时,值都会通过ajax和重写hash后的URL发送到服务器。哈希后可以有 1 到 3 个参数,参数个数是可选的。

我的目标是,当用户为 URL 添加书签(带有哈希)并重新访问保存的页面时,页面应该将正确的值注入系统并在以前的状态下可视化页面(如查询参数)。

下面,我有一个正则表达式,可以捕获散列后的所有参数。

//URL:   
http://localhost:8080/nymphaea/workspace/#node=b48dd073-145c-4eb6-9ae0-e1d8ba90303c&lod=75e63fcd-f94a-49f5-b0a7-69f34d4e63d7&ln=en

//Regular Expression:    
\#(\w*\=(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}))|\&(\w*\=(\w{8}-\w{4}-\w{4}-\w{4}-\w{12}))|\&(\w*\=\w{2})

我知道有些网站如何将 URL 片段发送到服务器端逻辑中,

无论如何将URL片段中的值注入服务器端bean?

4

5 回答 5

8

您可以通过window.onhashchange填充隐藏表单的输入字段来执行此操作,该隐藏表单在输入字段更改时异步提交。

这是 Facelets 页面的启动示例:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>SO question 3475076</title>
        <script>
            window.onload = window.onhashchange = function() {
                var fragment = document.getElementById("processFragment:fragment");
                fragment.value = window.location.hash;
                fragment.onchange();
            }
        </script>
        <style>.hide { display: none; }</style>
    </h:head>
    <h:body>
        <h:form id="processFragment" class="hide">
            <h:inputText id="fragment" value="#{bean.fragment}">
                <f:ajax event="change" execute="@form" listener="#{bean.processFragment}" render=":showFragment" />
            </h:inputText>
        </h:form>
        <p>Change the fragment in the URL. Either manually or by those links:
            <a href="#foo">foo</a>, <a href="#bar">bar</a>, <a href="#baz">baz</a>
        </p>
        <p>Fragment is currently: <h:outputText id="showFragment" value="#{bean.fragment}" /></p>
    </h:body>
</html>

下面是合适的 bean 的样子:

package com.stackoverflow.q3475076;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.AjaxBehaviorEvent;

@ManagedBean
@RequestScoped
public class Bean {

    private String fragment;

    public void processFragment(AjaxBehaviorEvent event) {
        // Do your thing here. This example is just printing to stdout.
        System.out.println("Process fragment: " + fragment);
    }

    public String getFragment() {
        return fragment;
    }

    public void setFragment(String fragment) {
        this.fragment = fragment;
    }

}

就这样。

请注意,该onhashchange事件相对较新,旧浏览器不支持。在没有浏览器支持(未定义等)的情况下,您希望window.location.hash每隔一段时间检查一下setInterval()。上面的代码示例至少应该给出一个很好的开场白。它至少适用于 FF3.6 和 IE8。

于 2010-08-13T13:34:26.897 回答
3

这是从语法有效的 URL / URI 中提取片段的最可靠方法。

 URI uri = new URI(someString);
 String fragment = uri.getFragment();

如何将其注入 bean 将取决于您使用的服务器端框架,以及您是使用 XML 或注释进行注入,还是以编程方式进行。

于 2010-08-13T08:29:04.273 回答
1

Watch this question and especially the answers: JSP Servlet anchor

  • Fragment cannot be seen on server-side.
  • You can extract fragment on client-side, convert it and send to the server via Ajax.
于 2010-08-13T10:33:04.693 回答
1

从服务器端看不到片段,只能通过客户端脚本访问。通常的做法是服务端生成一个非参数化的页面,然后由脚本根据片段参数进行修改。脚本可以使用查询参数发出 AJAX 请求,其中 AJAX 响应由 JSF 使用由参数控制的 bean 生成。

如果您绝对希望服务器端在呈现页面本身时可以访问片段参数,则需要使用参数作为查询参数重新加载页面。

编辑:要重新加载页面,您可以使用以下代码:

if (window.location.hash != '') {
  var newsearch = window.location.search;
  if(newsearch != '') {
    newsearch += '&';
  }
  newsearch += window.location.hash.match(/#?(.*)/)[1];
  window.location.hash = '';
  window.location.search = newsearch;
}
于 2010-08-13T09:25:25.480 回答
0

非常感谢您的想法,特别是 BalusC(非常感谢您)。我的最终解决方案是一个轻微的突变,我想与大家分享。

由于我使用的是richfaces 3.3.3,所以就像使用jsf1.2一样,因此没有<f:ajax>

在常规情况下,您应该单击带有指向内部链接的 href 的链接<a id='internalLink'href='#internalLink'>。我正在使用许多预先打包的组件,其中并非每个可点击的项目都是一个链接<a>。它可以是任何 html 元素。使用 Ben Alman 的 Bookmarkable jQuery 插件( http://benalman.com/projects/jquery-bbq-plugin/)非常有用

请注意,在下面,onclick 我可以以编程方式将片段的值设置为键/值,并在下面的代码中用新值替换旧值jQuery.bbq.pushState(state,2)(或者我可以简单地在旧值后面添加jQuery.bbq.pushState(state))。

在我的情况下,ws->md->nd是一个分层数据,其中一个就足够了,它的父级在服务器上计算,所以我用子级值替换父级值。

<ui:repeat value="#{workspaceController.modulesForWorkspace}" var="item">  
 <a4j:commandLink onclick="var state = {}; state['md'] = '#{item.uuid}';
  jQuery.bbq.pushState( state, 2 );"    
  action="#{workspaceController.setSelectedModule}"
  reRender="localesList" 
  oncomplete="selectNavigationElement(this.parentNode>
    <f:param name="selectedModule" value="#{item.uuid}"/>
  </a4j:commandLink>
</ui:repeat>

在richfaces 中,这是从浏览器中使用参数调用setter 方法的一种很酷的方式。请注意,它sendURLFragment被视为具有 5 个参数的 JS 方法,并将值传递给服务器(这非常酷)。

<h:form id="processFragment" prependId="false" style="display: none">
  <h:inputText id="fragment" value="#{workspaceController.selectedWorkspace}">                                  
   <a4j:support event="onchange"/>
  </h:inputText>    
<a4j:jsFunction name="sendURLFragment" reRender="workspaces">
 <a4j:actionparam name="ws" assignTo="#{workspaceController.selectedWorkspace}"/>
 <a4j:actionparam name="md" assignTo="#{workspaceController.selectedModule}"/>
 <a4j:actionparam name="nd" assignTo="#{workspaceController.selectedContentNode}"/>
 <a4j:actionparam name="ld" assignTo="#{workspaceController.selectedLOD}"/>
 <a4j:actionparam name="ln" assignTo="#{workspaceController.contentNodeTreeLocale}"  
 converter="localeConverter"/>                              
</a4j:jsFunction>
</h:form>

当您复制粘贴新网址并加载页面时,请执行此操作。

window.onload = function(){
if(window.location.hash != ""){  
    //Parse the fragment (hash) from a URL, deserializing it into an object            
    var deparamObj = jQuery.deparam.fragment();
    var ws= '', md= '', nd= '', ld= '', ln = '';            
    jQuery.each(deparamObj, function(key, value) {              
        switch(key){
            case 'ws':
                ws = value;
                break;
            case 'md':
                md = value;
                break;
            case 'nd':
                nd = value;
                break;
            case 'ld':
                ld = value;
                break;
            case 'ln':
                ln = value;
                break;
            default:
                break;          
        }

    });
    sendURLFragment(ws,md,nd,ld,ln);
}   };  
于 2010-08-20T09:47:11.517 回答