2

I want to do the following:

  1. Select an item from an h:selectOneMenu
  2. Update the backing bean with the new value, via ajax
  3. Run a Javascript function with the new value

In the following code though, alert(#{backingBean.derivedValue}) still contains the value from the last change (i.e. it's 0 when I select "Two", 4 when I select "One", and so on):

<h:form>
    <h:selectOneMenu value="#{backingBean.input1}">
        <f:selectItem itemLabel="One" itemValue="1"/>
        <f:selectItem itemLabel="Two" itemValue="2"/>
        <f:ajax render="@form" onevent="function(data) { if (data.status === 'success') { alert(#{backingBean.derivedValue}) }}"/>
    </h:selectOneMenu>

    Input value: #{backingBean.input1}
    Derived value: #{backingBean.derivedValue}
</h:form>

And the backing-bean:

@ManagedBean
@ViewScoped
public class BackingBean {

    private int input1;
    private int derivedValue;

    public int getDerivedValue() {
        return derivedValue;
    }

    public void setDerivedValue(int derivedValue) {
        this.derivedValue = derivedValue;
    }

    public int getInput1() {
        return input1;
    }

    public void setInput1(int input1) {
        this.input1 = input1;
        derivedValue = input1 * 2;
    }
}

Is there a way to do this? (BTW, I've read countless threads on this site which kind of/sort of deal with JSF/ajax/javascript working together, but not this specific issue)

DavidS
  • 5,022
  • 2
  • 28
  • 55
wsaxton
  • 1,030
  • 15
  • 34
  • far simpler to remove your inline javascript and use unobtrusive script event handler. Do everything you need inside a named function – charlietfl Apr 09 '15 at 19:38
  • I'm not married to doing it inline. Can you provide a sample of what you are talking about? I'm not familiar with what you mean by "script event handler". I've tried having onevent="refresh_on_success" which points to "function refresh_on_success(data)" but I have the same issue... – wsaxton Apr 09 '15 at 19:46
  • RE EDIT 2: I think you're right, @wsaxton. Have a look at the HTML source of your page, see what the Javascript says. – DavidS Apr 09 '15 at 22:51
  • @DavidS, The HTML definitely shows the old value and the old value is appearing inside the javascript call so...it's certainly a timing thing. Perhaps EL expressions within the AJAX tags don't render like other tags? – wsaxton Apr 09 '15 at 22:56
  • I am seeing the same behaviour, @wsaxton. I can display the new `durationDerivedValue` on the page using `h:outputText`, but if I use a Javascript `alert` inside the function, the old value is shown. We're probably misunderstanding something fundamental about EL. – DavidS Apr 09 '15 at 23:03
  • After playing around with this for a bit, I think you should clarify what you want to do when you "Run a Javascript function with the new value". Maybe we can find a simpler way to do it, as @charletfl suggested. – DavidS Apr 09 '15 at 23:20
  • When the ajax POST happens, if you look at the browser network monitor, you'll see that the response contains the updated Javascript function with the new value. The problem appears to be that it doesn't run this updated Javascript function, it runs the old one with the previous value. – DavidS Apr 09 '15 at 23:24
  • That's why I noted "I'm also open to figuring out a better way to pass JSF Manage Bean variables to my javascript commands". I'm basically trying to integrate D3.js into my existing JSF UI so getting JSF/Javascript to play nice is important! – wsaxton Apr 09 '15 at 23:24

1 Answers1

3

I had success implementing the solution proposed in this answer, Getting backing bean value with Javascript which uses PrimeFace's RequestContext to add a Javascript callback parameter server-side.

Here is my Facelets page:

<h:form>
    <p:selectOneMenu value="#{backingBean.input1}">
        <f:selectItem itemLabel="One" itemValue="1"/>
        <f:selectItem itemLabel="Two" itemValue="2"/>
        <p:ajax oncomplete="afterLoad(xhr, status, args)"/>
    </p:selectOneMenu>
    <h:outputScript>
        function afterLoad(xhr, status, args) {    
          alert("Input * 2 = " + args.derived);
        }
    </h:outputScript>
</h:form>

And here is my backing-bean:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.primefaces.context.RequestContext;

@ManagedBean
@ViewScoped
public class BackingBean {

    private int input1;
    private int derivedValue;

    public int getDerivedValue() {
        return derivedValue;
    }

    public void setDerivedValue(int derivedValue) {
        this.derivedValue = derivedValue;
    }

    public int getInput1() {
        return input1;
    }

    public void setInput1(int input1) {
        this.input1 = input1;
        derivedValue = input1 * 2;
        RequestContext.getCurrentInstance().addCallbackParam("derived", derivedValue);
    }
}

(I don't know if this is a good solution for integrating JSF and D3.)

See also

Community
  • 1
  • 1
DavidS
  • 5,022
  • 2
  • 28
  • 55
  • This could work as a workaround, but its a bit of a mess. I really wish there was an EL way of doing it. Unfortunately, I ended up breaking JSF and D3 apart completely and just use URL's to pass information between them. – wsaxton Apr 10 '15 at 22:34
  • I understand wanting something simpler, but I don't agree that this is a workaround or a mess. It's just passing a JSON parameter to a Javascript callback, which is how a standard REST API works anyhow. It's exactly what PrimeFace's RequestContext was designed for, and it's what JSF ajax-enabled components are doing behind the scenes. Again though, I understand wanting a simpler solution -- I'm not satisfied with this myself. – DavidS Apr 10 '15 at 22:58