0

I have a composite component that is using a PrimeFaces <p:selectOneMenu> defined like:

<p:selectOneMenu id="inOut" onchange="inOutChanged()">
  ..
</p:selectOneMenu>

I then have a JavaScript file that is included as part of the component that defines the inOutChanged() function like

function inOutChanged() {
  var inOut = $({cc.clientId}:inOut);
  if(inOut.val() == "INC") {
    slider.addClass("includedInRange");
  }
}

This composite component works fine when there is only one on the page. The problem is that I need to have 4 of them on the page. So, when I change the select of one composite component it is changing the slider class of the last component. I believe this is because there are in effect 4 inOutChanged() functions that are all defined within the page.

Is there a way to either scope the function or name it uniquely so that they won't stomp on each other?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
mpaulse
  • 87
  • 1
  • 11
  • You're misinterpreting the problem. First, how and where have you declared the `slider` variable? It look namely much like that you've just overridden the variable's value everytime so that it end up holding the last assigned value. All with all, I have the impression that you actually meant "composite component" when you said "component"? This way the question would make much more sense. – BalusC Apr 05 '13 at 11:53
  • @BalusC Yes, it is a composite component. The slider variable is also declared using a similair approach with cc.clientId. It is possible that I've overridden the slider variable. I think I'll have to try the approach you suggested in your answer maybe as well. – mpaulse Apr 05 '13 at 15:07

2 Answers2

2

Your concrete problem is caused because you redeclared and reassigned the same JavaScript variable slider in the global scope. You need to move the declaration of slider to inside the function.

Redeclaring exactly the same function doesn't harm, but is plain inefficient and not DRY. You should move the function into its own .js file and use <h:outputScript> to include it. JSF will ultimately include it only once. Then, alter the function accordingly to take variables as arguments.

Something like this

<h:outputScript library="composite" name="some.js" target="head" />
...
<h:panelGroup id="...">
    <p:selectOneMenu ... onchange="inOutChanged(this)">
        ...
    </p:selectOneMenu>
    ...
    <p:slider ... />
</h:panelGroup>

with this inside /resources/composite/some.js (provided that /resoruces/composite contains your composite component XHTML file)

function inOutChanged(menuElement) {
    var $menu = $(menuElement);
    if ($menu.val() == "INC") {
        $menu.parent().find(".ui-slider").addClass("includedInRange");
    }
}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • It wasn't because of my slider variable. It was declared within my function so it should have been unique. However, I did use both your approach of making a separate function that was included and using this + a parent selector. I had to mess with the parent selector since PrimeFaces adds several layers of divs between the panelgroup and the selectOneMenu. But this got me moving along. – mpaulse Apr 06 '13 at 08:39
  • You can also use `.closest()` to get the closest parent matching the given selector. – BalusC Apr 06 '13 at 12:00
1

You should use $(this) selector within your change event listener:

$(".inOut").change(function(){
    var sel = $(this).find('option:selected').text();
    if(sel == 'INC') {
        //find slider and add class
    }
});

Note that I bound onchange event listeners outside the scope of the component, and found components basing on attached CSS class. This could be done within $(document).ready().

Of course, you can use client ids in the JS function or bind that function to a component's attribute as well.

skuntsel
  • 11,624
  • 11
  • 44
  • 67