Search code examples
javascriptjsfprimefacesrequestcontext

How to execute javascript function which uses the callback parameters on the server/client side?


I use a JSF 2.2, a Primefaces 6.0 and a CDI. One of my page includes a script which has got two javascript functions (one of them create a chart, second of them supply new data to the chart) and a simple form which works as a filter. I've cut my code (look at below) to show you the most important part of my code.

I would like to achieve something like this:

  • For the first time when the page is loaded/opened, the first javascript function should run (automatically without any pressing the button or the link) and should use several callback parameters which I set in the cdi bean.
  • Each time when I press the button, the second javascript function should run and should use several callback parameters which I set in the CDI bean.

How you see, the above mechanism is based on passing some values from the CDI bean to the javascript by means of the RequestContext. To achieve this I based on the primefaces documentation (chapter 11.1). So far, I've:

  • created several callback parameters for the first function in the init() method (which has the @PostConstruct annotation) of the CDI bean;
  • added the oncomplete attribute to the <p:commandButton> component which calls second of the function with args parameter. The values which I want to pass to the second function are prepared in actionFilterButton() method which I call in action attribute of the button.

At the moment my page works like this:

  • When the page is loaded/opened, the first javascript function isn't executed.
  • When I press the button, the second javascript function is executed and it saves the values of the callback parameters in the javascript variables.

How you see, the problem is the first stage. The first javascript function isn't executed when I open my page.

How can I call the first javascript function?

What did I try?

  • I tried to call the first javascript function on the server side calling requestContext.execute("firstFunction(args)"); in the init() method in hope it'll be worked but it isn't work and it breaks my form when I use the form. I'm not sure if I can put args as I did it on the server side (indeed I'm not sure if it is interpreted correctly).
  • I also tried to call firstFunction() at the end of my script (how you see below), but it isn't work as well.

My xhtml page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" 
xmlns:ui="http://java.sun.com/jsf/facelets" 
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:pt="http://xmlns.jcp.org/jsf/passthrough"
xmlns:pe="http://primefaces.org/ui/extensions">

<h:head> 
    <!-- loading css -->
</h:head>

<h:body>

    <div id="container" style="height: 750px; width: 100%; margin: 0 auto;"/>

<script type="text/javascript">

    //<![CDATA[

    //Declaration of variables
    var chart;
    var data1;
    var data2;
    var data3;

    function firstFunction(args) {  

        $(function() {

            data1 = JSON.parse(args.data1);
            data2 = JSON.parse(args.data2);
            data3 = JSON.parse(args.data3);

            //Other operations          

        });
    }

    function secondFunction(args) {

        $(function() {

            data1 = JSON.parse(args.data1);
            data2 = JSON.parse(args.data2);
            data3 = JSON.parse(args.data3);

            //Other different operations            

        });
    }

    firstFunction(args);

    //]]>
</script>

<br/>

<h:form id="filterForm">

    <p:selectManyCheckbox id="filter" value="#{chart2Controller.selectedKindOfNetwork}" layout="responsive" columns="4">
        <p:ajax update="filterButton" />
        <f:selectItems value="#{chart2Controller.kindOfNetwork}" var="kindOfNetwork" itemLabel="#{kindOfNetwork}" itemValue="#{kindOfNetwork}" />
    </p:selectManyCheckbox>

    <br/>

    <p:commandButton id="filterButton" value="Filter" action="#{chart2Controller.actionFilterButton()}"
                    disabled="#{!chart2Controller.visibilityFilterButton}"
                    update="filterForm"
                    oncomplete="secondFunction(args);"/>    

</h:form>

</h:body>       
</html>                      

Part of my CDI bean:

@Named
@ViewScoped
public class Chart2Controller implements Serializable {

    /**
     * Start method.
     */
    @PostConstruct
    public void init() {

        initData();     

        //This isn't work.
        //requestContext.execute("firstFunction(args)");
    }

