Search code examples
sqlbirt

How to make a report parameter selection change another report parameter selection values while allowing multiselect for both of them?


Lets say I have a table with a list of names, such as "a, b, c" and each name has several other values assigned (some of the other values can be assigned to several/all of names values, example below).

Table example: ( names - other ):
a - aa
a - ab
a - ac
b - ab
b - bb
b - cb
c - ac
c - bc
c - cc

How do I make in birt so that I could select names in one parameter box and get corresponding other values to select for another parameter? I know it's possible to do that with cascading parameter, but that doesn't allow to have multiselect for the first parameter, which in our example would be names values.


Solution

  • Found a solution partly here: https://forums.opentext.com/forums/developer/discussion/61283/allow-multi-value-for-cascading-parameter and here (thanks google translate ^^): https://www.developpez.net/forums/d1402270/logiciels/solutions-d-entreprise/business-intelligence/birt/birt-4-3-1-multi-value-cascade-parameters/

    Steps to do:
    change"\birt\webcontent\birt\ajax\ui\dialog\BirtParameterDialog.js" file contents (there is a file to download for replacement in one of the links, but in case it goes dead I'm sharing 2 snippets from it where the changes occur:

        __refresh_cascade_select : function( element )
    {
        var matrix = new Array( );
        var m = 0;
        for( var i = 0; i < this.__cascadingParameter.length; i++ )
        {
            for( var j = 0; j < this.__cascadingParameter[i].length; j++ )
            {
                var paramName = this.__cascadingParameter[i][j].name;
                if( paramName == this.__isnull )
                    paramName = this.__cascadingParameter[i][j].value;
                    
                if( paramName == element.id.substr( 0, element.id.length - 10 ) )
                {
                    //CHANGE//
                    //The two way work (String(selectedValue) or Array(SelectedValueArray))
                    var l = 0;
                    var isFirstElement = true;
                    var selectedValue = "";
                    var selectedValueArray = new Array();
                    for(var test = 0; test<element.options.length;test++){
                        if(element.options[test].selected){
                            if(isFirstElement){
                                isFirstElement = false;
                                selectedValue = element.options[test].value;
                                
                            }
                            else{
                                selectedValue = selectedValue+","+element.options[test].value;
                            }
                            selectedValueArray[l] = element.options[test].value;
                            l++;
                        }
                    }
                    //CHANGE//
                    
                    var tempText = element.options[element.selectedIndex].text;
                    var tempValue = element.options[element.selectedIndex].value;
                     
                    // Null Value Parameter
                    if ( tempValue == Constants.nullValue )
                    {
                        this.__cascadingParameter[i][j].name = this.__isnull;
                        this.__cascadingParameter[i][j].value = paramName;
                    }                   
                    else if( tempValue == '' )
                    {
                        if( tempText == "" )
                        {
                            var target = element;
                            target = target.parentNode;
                            var oInputs = target.getElementsByTagName( "input" );
                            if( oInputs.length >0 && oInputs[1].value != Constants.TYPE_STRING )
                            {
                                // Only String parameter allows blank value
                                alert( birtUtility.formatMessage( Constants.error.parameterNotAllowBlank, paramName ) );
                                this.__clearSubCascadingParameter( this.__cascadingParameter[i], j );
                                return;
                            }
                            else
                            {
                                // Blank Value
                                this.__cascadingParameter[i][j].name = paramName;
                                this.__cascadingParameter[i][j].value = tempValue;                              
                            }
                        }
                        else
                        {
                            // Blank Value
                            this.__cascadingParameter[i][j].name = paramName;
                            this.__cascadingParameter[i][j].value = tempValue;
                        }                       
                    }
                    else
                    {
                        this.__cascadingParameter[i][j].name = paramName;
                        //CHANGE//
                        //The two way work (String(selectedValue) or Array(SelectedValueArray))
                        this.__cascadingParameter[i][j].value = selectedValueArray;
                        //CHANGE//
                    }
                    
                    for( var m = 0; m <= j; m++ )
                    {
                        if( !matrix[m] )
                        {
                            matrix[m] = {};
                        }
                        matrix[m].name = this.__cascadingParameter[i][m].name;
                        matrix[m].value = this.__cascadingParameter[i][m].value;
                    }
                    this.__pendingCascadingCalls++;
                    birtEventDispatcher.broadcastEvent( birtEvent.__E_CASCADING_PARAMETER, matrix );
                }
            }
        }
    },
    

    and this:

    // exist select control and input text/password
            // compare the parent div offsetTop
            if( oFirstITC.parentNode && oFirstST.parentNode )
            {
                // Bugzilla 265615: need to use cumulative offset for special cases
                // where one element is inside a group container
                var offsetITC = Position.cumulativeOffset( oFirstITC );
                var offsetST = Position.cumulativeOffset( oFirstST );
                
                // compare y-offset first, then x-offset to determine the visual order
                if( ( offsetITC[1] > offsetST[1] ) || ( offsetITC[1] == offsetST[1] && offsetITC[0] > offsetST[0] ) )
                {
                    oFirstST.focus( );              
                }
                else
                {
                    oFirstITC.focus( );
                }
            }
    

    After .js is changed cascading parameters can have multiselect on all levels. Example:
    1st DataSet "DS_country" query:

    select CLASSICMODELS.OFFICES.COUNTRY
    from CLASSICMODELS.OFFICES
    

    2nd DataSet "DS_office" query:

    select CLASSICMODELS.OFFICES.OFFICECODE
    from CLASSICMODELS.OFFICES
    where CLASSICMODELS.OFFICES.COUNTRY IN ('DS_country')
    

    After datasets are created we can make cascading report parameters CRP_country and CRP_office (keep in mind that UI doesn't let you to choose "Allow multiple values" on the upper levels, but we can change that in property editor after the parameters are made by going to advanced tab and changing "Scalar parameter type" property value to "Multi Value")

    The only thing left is to pick lower level cascading parameters (CRP_office in our example) and go to script tab and add this on "beforeOpen":

    // Do that if your selections are in String type.
    var stringArray = params["CRP_country"].value.toString().split(",");
    var result = "";
    for(var i =0 ; i < stringArray.length ; i++){
        if(i==0){
            result = "'"+stringArray[i]+"'";
        }
        else{
            result = result+",'"+stringArray[i]+"'";
        }
    }
    
    // For String
    this.queryText = this.queryText.replace("'DS_country'", result);
    
    //For integer (the first part is useless)
    //this.queryText = this.queryText.replace("'DS_country'", 
    params["CRP_country"].value);