Débarrassez-vous de coder en dur le chemin du contexte des applications Web dans les fichiers JavaScript externes

J'ai plusieurs points d'extrémité WebSockets tels que,

wss://localhost:8181/ContextPath/Push 

Toutes ces URL d'extrémité sont codées dans des fichiers JavaScript externes distincts ( .js ). Ces fichiers JavaScript sont inclus dans les fichiers XHTML respectifs au besoin. Le nom d'hôte et le chemin du contexte doivent être évalués par programme au lieu de coder sur tout le lieu où ils sont requis.

Le nom d'hôte ( localhost:8181 ) peut être obtenu en JavaScript en utilisant document.location.host mais il n'y a pas de mode standard / canonique en JavaScript pour obtenir un chemin de contexte où l'application s'exécute.


Je fais quelque chose comme ce qui suit.

Une variable JavaScript globale est déclarée sur le modèle maître comme suit.

 <f:view locale="#{bean.locale}" encoding="UTF-8" contentType="text/html"> <f:loadBundle basename="messages.ResourceBundle" var="messages"/> <ui:param name="contextPath" value="#{request.contextPath}"/> <ui:insert name="metaData"></ui:insert> <h:head> <script type="text/javascript">var contextPath = "#{contextPath}";</script> </h:head> <h:body id="body"> </h:body> </f:view> </html> 

Les fichiers JavaScript dans lesquels le nom d'hôte et le chemin de contexte sont codés en dur sont inclus dans les clients modèles respectifs ou l'une des sections du modèle nord, sud, est et ouest comme suit.

 <html lang="#{bean.language}" xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"> <h:form> <h:outputScript library="default" name="js/websockets.js" target="head"/> </h:form> 

Pour le seul point de vue, websockets.js ressemble à ce qui suit (vous pouvez simplement l'ignorer).

 if (window.WebSocket) { // The global variable "contextPath" is unavailable here // because it is declared afterwards in the generated HTML. var ws = new WebSocket("wss://"+document.location.host + contextPath + "/Push"); ws.onmessage = function (event) { // This handler is invoked, when a message is received through a WebSockets channel. }; $(window).on('beforeunload', function () { ws.close(); }); } else {} 

Maintenant, le contextPath variable global JavaScript contextPath dans le modèle maître devrait être disponible dans le fichier JavaScript inclus, à savoir websockets.js . Ceci est cependant faux.

Ce qui se passe, c'est que le fichier JavaScript inclus, à savoir websockets.js où la variable globale du contextPath été tentée, est placé avant la balise <script> codée en dur dans la balise HTML <head> générée dans le modèle maître.

En d'autres termes, la variable JavaScript globale contextPath est en fait tentée d'utiliser dans le fichier inclus websockets.js avant d'être déclarée.

Quoi qu'il en soit, comment se débarrasser de coder en dur le chemin du contexte dans les fichiers JavaScript externes?

Le seul but de cela est que, contrairement aux fichiers CSS, EL n'est pas évalué dans les fichiers JavaScript externes. Par conséquent, la chose #{} ne fonctionnera pas à moins qu'elle ne soit placée dans un fichier XHTML.

Ce qui se passe, c'est que le fichier JavaScript inclus appelé websockets.js où la variable globale contextPath été tentée, est placé avant la balise <script> codée en dur dans la balise HTML <head> générée dans le modèle maître

C'est inattendu. Vous avez déclaré le <h:outputScript> référençant websockets.js fichiers websockets.js dans <h:body> avec target="head" . Ceci devrait se terminer après toutes les autres ressources de script déjà déclarées dans <h:head> . Voir aussi ao Comment faire référence à la ressource CSS / JS / image dans le modèle Facelets? Après tout, cela semble être causé par PrimeFaces regroupé HeadRenderer qui est destiné à inclure automatiquement certaines ressources CSS et à prendre soin du <facet name="first|middle|last"> .

Cela vaut un rapport sur les problèmes des garçons PF (si ce n'est déjà fait). Dans l'intervalle, votre meilleur pari est de l'éteindre en enregistrant explicitement le HeadRenderer implémentation JSF comme indiqué ci faces-config.xml dessous dans faces-config.xml (pourvu que vous utilisez Mojarra).

 <render-kit> <renderer> <component-family>javax.faces.Output</component-family> <renderer-type>javax.faces.Head</renderer-type> <renderer-class>com.sun.faces.renderkit.html_basic.HeadRenderer</renderer-class> </renderer> </render-kit> 

Et inclure explicitement le theme.css spécifique au thème theme.css comme ci-dessous dans <h:head> :

 <h:outputStylesheet library="primefaces-aristo" name="theme.css" /> 

En revenant à la vraie question,

Quoi qu'il en soit, comment se débarrasser de coder en dur le chemin du contexte dans les fichiers JavaScript externes?

Définissez-le comme URI de base (note: le chemin relatif n'est pas pris en charge dans HTML4 / IE6-8).

 <h:head> <base href="#{request.contextPath}/" /> ... </h:head> 
 var baseURI = $("base").attr("href"); 

Ou définissez-le comme un attribut de données de l'élément racine HTML.

 <!DOCTYPE html> <html lang="en" data-baseuri="#{request.contextPath}/" ...> ... </html> 
 var baseURI = $("html").data("baseuri"); 

Sans rapport avec le problème concret, comme un mot de conseil, pour couvrir de manière transparente à la fois http + ws et https + wss, envisagez d'utiliser location.protocol au lieu d'un wss .

 var ws = new WebSocket(location.protocol.replace("http", "ws") + "//" + location.host + baseURI + "Push"); 

Est-ce que la suivante est une option pour vous?

  1. Définissez une étiquette html cachée dans le modèle maître, quelque chose comme:

     <span id="pageContextPath" data="#{contextPath}" style="display:none;"></span> 
  2. Modifiez votre code JavaScript à quelque chose comme:

     jQuery(document).ready(function ($) { if (window.WebSocket) { contextPath = $("#pageContextPath").attr("data"); var ws = new WebSocket("wss://" + document.location.host + contextPath + "/Push"); //... } else {} }); 

J'ai utilisé ici jQuery. Vous pouvez l'écrire en JavaScript. Mais cela devrait être fait après "document prêt" pour s'assurer que la balise cachée a été rendue. Sinon js ne trouvera pas cet élément.