java - Recupere el fragmento(hash) de una URL e inyecte los valores en el bean
javascript jsf (5)
Estoy buscando una forma de inyectar valores desde el fragmento (#) de una URL en un bean (JSF), de la misma manera en que se inyectan los valores de parámetros de consulta. Estoy usando el plugin jQuery de Ben Alman para marcar ( http://benalman.com/projects/jquery-bbq-plugin/ ) para crear los fragmentos de URL. Esperaba que los patrones de expresiones regulares personalizadas de prettyFaces pudieran ser una forma de resolver mi problema, pero hasta ahora no he tenido éxito.
( http://ocpsoft.com/docs/prettyfaces/snapshot/en-US/html_single/#config.pathparams.regext )
Me gustaría definir aquí mi situación y si alguien tiene una idea, me encantaría probarla.
estoy usando
RichFaces: 3.3.3,
Primavera: 3.0.2.
Hibernate: 3.5.3-Final,
JSF: 2.0.2-FCS,
PrettyFaces: 3.0.1
La aplicación web genera, siguiendo el tipo de URL donde los parámetros se enumeran después de un hash (#). La idea es tener una URL de Bookmarkable basada en ajax. Así que cada vez que hago clic en un elemento que cambia el estado del sistema, el valor se envía al servidor a través de ajax y la URL después de que se reescriba el hash. Puede haber de 1 a 3 parámetros después del hash, el número de parámetros es opcional.
Mi objetivo es que, cuando el usuario marca la URL (con hash) y vuelve a visitar la página guardada, la página debe inyectar los valores correctos en el sistema y visualizar la página en el estado anterior (como parámetro de consulta).
A continuación, tengo una expresión regular que captura todos los parámetros después del hash.
//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})
Sé que hay sitios web que de alguna manera envían el fragmento de URL a la lógica del lado del servidor,
- http://maps.yahoo.com/#mvt=m&lat=36.952736&lon=-95.84758&zoom=11&tt=starbucks&tp=1&ioride=us
- http://www.cbc.ca/video/#/Shows/Death_Comes_to_Town/ID=1365210427
¿Hay alguna forma de inyectar valores de los fragmentos de URL en los beans del lado del servidor?
El fragmento no se puede ver desde el lado del servidor, solo se puede acceder a él mediante scripts del lado del cliente. La forma en que se hace generalmente es que el lado del servidor genera una página no parametrizada, que luego se modifica mediante scripts de acuerdo con los parámetros del fragmento. Los scripts podrían realizar solicitudes AJAX con parámetros de consulta, donde JSF genera las respuestas AJAX utilizando beans controlados por los parámetros.
Si absolutamente desea que el lado del servidor tenga acceso a los parámetros del fragmento al renderizar la propia página, debe volver a cargar la página con los parámetros como parámetros de consulta.
EDITAR: Para recargar la página puedes usar este código:
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;
}
Esta es la forma más confiable de extraer un fragmento de una URL / URI sintácticamente válida.
URI uri = new URI(someString);
String fragment = uri.getFragment();
La forma de inyectar esto en un bean dependerá de la estructura del lado del servidor que esté utilizando, y de si está realizando la inyección utilizando XML o anotaciones, o mediante programación.
Muchas gracias por tus ideas y especialmente por BalusC (muchas felicitaciones para ti). Mi solución final fue una ligera mutación y me gustaría compartir con todos ustedes.
Ya que estoy usando richfaces 3.3.3, es como usar jsf1.2, por lo tanto no <f:ajax>
En una situación convencional, se espera que haga clic en un enlace con un href que apunta a un enlace interno <a id=''internalLink''href=''#internalLink''>
. Estoy usando muchos componentes preempaquetados en los que no todos los elementos seleccionables son un enlace <a>
. Puede ser cualquier elemento html. Fue muy útil utilizar el plugin jQuery de Ben Alman ( http://benalman.com/projects/jquery-bbq-plugin/ )
Tenga en cuenta a continuación que, en un clic, puedo establecer el valor del fragmento como clave / valor mediante programación y en el siguiente código sustituyo el valor anterior por un nuevo valor jQuery.bbq.pushState(state,2)
(o simplemente podría haber agregado detrás del valor antiguo jQuery.bbq.pushState(state)
).
En mi caso, ws->md->nd
es un dato jerárquico donde basta con que uno de ellos y sus padres se calculen en el servidor, por lo que sustituyo el valor padre por valor hijo.
<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>
En richfaces, esta es una forma genial de llamar a los métodos de configuración desde el navegador con un parámetro. Tenga en cuenta que sendURLFragment
se trata como un método JS con 5 parámetros y los valores se pasan al servidor (lo cual es muy bueno).
<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>
Haga la cosa, cuando copie y pegue la nueva URL y cargue la página.
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);
} };
Observe esta pregunta y especialmente las respuestas: JSP Servlet anchor
- El fragmento no se puede ver en el lado del servidor.
- Puede extraer el fragmento en el lado del cliente, convertirlo y enviarlo al servidor a través de Ajax.
Puede hacerlo con la ayuda de window.onhashchange
que rellena un campo de entrada de un formulario oculto que se envía de forma asíncrona cuando el campo de entrada ha cambiado.
Aquí hay un ejemplo de inicio de la página de 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>
Así es como se ve el frijol apropiado:
package com..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;
}
}
Eso es todo.
Tenga en cuenta que el evento onhashchange
es relativamente nuevo y no es compatible con los navegadores más antiguos. En ausencia del soporte del navegador (no definido, etc.), le gustaría revisar window.location.hash
a intervalos usando setInterval()
lugar. El ejemplo de código anterior debería al menos dar un buen saque inicial. Funciona al menos en FF3.6 e IE8.