Search code examples
javascriptjsffacelets

Javascript method not defined in Facelets


FireFox says my method, doSend, is not defined, but it is.
And Chrome says nothing about the websockets...

Here's my code:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:p="http://primefaces.org/ui"
  xmlns:c="http://java.sun.com/jsp/jstl/core"
  xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<script type="text/javascript">

    var image;
    var output;
    function init() {
        console.log('CONNECTED to websockets!');
        output = document.getElementById('output');
        testWebSocket();
    }

    //connects websocket to KwetterWebsocketBean
    function testWebSocket() {
        websocket = new WebSocket(wsUri);
        websocket.onopen = function(evt) {
            onOpen(evt);
        };
        websocket.onclose = function(evt) {
            onClose(evt);
        };
        websocket.onmessage = function(evt) {
            onMessage(evt);
        };
        websocket.onerror = function(evt) {
            onError(evt);
        };
    }

    //define event handlers
    function onOpen(evt) {
    }            
    function onWindowClose(evt){
        websocket.close();
    }
    function onClose(evt) {
    }
    function onMessage(evt) {
        //convert json to javascript object
        var message = JSON.parse(evt.data);
        writeToScreen('<span style="color: green;">New tweet by ' + message.username + ': ' + message.text + '</span>');
        console.log(message.text + ' : ' + message.username);
        //write message.text to screen
    }

    function onError(event) {
    }

    function doSend(message) {
        console.log(message);
        websocket.send(message);
    }

    //appends text to #output
    function writeToScreen(text) {
        var pre = document.createElement('p');
        pre.style.wordWrap = 'break-word';
        pre.innerHTML = text;
        output.appendChild(pre);
    }

    //invoke init() on load
    window.addEventListener('load', init, false);

    //enter key clicks #sendButton
    function keyPressed(event){
        if(event.keyCode == 13){
            document.getElementById('sendButton').click();
            document.getElementById('textforws').value='';
        }
    }
</script>
</h:head>
<h:body>
    <ui:composition template="./aTemplate.xhtml">
        <ui:define name="S1">
            <h1>What's happening?</h1>
            <h:form id="formNewestTweet">
                <p:inputText maxlength="140" value="aMessage"/>
                <p:commandButton value="Post Tweet" id="sendButton"                                     
                                 onclick='doSend("aMessage");'/>
            </h:form>
            <div id="output"/>
        </ui:define>
    </ui:composition>
</h:body>
</html>

How should I fix this?


Solution

  • In Facelets, Any content outside <ui:composition> of a template client is ignored. Basically, you need to put the content inside <ui:define> of <ui:composition>.

    There are in your particular case several ways to fix this wrong code:

    1. Put it straight in <ui:define>.

      <ui:composition template="./aTemplate.xhtml">
          <ui:define name="S1">
              <script type="text/javascript">
                  ...
              </script>
              ...
          </ui:define>
      </ui:composition>
      
    2. The same way, but then using <h:outputScript target="head"> instead of <script>. It'll automatically end up in HTML head (provided that you've a <h:head> instead of <head> in your master template aTemplate.xhtml):

      <ui:composition template="./aTemplate.xhtml">
          <ui:define name="S1">
              <h:outputScript target="head">
                  ...
              </h:outputScript>
              ...
          </ui:define>
      </ui:composition>
      
    3. Add a new <ui:insert name="head"> to the bottom of <h:head> of your master template aTemplate.xhtml and declare in there instead:

      <ui:composition template="./aTemplate.xhtml">
          <ui:define name="head">
              <script type="text/javascript">
                  ...
              </script>
          </ui:define>
      
          <ui:define name="S1">
              ...
          </ui:define>
      </ui:composition>
      

    See also:


    Unrelated to the concrete problem, I recommend to put master template files in /WEB-INF folder and use it like so

    <ui:composition template="/WEB-INF/aTemplate.xhtml">
    

    otherwise endusers are able to open it individually and see raw (unparsed) source code.

    See also: