2010-08-13 5 views
16

Sto cercando un modo per iniettare valori dal frammento (#) di un URL in bean (JSF), allo stesso modo parametro di query i valori sono iniettati. Sto usando il plugin jQuery Bookmarkable di Ben Alman (http://benalman.com/projects/jquery-bbq-plugin/) per creare i frammenti di URL. Speravo che i modelli di regex personalizzati da prettyFaces potessero essere un modo per risolvere il mio problema, ma fino ad ora non ho avuto successo.Recupera il frammento (hash) da un URL e inserisce i valori nel bean

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

vorrei definire qui la mia situazione e se qualcuno ha un'idea, mi piacerebbe provare a giocare.

Sto usando
RichFaces: 3.3.3,
Primavera: 3.0.2.RELEASE,
Hibernate: 3.5.3-Finale,
JSF: 2.0.2-FCS,
PrettyFaces: 3.0.1

L'applicazione Web genera, in base al tipo di URL, i parametri elencati dopo un cancelletto (#). L'idea è di avere un URL con segnalibro basato su Ajax. Quindi ogni volta che clicco su un elemento che cambia lo stato del sistema, il valore viene inviato al server tramite ajax e l'URL dopo che l'hash è stato riscritto. Possono esserci da 1 a 3 parametri dopo l' hash, il numero di parametri è facoltativo.

Il mio obiettivo è, quando l'utente segnalibri URL (con hash) e poi rivisita la pagina salvata, la pagina deve iniettare i valori corretti nel sistema e visualizzare la pagina nello stato precedente (come query- parametro).

Di seguito, ho un'espressione regolare che prenderebbe tutti i parametri dopo l'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}) 

So che ci sono siti web che in qualche modo inviare il frammento URL nella logica là lato server,

Esiste un modo per iniettare i valori da i frammenti dell'URL nei bean laterali del server?

risposta

8

È possibile eseguire questa operazione con l'aiuto di window.onhashchange che riempie un campo di input di un modulo nascosto che si invia in modo asincrono quando il campo di input è stato modificato.

Ecco un esempio calcio d'inizio della pagina 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> 

Ecco come il fagiolo appropriata assomigliare:

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

} 

Questo è tutto.

Si noti che l'evento onhashchange è relativamente nuovo e non supportato dai browser meno recenti. In assenza del supporto del browser (non modificato e così via), si desidera controllare lo window.location.hash ad intervalli usando invece setInterval(). L'esempio di codice sopra dovrebbe dare almeno un buon calcio d'inizio. Funziona almeno a FF3.6 e IE8.

+1

Wow, è semplicemente fantastico, grazie per la condivisione. Ora ho bisogno di dare un'occhiata a JSF 2. – clacke

+1

Per i browser IE6/7, considera il plugin jQuery Hashchange. Vedi anche http://stackoverflow.com/questions/5911899/ie6-7-back-forward-button-dont-change-window-location-hash – BalusC

3

Ecco il modo più affidabile per estrarre un frammento da un URL/URI sintatticamente valido.

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

Come iniettare questo in un fagiolo dipenderà da quello che framework lato server che si sta utilizzando, e se si sta facendo l'iniezione utilizzando XML o annotazioni, o farlo programmaticaly.

+0

Grazie per la tua idea. Sto cercando un modo per catturare l'URL quando c'è una pagina di ricaricamento (GET) e di quanto non facciano getFragment(), analizzarlo in modo corretto per ottenere i valori e rispetto impostato le rispettive variabili. – tchoesang

+0

Va bene che il metodo esiste lì, ma i browser non inviano informazioni sul frammento al server. Può essere visto registrando il traffico HTTP. – pcjuzer

+1

Mentre tecnicamente corretto, questa risposta è praticamente e fondamentalmente sbagliata. Il frammento impostato dal browser web [** will not **] (http://en.wikipedia.org/wiki/Fragment_identifier#Processing) arriva sul server web. – BalusC

1

Il frammento non può essere visto dal lato server, è accessibile solo dagli script sul lato client. Il modo in cui di solito viene fatto è che il lato server genera una pagina non parametrizzata, che viene quindi modificata dagli script in base ai parametri del frammento. Gli script possono eseguire richieste AJAX con parametri di query, in cui le risposte AJAX vengono generate da JSF utilizzando bean controllati dai parametri.

Se si desidera assolutamente che il lato server abbia accesso ai parametri del frammento durante il rendering della pagina stessa, è necessario ricaricare la pagina con i parametri come parametri di query.

EDIT: Per ricaricare la pagina è possibile utilizzare questo codice:

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

Ho visto quella tecnica utilizzata da Facebook in cui i parametri dei frammenti vengono convertiti in parametri di query quando la pagina viene ricaricata. Mi piacerebbe provare questa idea. Come può un URL contenente il frammento essere trasformato in URL con parametri di query alla pagina ricaricare? cioè http://url.com/#a=1&b=2 -> http://url.com/?a=1&b=2 – tchoesang

+0

Aggiunto codice di esempio. Tuttavia, ti suggerisco di scrivere codice per analizzare i parametri e creare la richiesta AJAX appropriata per ricostruire lo stato dei segnalibri. Inoltre, il mio esempio di codice è troppo semplice per le tue esigenze, non controlla per es. parametri duplicati e quindi si imbatteranno in problemi quando l'utente inizia a fare clic su di esso e si inizia ad aggiungere di nuovo elementi al frammento. – clacke

+0

L'esempio di codice è maldestro. Solo 'window.location.hash' fornisce già il frammento e' window.location.search' la stringa di query. Leggi i documenti ['window.location'] (https://developer.mozilla.org/en/window.location). – BalusC

1

Guarda questa domanda e soprattutto le risposte: JSP Servlet anchor

  • Frammento non può essere visto sul lato server.
  • È possibile estrarre il frammento sul lato client, convertirlo e inviare a il server tramite Ajax.
+0

Grande riferimento. Soprattutto il link [ajaxpatterns] (http://ajaxpatterns.org/Unique_URLs). – clacke

0

Grazie mille per le tue idee & specialmente BalusC (molto complimenti a te). La mia soluzione finale era una leggera mutazione e vorrei condividere con tutti voi.

Dal momento che sto usando RichFaces 3.3.3, è come usando jsf1.2, quindi non <f:ajax>

In una situazione convenzionale, si sono tenuti a cliccare su un link con una punta href ad un collegamento interno <a id='internalLink'href='#internalLink'> . Sto usando molti componenti preconfezionati in cui non tutti gli elementi selezionabili sono un collegamento <a>. Può essere un qualsiasi elemento html. È stato molto utile utilizzare il plugin jQuery Bookmarkable di Ben Alman (http://benalman.com/projects/jquery-bbq-plugin/)

Si noti che di seguito, onclick posso impostare a livello di codice il valore del frammento come chiave/valore e nel seguente codice sostituisco il vecchio valore con un nuovo valore jQuery.bbq.pushState(state,2) (o avrei potuto semplicemente aggiungere dietro il vecchio valore jQuery.bbq.pushState(state)).

Nel mio caso, ws->md->nd è un dato gerarchico in cui è sufficiente avere uno di essi ei relativi genitori vengono calcolati sul server, quindi sostituisco il valore padre con il valore figlio.

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

In richfaces, questo è un modo interessante per chiamare i metodi setter dal browser con un parametro. Notare che sendURLFragment viene trattato come un metodo JS con 5 parametri ei valori vengono passati al server (che è molto interessante).

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

Fai la cosa, quando copia e incolla il nuovo url e carica la pagina.

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