Rally App SDK rallyfieldvaluescombobox component (Used in Custom Applications) does not retrieve all results returned by API query for multi-select fields
search cancel

Rally App SDK rallyfieldvaluescombobox component (Used in Custom Applications) does not retrieve all results returned by API query for multi-select fields

book

Article ID: 251975

calendar_today

Updated On:

Products

Rally SaaS

Issue/Introduction

When building Rally Apps with the Rally SDK and using the Rally.ui.ComboBox.FieldValueComboBox (xtype: rallyfieldvaluecombobox) and there are more than 200 allowed values for the selected field and the field is a Multi-select, then not all values may be accessible via the dropdown list in the SDK.  

If there are less than 2000 items in the allowed values for the field, the Rally API can pull all the values for the field using this query (Note that the attributedefinition id will be different for each field and subscription):

https://rally1.rallydev.com/slm/webservice/v2.0/attributedefinition/<OBJECT_ID>/AllowedValues?pagesize=2000

But the Rally App SDK for the field does not display more than the default of 200 results.  The user is unable to override the page size.

This works correctly with a single select field

Cause

In the case of this issue, the component is only returning 200 items (which is also the default page size).  Adding a configuration to return more than 200 items is ignored in the component code and the component does not properly accommodate the scenario of a dropdown having more than 200 items.    

Resolution

The ideal way to address the problem would be to build a custom component that manages the paging for the collection and accessing allowed values in the collection, and this would require advanced programming knowledge and understanding of how the Rally SDK and API work together.    

 

If there are less than 2000 allowed values in the field (and always will be), the most straightforward alternate resolution is to adjust the page size for the store in the combobox and override a function in the rallyfieldvaluecombobox component to honor the storeConfig that is passed into the combobox configuration.  You can see the contents of the entire test app below.  There are 2 key steps to address this: 

 

1.  Update the component to use the max pageSize (2000) or a pageSize that will accommodate all allowed values for the dropdown field:  

  this.add({
                xtype: 'rallyfieldvaluecombobox',
                model: 'HierarchicalRequirement',
                field: 'c_MultiValueFieldWithAllTheChoices',
                fieldLabel: 'All the Choices:',
                multiSelect: true,
                storeConfig : {
                    pageSize : 2000,   // <--- This number determines how many items will be returned in the first page 
                },
                // ... combobox configurations 
 });

 

 

 

2.  Override the _loadStoreValues function in the Rally.ui.combobox.FieldValueComboBox class and add the if statement surrounded by comments in the code below to merge the storeConfig and honor the pageSize being passed from step 1:   

Ext.override(Rally.ui.combobox.FieldValueComboBox,{
            _loadStoreValues: function() {
                var storeConfig = {context: this.context && _.isFunction(this.context.getDataContext) ? this.context.getDataContext() : this.context};
               
               /** Start new code here to merge the StoreConfig **/ 
               if (this.storeConfig){
                    storeConfig = Ext.merge(storeConfig, this.storeConfig);
                }
               /** End new code to merge the StoreConfig **/

            this.field.getAllowedValueStore(storeConfig).load({  
                    /*... */  
            });
}

 

The entire example app that contains HTML and javascript for these fixes is below:  

<!DOCTYPE html>
<html>
<head>
    <title>Test App</title>
    <!--  (c) 2015 Rally Software Development Corp.  All Rights Reserved. -->
    <!--  Build Date: Fri Sep 23 2022 09:06:36 GMT-0600 (Mountain Daylight Time) -->
    
    <script type="text/javascript">
        var APP_BUILD_DATE = "Fri Sep 23 2022 09:06:36 GMT-0600 (Mountain Daylight Time)";
        var CHECKSUM = 20908995072;
    </script>
    
    <script type="text/javascript" src="/apps/2.1/sdk.js"></script>

    <script type="text/javascript">
        Rally.onReady(function() {
             

// Add the following override so that the combobox honors the page size
Ext.override(Rally.ui.combobox.FieldValueComboBox,{
            _loadStoreValues: function() {
                var storeConfig = {context: this.context && _.isFunction(this.context.getDataContext) ? this.context.getDataContext() : this.context};
                /** Start new code here to merge storeConfig so that the page size is honored **/
                if (this.storeConfig){
                    storeConfig = Ext.merge(storeConfig, this.storeConfig);
                }
               /** End new code to honor page size **/
            this.field.getAllowedValueStore(storeConfig).load({
                requester: this,
                callback: function(records, operation, success) {
                    var store = this.store;
                    if (!store) {
                        return;
                    }
                    var values = [],
                        labelValues = _.map(
                            _.filter(records, this._hasStringValue),
                            this._convertAllowedValueToLabelValuePair,
                            this
                        );

                    if(this.field.getType() === 'boolean') {
                        labelValues = labelValues.concat([
                            this._convertToLabelValuePair('Yes', true),
                            this._convertToLabelValuePair('No', false)
                        ]);
                    } else if (this.field.required === false) {
                        var name = "-- No Entry --",
                            value = this.noEntryValue;
                        if (this.getUseNullForNoEntryValue()) {
                            this.noEntryValue = value = null;
                        }
                        if (this.field.attributeDefinition.AttributeType.toLowerCase() === 'rating') {
                            name = this.getRatingNoEntryString();
                            value = "None";
                        }
                        values.push(this._convertToLabelValuePair(name, value));
                    }

                    if (this.getAllowInitialValue() && this.config.value) {
                        var initialValue = this.transformOriginalValue(this.config.value);
                        if (this._valueNotInAllowedValues(initialValue, labelValues)) {
                            var label = this.config.value._refObjectName || initialValue;
                            values.push(this._convertToLabelValuePair(label, initialValue));
                        }
                    }

                    store.loadRawData(values.concat(labelValues));
                    store.fireEvent('load', store, store.getRange(), success);
                },
                scope: this
            });
        }
});


Ext.define("test-app", {
        extend: 'Rally.app.App',
        componentCls: 'app',
    
        launch: function() {
                   
            this.add({
                xtype: 'rallyfieldvaluecombobox',
                model: 'HierarchicalRequirement',
                field: 'c_MultiValueAllTheChoices',
                fieldLabel: 'All the Choices:',
                multiSelect: true,
                storeConfig : {
                    pageSize : 2000 // <-- This number determines the page size for returning one page of data
                }
            });
        }
    });
            
               Rally.launchApp('test-app', {
                   name: 'Test App'
               });
        });
    </script>
    
    <style type="text/css">

.app {
}
.tsinfolink {
    position:absolute;
    right:0px;
    width: 14px;
    height: 14px;
    border-radius: 7px;
    text-align: center;
    color: white;
    background: #C0C0C0;
    border-style: solid;
    border-width: 1px;
    margin-top: 25px;
    margin-right: 5px;
    cursor: pointer;
}
    </style>

</head>
<body></body>
</html>