RichFaces JavaScript handling.

JSF 2.0 resources limitations:

  1. no API to track JavaScript dependencies ( eg. validator.js requires richfaces.js that depends from jquery.js and jsf.js ).
  2. Resources loaded by @ResourceDependency annotation are fixed, while it would be nice to load them in different ways: separate uncomplessed files for development, and compressed single-file library in production, maybe from separate server
  3. Limited way to append resources during request, or include new libraries by AJAX.
  4. No API to use jQuery in preffered way: load JavaScript files at the end of page and put initialization code after them, as document.ready event listener.

Goals:

  1. Performance. Follow Google and Yahoo recomendations: load all scripts at the end of page, load additional libraries on demand. Compress scripts for production deployment, load them from cookies-free domain.

  2. jQuery compatibility. jQuery reccommends to put initialization code in the document.ready event listener that runs when all parts of page were loaded. It eliminates some errors and improves site usability: user gets page rendered without delays, and get functionality activated a little bit later.
  3. Unified management for javascript libraries. Load all of them in single place and with proper order. Elimitate solution like Mojarra jsf.js loading: this file can be loaded in 3 different ways: as renderer dependency, from behavior code, and by TagHandler. Therefore, there are implementation-specific attributes and special utility methods to avoid duplicate resource loading. In RichFaces, we already have similar solutions for queue, push, client side validator and calendar ( may be even more ) that should be unified.
  4. Manage JavaScript dependencies.
  5. On demand Java Script loading. That is necessary for client side validator which creates code from bean attribute annotations. Therefore, it's not possible for CSV to provide necessary resources as renderer dependencies.
  6. Avoid compatibility problems like that. Reccommended jQuery initialization code runs when all libraries were loaded and initialized.

Implementation:

  • Instead of providing JavaScript dependencies and rendering scripts directly to ResponseWriter, component/behavior creates special object that describes resources used by script and provides method to generate JavaScript code. Component sends that object to RichFaces JavaScriptService, available via ServiceTracker. That object should be immutable and contains all information which is necessary to generate code. The simplest solution is Java Bean that has read-only attributes for JavaScript resource ( library and name ) and JavaScript string.
  • Service collects all these requests and pass them to the JSF component that rendered at the bottom of the page ( therefore, that component should be stored as UIViewRoot viewResource with target "body" ).
  • Resource component sorts required libraries in proper order, adds dependencies and removes duplicates. Dependencies can be defined in separate configuration file or provided as JSDoc comments: @depends org.richfaces:richfaces.js org.richfaces:jquery.js ...
  • Resource Component renders links to load all necessary libraries. Because it already have information about all required dependencies, it can organize them in proper order and load only once. For production stage, it can be configured to load compressed scripts, link from external site or even merge necessary resources into single file.
  • Aftrer links to external scripts, component will render jQuery $(){ .. } initialization script and put code collected by ScriptService there.
  • For AJAX requests, custom PartialViewContext should call that Component to generate <eval> section of AJAX response with the same initialization code that will be executed after DOM updates. That code also can add links to JavaScript libraries required by added components.