    /**
     * Downloading the data from the database.
     */
    private void initData(){


        /*
         * Sending the query to the database including the filter options of the form,
         * saving the result and preparing the data basing on the result.
         */

        //Preparing the callback parameters.
        requestContext = RequestContext.getCurrentInstance();
        requestContext.addCallbackParam("data1", data1);
        requestContext.addCallbackParam("data2", data2);
        requestContext.addCallbackParam("data3", data3);
    }

    /**
     * Action for the filter button.
     */
    public void actionFilterButton(){

        initData(); 
    }   

    /**
     * Visibility for filter button.
     */
    public boolean getVisibilityFilterButton(){     
        //return true or false.
    }   

    //Getter and Setter

    private static final long serialVersionUID = -8128862377479499815L;

    @Inject
    private VisualizationService visualizationService;

    private RequestContext requestContext;

    private List<ChartModel2> data;

    private List<String> kindOfNetwork;
    private List<String> selectedKindOfNetwork;

    private String data1;
    private String data2;
    private String data3;
}

Solution

  • I've just found the soultion. Indeed two solutions: on the server side (which I recommend) and on the client side. My problem was this javascript line: firstFunction(args);. The args parameter wasn't properly interpreted.


    On the server side

    First of all I've changed the parameters of my javascript functions. Instead of args parameter I've used three parameters (one for each variable). At the moment, the script should look like this:

    <script type="text/javascript">
    
        //<![CDATA[
    
        //Declaration of variables
        var chart;
        var data1;
        var data2;
        var data3;
    
        function firstFunction(data1p, data2p, data3p) {    
    
            $(function() {
    
                data1 = JSON.parse(data1p);
                data2 = JSON.parse(data2p);
                data3 = JSON.parse(data3p);
    
                //Other operations          
    
            });
        }
    
        function secondFunction(data1p, data2p, data3p) {
    
            $(function() {
    
                data1 = JSON.parse(data1p);
                data2 = JSON.parse(data2p);
                data3 = JSON.parse(data3p);
    
                //Other different operations            
    
            });
        }
    
        //]]>
    </script>
    

    I've changed the value of the oncomplete attribute of the <p:commandButton> as well: oncomplete="createChart(args.data1, args.data2, args.data3);"/>.

    Note that, if you want, you can keep the old version of the second javascript function (with one args parameter) and keep the old version of the oncomplete attribute. The most imporatant is the first javascript function which will be called on the server side.

    Finally, I've called requestcontext.execute() method in the init() method of the CDI bean, so I'm sure that the data which will be downloaded are ready.

        /**
         * Start method.
         */
        @PostConstruct
        public void init() {
    
            initData();     
    
            requestContext.execute(String.format("createChart('%s', '%s', '%s')", getProtos(),getData(),getColors()));
        }
    

    The execute method calls the first javascript method and passes the three required values.

    Note that, I've used '' characters to achieve the correct representation on the javascript side.


    On the client side

    In this case, I've change only first javascript function. Instead of the args parameter, I've used three parameters (one for each varables) such as in the previous example. I've kept the second function with the args parameter, but if you want, you can change this in the same way like in the previous example.

    Finally, I've called the first function at the end of the script. Look at the code below. I've used the EL to pass the values from the CDI bean. I've also deleted JSON.parse() method because the ELs were written directly into the javascript code and the browser correctly interpreted the data in my case.

    <script type="text/javascript">
    
        //<![CDATA[
    
        //Declaration of variables
        var chart;
        var data1;
        var data2;
        var data3;
    
        function firstFunction(data1p, data2p, data3p) {    
    
            $(function() {
    
                data1 = data1p;
                data2 = data2p;
                data3 = data3p;
    
                //Other operations          
    
            });
        }
    
        function secondFunction(args) {
    
            $(function() {
    
                data1 = JSON.parse(args.data1);
                data2 = JSON.parse(args.data2);
                data3 = JSON.parse(args.data3);
    
                //Other different operations            
    
            });
        }
    
        firstFunction(#{chart2Controller.data1}, #{chart2Controller.data2}, #{chart2Controller.data3});
    
        //]]>
    </script>