webtier@glassfish.java.net

Dynamic loading of JSF resources using AJAX

From: <webtier_at_javadesktop.org>
Date: Mon, 27 Sep 2010 03:46:59 PDT

I'm facing problems when trying to dynamically load new resources to JSF pages using AJAX requests.

I'm dynamically including content to a page using ui:include and that dynamically included page uses h:outputStylesheet to use new a component-specific stylesheet, which won't get included to the final page for some reason.

The same can be replicated by dynamically including any (PrimeFaces-) component that uses component-specific scripts or styles.

Is this kind of setup even supposed to work? It anyway seems that the AJAX infrastructure is working correctly as it passes a new, updated h:head to the browser, but that is just not used by the browser.

Also, there seems to be a problem with f:ajax updating the h:head component in the client-side infrastructure, but with PrimeFaces client-side AJAX infrastructure this work. Note that I'm using PrimeFaces 2.2M1, which already uses the standard JSF 2.0 AJAX server-side update mechanism and NOT a custom one.

I would like to avoid the kind of situation where I collect all JavaScript and CSS files from PrimeFaces and any other components and include ALL OF THEM for EVERY page load request. That would be waste of bandwidth and very slow.

Here's the whole test application code in a .tar.gz package: [url]http://rapidshare.com/files/421618719/jsf-ajax-resource-include-test.tar.gz[/url]

And here are the main files for easy access:

A composite component that uses a style sheet:

styled.xhtml
[code]
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite">

  <composite:interface>
  </composite:interface>

  <composite:implementation>
    <h:outputStylesheet library="component" name="styled.css" />

    <div class="divStyle">
      <p class="textStyle">content2.xhtml: As you can see, the CSS styles are not applied in an AJAX update.<br />
      Refresh the page manually to load the style sheet.</p>
    </div>
  </composite:implementation>
</html>
[/code]

styled.css
[code]
.divStyle {
  border: 1px solid #808080;
  overflow: auto;
  padding: 3px 10px 3px 10px;
  margin: 0px;
  vertical-align: top;
  font-weight: bold;
}

.textStyle {
  text-decoration: underline;
  color: #6f6f6f;
  border: none;
}
[/code]

Two sample pages that are included to the main page dynamically:

content1.xhtml
[code]
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core">
  <p>content1.xhtml: Some simple content</p>
</f:view>
[/code]

content2.xhtml
[code]
<?xml version="1.0" encoding="UTF-8"?>
<f:view xmlns="http://www.w3.org/1999/xhtml"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:comp="http://java.sun.com/jsf/composite/component">
  <comp:styled />
</f:view>
[/code]

And finally the main page that uses the previous pages:

index.html
[code]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<f:view
    xmlns:c="http://java.sun.com/jstl/core"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.prime.com.tr/ui"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.w3.org/1999/xhtml
      http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd"
    contentType="text/html">

  <html xmlns="http://www.w3.org/1999/xhtml">
    <h:head id="pageHead">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      <title>JSF dynamic resource include test</title>
    </h:head>
    <h:body id="pageBody">
      <h:form>
        <h:panelGroup id="content">
          <ui:include src="#{testBackingBean.source}" />
        </h:panelGroup>

        <br />
        Regular f:ajax:<br />
        <ul>
          <li>First press "Toggle content" and then "Update" to
            actually get the updated content as ui:include is evaluated
            before testBackingBean.toggleSource() is executed.</li>
          <li>For some reason f:ajax produces a JavaScript error dialog:
             "malformedXML: During update: pageHead not found"</li>
        </ul>
        <br />

        <!-- -->
        <h:commandLink action="#{testBackingBean.toggleSource()}"
                       value="Toggle content">
          <f:ajax render=":pageHead content" />
        </h:commandLink>
        <br />

        <h:commandLink value="Update">
          <f:ajax render=":pageHead content" />
        </h:commandLink>

        <br />
        <br />
        <br />
        PrimeFaces 2.2M1 AJAX (JSF 2.0 AJAX infrastructure) using p:commandButton:
        <ul>
          <li>First press "Toggle content" and then "Update" to
            actually get the updated content as ui:include is evaluated
            before testBackingBean.toggleSource() is executed.</li>
          <li>PrimeFaces AJAX works properly and the AJAX HTTP POST request
             response contains the h:head section with reference to
             the new CSS file "styled.css", but browsers do not load it.</li>
        </ul>
        <br />

        <p:commandLink ajax="true" update="pageHead content"
                       action="#{testBackingBean.toggleSource()}"
                       value="Toggle content" />
        <br />
        <p:commandLink ajax="true" update="pageHead content"
                       value="Update" />
      </h:form>
    </h:body>
  </html>
</f:view>
[/code]

A small backing bean that stores the current page being displayed:

TestBackingBean.java
[code]
package net.example.jsftest;

import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;

@Named("testBackingBean")
@SessionScoped
public class TestBackingBean implements Serializable {
  private boolean toggle;
  private String source;

  @PostConstruct
  private void init() {
    toggle = false;
    source = "content1.xhtml";
    System.out.println("Initializing TestBackingBean");
  }

  public String getSource() {
    return source;
  }

  public void setSource(String source) {
    this.source = source;
  }

  public void toggleSource() {
    if (toggle) {
      source = "content1.xhtml";
    } else {
      source = "content2.xhtml";
    }
    toggle = !toggle;
  }
}
[/code]
[Message sent by forum member 'turjakas']

http://forums.java.net/jive/thread.jspa?messageID=483755