2010-08-13 3 views
16

Я ищу способ ввода значений из фрагмента (#) URL-адреса в bean-компонент (JSF), так же, как и параметр запроса значения вводятся. Я использую плагин jAQuery для закладок Ben Alman (http://benalman.com/projects/jquery-bbq-plugin/) для создания фрагментов URL. Я надеялся, что пользовательские шаблоны регулярных выражений из prettyFaces могут быть способом решения моей проблемы, но до сих пор я не увенчался успехом.Извлеките фрагмент (хэш) из URL-адреса и введите значения в bean-компонент

(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

Веб-приложение генерирует следующий URL-адрес, где параметры перечислены после хэша (#). Идея состоит в том, чтобы ajax основал Bookmarkable URL. Поэтому каждый раз, когда я нажимаю на элемент, который изменяет состояние системы , значение отправляется на сервер через ajax и URL-адрес после перезаписывания хеша. После может быть от 1 до 3 параметров, количество параметров не обязательно.

Моя цель состоит в том, когда пользователь закладки на URL (с хэш) и чем пересмотрит сохраненной страницы, страница должна вводить правильные значения в систему и визуализировать страницу в предыдущем состоянии (как query- параметр).

Ниже, у меня есть регулярное выражение, которое поймает все параметры после хэша.

//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-адреса на стороне сервера?

ответ

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> 

Вот как соответствующий боб выглядеть следующим образом:

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 относительно новое и не поддерживается старыми браузерами. В случае отсутствия поддержки браузера (undefinied и т. Д.), Вы должны проверить window.location.hash с интервалами, используя вместо этого setInterval(). Приведенный выше пример кода должен, по крайней мере, дать хорошее начало. Он работает как минимум на FF3.6 и IE8.

+1

Ничего себе, это просто потрясающе, спасибо за обмен. Теперь мне нужно взглянуть на JSF 2. – clacke

+1

Для браузеров IE6/7 рассмотрим плагин jQuery Hashchange. См. Также http://stackoverflow.com/questions/5911899/ie6-7-back-forward-button-dont-change-window-location-hash – BalusC

3

Вот самый надежный способ извлечь фрагмент из синтаксически допустимого URL/URI.

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

Как вводить это в бина будет зависеть от того, что на стороне сервера рамки вы используете, и делаете ли вы инъекции с использованием XML или аннотации, или делать это programmaticaly.

+0

Спасибо за вашу идею. Я ищу способ поймать URL-адрес, когда есть перезагрузка страницы (GET), а не getFragment(), правильно разобрать его, чтобы получить значения и установить соответствующие переменные. – tchoesang

+0

Все в порядке, что метод существует, но браузеры не отправляют информацию об фрагменте на сервер. Это можно увидеть, зарегистрировав HTTP-трафик. – pcjuzer

+1

Хотя это технически правильно, этот ответ практически и принципиально неверен. Фрагмент, установленный веб-браузером [** не будет **] (http://en.wikipedia.org/wiki/Fragment_identifier#Processing), поступает на веб-сервер. – BalusC

1

Фрагмент не может быть замечен со стороны сервера, к нему могут быть доступны только клиентские скрипты. Обычно это делается так, что серверная сторона генерирует непараметрированную страницу, которая затем модифицируется скриптами в соответствии с параметрами фрагмента. Сценарии могут создавать запросы AJAX с параметрами запроса, где AJAX-ответы генерируются JSF с использованием beans, контролируемых параметрами.

Если вы абсолютно хотите, чтобы серверная сторона имела доступ к параметрам фрагмента при рендеринге самой страницы, вам нужно перезагрузить страницу с параметрами в качестве параметров запроса.

EDIT: Для того, чтобы перезагрузить страницу, которую вы могли бы использовать этот код:

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; 
} 
+0

Я видел эту технику, используемую facebook, где параметры фрагмента преобразуются в параметры запроса при перезагрузке страницы. Я хотел бы попробовать эту идею. Как преобразовать URL-адрес, содержащий фрагмент, в URL-адрес с параметрами запроса при перезагрузке страницы? i.e. http://url.com/#a=1&b=2 -> http://url.com/?a=1&b=2 – tchoesang

+0

Добавлен пример кода. Однако я бы предложил вместо этого написать код для анализа параметров и сделать соответствующий запрос AJAX для восстановления состояния закладки. Кроме того, мой пример кода слишком прост для ваших нужд, он не проверяет, например. дублируются параметры и, следовательно, сталкиваются с проблемами, когда пользователь начинает нажимать, и вы снова добавляете вещи в фрагмент. – clacke

+0

Пример кода неуклюжий. Просто 'window.location.hash' дает вам уже фрагмент и' window.location.search' строку запроса. Прочитайте документы ['window.location'] (https://developer.mozilla.org/en/window.location). – BalusC

1

Смотреть этот вопрос и особенно ответов: JSP Servlet anchor

  • Фрагмент нельзя увидеть на стороне сервера.
  • Вы можете извлечь фрагмент на стороне клиента, преобразовать его и отправить в сервер через Ajax.
+0

Отличная ссылка. Особенно ссылка [ajaxpatterns] (http://ajaxpatterns.org/Unique_URLs). – clacke

0

Большое спасибо за ваши идеи & специально BalusC (много для вас). Моим окончательным решением была небольшая мутация, и я хотел бы поделиться с вами всеми.

Поскольку я использую RichFaces 3.3.3, это как не используя jsf1.2, следовательно, не <f:ajax>

В обычной ситуации вы, как ожидается, нажмите на ссылку с HREF, указывая на внутреннюю ссылку <a id='internalLink'href='#internalLink'> , Я использую много предварительно упакованных компонентов, где не каждый кликабельный элемент является ссылкой <a>. Это может быть любой элемент html. Было очень полезно использовать закладочный jQuery-плагин Ben Alman (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> 

В богатых слоях это классный способ вызова методов сеттера из браузера с параметром. Обратите внимание, что sendURLFragment рассматривается как метод JS с 5 параметрами, и значения передаются на сервер (что очень круто).

<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> 

Сделайте то же самое, когда вы скопируете новый URL-адрес и загрузите страницу.

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); 
} };