3

I have a JSF composite component like this:

 <cc:implementation>
   <div id="#{cc.clientId}">
        <h:outputScript library="js" name="helper.js"/>

        <script type="text/javascript">
        if(typeof variables === "undefined"){
             var variables = {};
        }

        (function(){
            variables.formid = '#{cc.clientId}'; 
        })();
        </script>
    </div>

Value of variables.formid I'm using in a helper.js file. When I include this composite component only once, it's working like it should. When I include multiple composite components, every component receives the value of a last included composite component, how can I solve this issue?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Anatoly
  • 5,056
  • 9
  • 62
  • 136
  • the `variables` variable in the global javascript scope. Maybe you should define a map of variables for your components, like `variables[#{cc.clientId}].formid = #{cc.clientId}` – cghislai Mar 18 '15 at 15:29
  • In cases like this, think of how you would solve this in plain html if you needed to include the script part multiple times. – Kukeltje Mar 18 '15 at 15:43

1 Answers1

5

There are basically 2 ways.

  1. Add kind of "register" function to helper.js, so that you can explicitly register it there, instead of letting it to search for composites.

    <h:outputScript name="js/helper.js" target="head" />
    <div id="#{cc.clientId}">
        ...
    </div>
    <h:outputScript>helper.register("#{cc.clientId}", { foo: "somevalue" });</h:outputScript>
    

    Options can be provided via a JS object as argument. This is also how a.o. PrimeFaces work with PrimeFaces.cw() function, whereby the "widget name" is also passed as an option.

  2. Give the composite an unique style class like so:

    <h:outputScript name="js/helper.js" target="head" />
    <div id="#{cc.clientId}" class="your-foo-composite">
        ...
    </div>
    

    This way the helper.js can just collect them by class name during document ready.

    // Vanilla JS:
    var yourFooComposites = document.getElementsByClassName("your-foo-composite");
    
    // Or if you happen to use jQuery:
    var $yourFooComposites = $(".your-foo-composite");
    

    Options can be provided as HTML5 data attributes (browser support is good these days).

    <div id="#{cc.clientId}" class="your-foo-composite" data-foo="somevalue">
    

    Which can be obtained as:

    // Vanilla JS:
    for (var i = 0; i < yourFooComposites.length; i++) {
        var yourFooComposite = yourFooComposites[i];
        var clientId = yourFooComposite.id;
        var dataFoo = yourFooComposite.dataset.foo;
        // ...
    }
    
    // Or if you happen to use jQuery:
    $yourFooComposites.each(function(index, yourFooComposite) {
        var $yourFooComposite = $(yourFooComposite);
        var clientId = $yourFooComposite.attr("id");
        var dataFoo = $yourFooComposite.data("foo");
        // ...
    });
    

    It also keeps your HTML output free of inline scripts.


Unrelated to the concrete problem, usage of "js" as library name as in your initial code is not good. Carefully read What is the JSF resource library for and how should it be used? Also note that I added target="head" attribute to the <h:outputScript>. In case you're properly using JSF <h:head> component, it will let JSF autorelocate the script to the generated HTML <head> element.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555