Programmatically determine the correct response filter key

Commerce also provides a mechanism for programmatically determining the response filter key to pass in a REST call, allowing you to avoid hard coding it into the widget itself.

The programmatic mechanism for determining which response filter key to pass in a REST request has several parts, as follows:

  • A context object that is instantiated in the widget’s JavaScript file.
  • A filter map that is defined in an application-level JavaScript file.
  • The CCStoreConfiguation library.

The context object contains the data required for locating the correct response filter key in the filter map. The widget instantiates the context object with the necessary data and then passes it to the CCStoreConfiguration library’s getFilterToUse() method, which locates the correct response filter key in the filter map and returns it to the widget.

Enable programmatic filter key determination

You must enable programmatic filter key determination before you can use it. To do so, create an application-level JavaScript module that lists the CCStoreConfiguration library as a dependency and includes the following code:

define(
   //-------------------------------------------------------------------
   // DEPENDENCIES
   //-------------------------------------------------------------------
     ['ccStoreConfiguration'],
   //-------------------------------------------------------------------
   // Module definition
   //-------------------------------------------------------------------
   function(CCStoreConfiguration) {
     'use strict';

     return {
       onLoad : function() {
                 CCStoreConfiguration.getInstance().enableFilter();
       },
     }
   }
 );

See Include Application-level JavaScript Modules for details on creating and uploading an application-level JavaScript module.

Understand the filter map

The filter map uses a prioritized structure of top-level objects and nested sub-objects. CCStoreConfiguration compares the data in the context object to that prioritized structure when locating the key. The following code sample shows the out-of-the-box filter map that Commerce uses. In it, the filter map sets the priorityList variable to ["endpoint","page","identifier"], meaning that CCStoreConfiguration will first try to find a top-level object that matches the endpoint in the context object, then it will search inside that object for a matching page object, then it will search inside that object for a matching identifier object. The following is an example:

define(
   //-------------------------------------------------------------------
   // DEPENDENCIES
   //-------------------------------------------------------------------
   ['ccStoreConfiguration'],
   //-------------------------------------------------------------------
   // Module definition
   //-------------------------------------------------------------------
   function(CCStoreConfiguration) {
     'use strict';
     return {
       onLoad : function() {
         console.log("Loading Application Level JS");
            var priorityList = ["endpoint","page","identifier"];
            var newFilterMap = {
                   "getCollection":{
                      "megaMenuNavigation": {"ccFilterConfigKey": "categoryNavData"},
                      "categoryNavigation": {"ccFilterConfigKey": "categoryNavData"}
                    },
                    "listProducts":{
                      "productListingData": {"ccFilterConfigKey": "PLPData"},
                      "collectionWidget": {"ccFilterConfigKey": "collectionData"},
                      "getProductData": {"ccFilterConfigKey": "productData"},
                      "getProductDataAndRedirect": {"ccFilterConfigKey": "productData"}
                    }
                  };
                  CCStoreConfiguration.getInstance().updateFiltersToUse(newFilterMap);
             },
        }
   }
 );

The top-level objects in the out-of-the-box filter map correspond to endpoints and their sub-objects correspond to identifiers. In other words, getCollection and listProducts represent endpoints and their children (megaMenuNavigation, categoryNavigation, productListingData, and so on) represent identifiers. (Note that the getCollection and listProducts endpoints return data that is page-independent so, even though the priority list includes page, page objects are not defined for these two endpoints in the out-of-the-box filter map.)

To understand how CCStoreConfiguration compares the contents of a context object to the filter map, we will compare the following context object to the out-of-the-box filter map:

var contextObj = {};
     contextObj["endpoint"] = "getCollection";
     contextObj["identifier"] = "categoryNavigation";

When considering this context object, CCStoreConfiguration first looks for a matching endpoint among the top-level objects in the filter map (because endpoint is first in the priority list). In this case, CCStoreConfiguration finds the getCollection top-level object. Next, CCStoreConfiguration looks for a matching page sub-object within the getCollection top-level object (because page is second in the priority list). The context object does not have page data, however, so CCStoreConfiguration moves on to find the next piece of data in the priority list, which is identifier. The thing to note here is that CCStoreConfiguration continues to look for the next piece of data in the current object. In other words, it looks for a categoryNavigation sub-object in the getCollection top-level object. When CCStoreConfiguration finds the categoryNavigation sub-object, it sees that the object has a ccFilterConfigKey defined for it. CCStoreConfiguration retrieves this filter key, categoryNavData, and returns it to the widget.

You can set your priority list and the object structure of your filter map in any way that makes sense for your implementation and then define context objects in your widgets that use that updated structure. However, keep in mind that the out-of-the-box filters, and the widgets that use them, may be affected by changes you make and may need modifications as a result.

Create a context object and use it to retrieve the response filter key

To create a context object and use it to retrieve a response filter key, add code similar to the following to the widget’s JavaScript file. Note that you must also add a dependency on the CCStoreConfiguration library. The following is an example:

// Add the CCStoreConfiguration library as a dependency for this widget
 // Create the context object and populate it
 var contextObj = {};
     contextObj["endpoint"] = "endpoint-name";
     contextObj["identifier"] = "identifier-in-filter-map";
 // Call the getFilterToUse method to retrieve the response filter key 
 var filterKey = CCStoreConfiguration.getInstance().getFilterToUse(contextObj);
 // Add the filterKey to the data passed with the REST call
 if (filterKey) {
   data["filterKey"] = filterKey;
 }
 //Make the REST call
 ccRestClient.request(url, data,
       this.successFunc.bind(this),
       this.errorFunc.bind(this));
        }

Add a new response filter key to the out-of-the-box filter map

The following code sample creates new identifiers in the out-of-the-box filter map for calls made to the getCollection and productListing endpoints. The new identifier for the getCollection endpoint is customIdentifer1 and the response filter key that is returned for it is customFilterKey1. The new identifier for the productListing endpoint is customIdentifer2 and the response filter key that is returned for it is customFilterKey2. The following is an example:

define(
   //-------------------------------------------------------------------
   // DEPENDENCIES
   //-------------------------------------------------------------------
   ['ccStoreConfiguration'],
   //-------------------------------------------------------------------
   // Module definition
   //-------------------------------------------------------------------
   function(CCStoreConfiguration) {
     'use strict';
     return {
       onLoad : function() {
         console.log("Loading Application Level JS");
            var priorityList = ["endpoint","page","identifier"];
            var newFilterMap = {
            "getCollection":{
               "megaMenuNavigation": {"ccFilterConfigKey": "categoryNavData"},
               "categoryNavigation": {"ccFilterConfigKey": "categoryNavData"},
               "customIdentifier1": {"ccFilterConfigKey": "customFilterKey1"}
             },
             "listProducts":{
               "productListingData": {"ccFilterConfigKey": "PLPData"},
               "collectionWidget": {"ccFilterConfigKey": "collectionData"},
               "getProductData": {"ccFilterConfigKey": "productData"},
               "getProductDataAndRedirect": {"ccFilterConfigKey": "productData"},
               "customIdentifier2": {"ccFilterConfigKey": "customFilterKey2"}
              }
           };
           CCStoreConfiguration.getInstance().updateFiltersToUse(newFilterMap);
       },
     }
   }
 );

Note that, when you override the filter map, the top-level objects you define completely replace any existing top-level objects. In other words, if you created a new filter map that looked as follows:

// This code overwrites the getCollection top-level object entirely
var newFilterMap = {
   "getCollection":{
            "customIdentifier1": {"ccFilterConfigKey": "customFilterKey1"}
   },
 };

You would lose the megaMenuNavigation and categoryNavigation identifiers defined out of the box for the getCollection top-level object. However, the listProducts top-level object would remain unchanged because no new top-level object definition for it has been introduced. For this reason, you should be careful to include the default identifiers, shown earlier, along with any new identifiers you create unless you explicitly intend to overwrite them.

Use defaults in the filter map

The filter map supports the concept of defaults at each object level. When CCStoreConfiguration cannot find a match for a piece of data in the context object, it looks for a default. If it finds a default, it searches within that default object’s children for the next piece of data in the priority list. If it cannot find a match or a default, it will not return a response filter key.

The concept of default objects can exist at any level in a filter map. For example, consider this filter map that sets its priority list to ["endpoint","page","identifier","viewport"]:

var newFilterMap = {
           "endpoint1": {
                      "page1":{"cc-filter-config-key": "key1"},
                      "page2":{"cc-filter-config-key": "key2"},
                      "page3":{
                          "identifier1":{"cc-filter-config-key": "key3"},
                          "identifier2":{"cc-filter-config-key": "key4"},
                          "cc-filter-config-key": "key11",
                          "default":{
                              "viewport1":{"cc-filter-config-key": "key5"},
                              "viewport2":{"cc-filter-config-key": "key6"},
                              "default":{"cc-filter-config-key": "key7"}
                              }
                          },
                      "cc-filter-config-key": "key8",
                      "default":{
                          "identifier1":{"cc-filter-config-key": "key9"},
                          "default":{"cc-filter-config-key": "key10"}
                      }
           }
};

The following table lists a variety of sample context objects and the response filter key that would be returned for them based on this filter map:

Context Object Data Filter Key Returned
endpoint1 key8
endpoint1, page1 key1
endpoint1, page3 key11
endpoint1, page3, identifier1 key3
endpoint1, page3, identifier2 key4
endpoint1, page1, identifier3 key1
endpoint1, page3, identifier3, viewport1 key5
endpoint1, page3, identifier3, viewport3 key7
endpoint1, page4 null
endpoint1, page4, identifier1 key9
endpoint1, page4, identifier2 key10
endpoint1, page1, identifier1 key1