back to top

Dynamically Filtering a Lookup on a Creatio Freedom UI Page

Filtering lookups on a Freedom UI page is something new in 8.0.10. However, even in 8.0.10 there are still some issues with filtering lookups. I do expect lookup filtering to improve for Freedom UI in versions to come, however, there is still a need to be able to dynamically filter a lookup with complex conditions at runtime. This article will outline how to do this for a lookup on Freedom UI pages, giving you full control over the conditions for the filter.

Note: When a lookup is opened on a Freedom UI page, the lookup makes a crt.LoadDataRequest request to obtain its data. This request can be handled/intercepted and filter conditions can be added so the data received by the lookup is filtered to match the provided conditions.

To filter a lookup on a Freedom UI page, we’ll need to know the attribute name for the lookup field in the model, and then we’re add a handler for any time anything on the page makes a request to load it’s datasource. If the requested datasource is for our lookup’s bound field, we’ll apply a filter to the request. In the example for this article, we’ll use a scenario of filtering the Case contact lookup to only show contacts for the selected Account on the Case. In 8.0.10, this can be implemented as a business rule, however, we’re just using this as an example (which also could be added to 8.0.9 now as well).

First, we’ll need to locate the lookup control so we can determine it’s bound attribute name. For the case page, we’ll see the following for the Contact lookup in the viewConfigDiff for the page:

Notice the name of the attribute the control is bound to (highlighted in the screenshot). This will correspond with the following attribute in the viewModelConfig, which shows it’s bound to the Contact column for the Case:

For the case page, the “LookupAttribute_c08bwtk” attribute is what we’ll need to look for when it requests that it’s data source is loaded. The name of the datasource will be the attribute name followed by “_List_DS”. For this attribute, we’ll be looking for the request to load the data for the datasource named “LookupAttribute_c08bwtk_List_DS”.

The request handler will look as follows (Note, be sure to also add the “@creatio-devkit/common” to the page as sdk):

{
	request: "crt.LoadDataRequest",
	handler: async (request, next) => {
		// filter the contact lookup for the account
					
		if(request.dataSourceName !== "LookupAttribute_ctwt6pv_List_DS") {
			return await next?.handle(request);
		}

		// get the account					
		const account = await request.$context.LookupAttribute_c08bwtk;
		if (account) {
			const filter = new sdk.FilterGroup();
			await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Account", account.value);

			// note, these lines are only needed due to an issue with filters in Creatio-DevKit SDK
			// expected to be fixed in Creatio 8.1
			const newFilter = Object.assign({}, filter);
			newFilter.items = filter.items;

			request.parameters.push({
				type: "filter",
				value: newFilter
			});
		}
					
		return await next?.handle(request);
	}
}

The end result will be that the Contact lookup will only show the contact’s for the Account on the Case.

One side note: As of Creatio 8.0.10, lookups only request their data once. Meaning, the first time the user clicks the dropdown. So, if you set a filter, then the user changes the fields you’re using for the filter, it won’t fire it’s crt.LoadDataRequest request again, allowing you to apply the filter again based on the new values (it will still use the original values). I’m sure this will change in future versions, but this is how it works as of 8.0.10.

Contact Customer FX for your CRM project
We let our expertise speak for itself - let us know how our all-star team can help on your CRM project
About the Author
Ryan Farley
Ryan Farleyhttps://customerfx.com/article/author/ryanfarley/
Ryan Farley is the Director of Development for Customer FX and creator of slxdeveloper.com. He's been blogging regularly about SalesLogix, now Infor CRM, since 2001 and believes in sharing with the community. His new passion for CRM is Creatio, formerly bpm'online. He loves C#, Javascript, web development, open source, and Linux. He also loves his hobby as an amateur filmmaker.

18 COMMENTS

  1. Hi Ryan, thanks for this guide, it’s worth noting that with the somewhat recent ability to configure applications to open lookup modal windows from “dropdown” fields in Freedom UI, that the request for this is different (and actually uses _both_ the one stated in this guide when using that field as a combo box where you type data in and autocomplete, and a different request for when opening the modal window, so code duplication may be required to filter both!) The one to look for is crt.OpenLookupPageRequest, and the filtering would have to be different – either on request.itemAttributeName = or request.itemsAttributeName = _List.

    I would also be interested to know if you’ve found any workarounds for the refreshing of data – I wondered if adding a custom handler for crt.HandleViewModelAttributeChangeRequest to check if fields that impact the lookup filter have been changed and to executeRequest to reload the data source or reapply the filter in some way?

    Thanks for your articles!

    • Thanks for that Harvey. I did know that the newer lookup dialogs did also request the datasource in the same way, but wasn’t aware yet that they data source requests were separate.
      I have not yet found a way to force the lookup to reload the data source yet. At least in current versions, once a lookup requests its data source, it does not trigger that again when re-opened and dependent values have changed. I’ve not found a way to force it to request that again once values change. It is a very big annoyance and hopefully will be changing in upcoming version soon. I do expect this will change, but I can’t find any way around it for now.
      Ryan

  2. Hi Ryan,
    thank you for your guides, they are always clear and informative!

    Do you have any news about the fact that lookups only request their data once?
    I think it is a serious limitation and I was wondering if in the meantime you had found another way to update the lookup or to filter it dynamically.

    Please let me know.
    Thanks!

    Luca

  3. Hi Ryan,

    Thank you so much for this Article of yours it is very helpful as we couldn’t really get that much information related to coding in Creatio Academy. We are currently using version 8.1.1 and is not working. Maybe there is only something wrong with my code.

    handlers: /**SCHEMA_HANDLERS*/[
    {
    request: “crt.LoadDataRequest”,
    handler: async (request, next) => {
    // filter the city lookup for the country

    if(request.dataSourceName !== “PDS_UsrCity_d9j1nkq_List_DS”) {
    return await next?.handle(request);
    }

    // get the country
    const country = await request.$context.PDS_UsrCountry_0sp83x0;
    if (country) {
    const filter = new sdk.FilterGroup();
    await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, “Country”, country.value);

    // note, these lines are only needed due to an issue with filters in Creatio-DevKit SDK
    // expected to be fixed in Creatio 8.1
    const newFilter = Object.assign({}, filter);
    newFilter.items = filter.items;

    request.parameters.push({
    type: “filter”,
    value: newFilter
    });
    }

    return await next?.handle(request);
    }
    }
    ]/**SCHEMA_HANDLERS*/,

    By the way, how you are doing the client side debugging in freedom ui? I tried to set breakpoint inside this “handlers” but it is not hit.

    • The debugger should definitely be hitting there. Make sure that if(request.dataSourceName !== “PDS_UsrCity_d9j1nkq_List_DS”) { is catching the request for the lookup. If it’s a lookup dialog, those didn’t exist when I wrote this article, only dropdowns – I believe the name of the data source is different. Try debugging before the If to inspect the data source names.

      Also, as a side note, on 8.1.1 you don’t need to use the workaround and can omit these lines:

      const newFilter = Object.assign({}, filter);
      newFilter.items = filter.items;

      Then assign filter to the request.parameters directly (but that isn’t causing your issue)

      Ryan

  4. Hi Ryan, thanks for the guide!

    Is there any UPD information how to update the contents of the combobox we are filtering? I implemented filtering according to the guide and it works once (unless we conditionally change the value of the element directly on the page), as you described. I encounter all sorts of difficulties in the new interface and I would like to learn how to solve them

    Thanks in advance!

    • The filter only applies currently on the first load of the lookup data. This is a known issue that I’ve been told will be addressed in a future version (although not sure which version or when)

  5. Harvey,

    Did you get a lookup modal window to filter at all? The `request` parameter does not have a `parameters` attribute (i.e., `request.parameters`), and adding one doesn’t result in a filter that works.

    • Filtering a lookup with a dialog on the page, I believe, should work the same way by filtering the data source. I know in code you can invoke a lookup and apply a filter so I know it works. I just don’t know how that translated into apply the filter to a lookup on the page – I’ve not tried that yet. I’d also assume that using a filter business rule would work, maybe it doesn’t (I assume you’ve tried that already)?

      For invoking the lookup via code and applying a filter, it’s something like this:

      const customFilter = new sdk.FilterGroup();
      await customFilter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Account", someAccountId);
      
      sdk.HandlerChainService.instance.process({
          type: "crt.OpenLookupPageRequest",
          scopes: [...request.scopes],
          $context: request.$context,
          entitySchemaName: "Contact",
          schemaName: 'CustomLookupPage',
          itemAttributeName: 'LookupAttribute_2mnilrq',
          afterClosed: (result) => {
              //
          },
          filtersConfig: {
              filterAttributes: [{
                  name: 'CustomFilter',
                  loadOnChange: false
              }],
              attributesConfig: {
                  CustomFilter: {
                      value: customFilter
                  }
              }
          }
      });
  6. Dear Ryan,

    Why sometimes you use: return next?.handle(request);

    And others like here: return await next?.handle(request);

    Which is the difference, and when use one or the another?

    Thanks in advance
    Julio

    • You can think of that line as being similar to the following in classic pages:

      this.callParent(arguments);

      Where it is placed, and whether you await the results, etc, depends on the behavior you want. For this article where we are setting a filter, we want to make sure the base page handler for loading the lookup’s datasource happens *after* we set the filter conditions, so we’re calling it after. You’ll see different usages of that depending on how you need things to work.

  7. Good afternoon. Could you please tell me what type the field should be to create a multiselect? Here is my version, but it’s not working because I don’t fully understand the structure.

    {
    				"operation": "insert",
    				"name": "ComboBox_93g6gzn",
    				"values": {
    					"layoutConfig": {
    						"column": 1,
    						"row": 2,
    						"colSpan": 1,
    						"rowSpan": 1
    					},
    					"type": "crt.ComboBox",
    					"label": "$Resources.Strings.PDS_GenContactRoles_57k55ma",
    					"labelPosition": "auto",
    					"control": "$PDS_GenContactRoles_57k55ma",
    					"listActions": [],
    					"showValueAsLink": true,
    					"controlActions": []
    				},
    				"parentName": "GridContainer_035sekm",
    				"propertyName": "items",
    				"index": 2
    			},  {
      "request": "usr.OpenLookupRequest",
      "handler": async (request, next) => {
        devkit.HandlerChainService.instance.process({
          type: "crt.OpenLookupPageRequest",
          scopes: [...request.scopes],
          $context: request.$context,
          entitySchemaName: "Contact", // Choose the correct entity schema
          schemaName: 'DefaultLookupPage', // Lookup schema
          itemAttributeName: 'ComboBox_asw7mde', // Item attribute name for multi-select
          afterClosed: (result) => {
            // Handling selected items for multi-select
            const selectedItems = result?.value ?? []; // result.value should contain the selected items
            alert(selectedItems.map(item => item.displayValue).join(", "));
          },
          filtersConfig: {
            filterAttributes: [
              {
                name: 'MyFilter',
                loadOnChange: false
              }
            ],
            attributesConfig: {
              MyFilter: {
                value: {
                  "items": {
                    "29e16d42-36f1-4e04-9029-4321cbb2494d": {
                      "filterType": 1,
                      "comparisonType": 11,
                      "isEnabled": true,
                      "trimDateTimeParameterToDate": false,
                      "leftExpression": {
                        "expressionType": 0,
                        "columnPath": "Name"
                      },
                      "isAggregative": false,
                      "dataValueType": 1,
                      "rightExpression": {
                        "expressionType": 2,
                        "parameter": {
                          "dataValueType": 1,
                          "value": "Super"
                        }
                      }
                    }
                  },
                  "logicalOperation": 0,
                  "isEnabled": true,
                  "filterType": 6,
                  "rootSchemaName": "Contact"
                }
              }
            }
          }
        });
        return next?.handle(request);
      }
    }
  8. Hi Ryan,

    Thank you for the article I found it very useful implementing. I just have question on if it is possible to implement this filter for more than one thing. I got the filter to work to where if a state was selected then it would filter the city based on the selected state but I have not gotten it to work for Countries. I want it to work to where if I selected a country it would filter both the city and the state based on the selected country. Is there anything that I am missing from here

    handlers: /**SCHEMA_HANDLERS*/[ 
            {
                request: "crt.LoadDataRequest",
                handler: async (request, next) => 
                {
                  // filter the contact lookup for the account
                  if(request.dataSourceName !== "AccountAddressDS_City_tlnr1l3_List_DS") 
                  {
                      return await next?.handle(request);
                  }
                  // get the state 
                  const state = await request.$context.AccountAddressDS_Region_vbucljt;
                  if (state) 
                  {
                      const filter = new sdk.FilterGroup();
                      await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Region", state.value);
     
                      // note, these lines are only needed due to an issue with filters in Creatio-DevKit SDK
                      // expected to be fixed in Creatio 8.1
                      const newFilter = Object.assign({}, filter);
                      newFilter.items = filter.items;
                      request.parameters.push({
                          type: "filter",
                          value: newFilter
                      });
                    }
                    return await next?.handle(request);
                  }
                },
                {
                    request: "crt.LoadDataRequest",
                    handler: async (request, next) =>
                        {
                            // filter the contact lookup for the account for City
                            if(request.dataSourceName !== "AccountAddressDS_City_tlnr1l3_List_DS") 
                              {
                                  return await next?.handle(request);
                              }
                            // filter the contact lookup for the account for State
                            if(request.dataSourceName !== "AccountAddressDS_Region_vbucljt_List_DS") 
                              {
                                  return await next?.handle(request);
                              }
    
                            //get the country
                            const country = await request.$context.AccountAddressDS_Country_389yuds;
                            if (country)
                              {
                                const filter = new sdk.FilterGroup();
                                await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Country", country.value);
     
                                // note, these lines are only needed due to an issue with filters in Creatio-DevKit SDK
                                // expected to be fixed in Creatio 8.1
                                const newFilter = Object.assign({}, filter);
                                newFilter.items = filter.items;
                                request.parameters.push({
                                      type: "filter",
                                      value: newFilter
                                    }); 
                                }
                          return await next?.handle(request);
                        }
                }
    • I would combine those into one where you create the filter for both conditions, not separately.

      const filter = new sdk.FilterGroup();
      if (state) {
          await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Region", state.value);
      }
      if (country) {
          await filter.addSchemaColumnFilterWithParameter(sdk.ComparisonType.Equal, "Country", country.value);
      }

LEAVE A REPLY

Please enter your comment!
Please enter your name here

AI Readiness Checklist for CRM Teams: Is Your CRM System Ready for AI?

Before using AI in Creatio, make sure your CRM data, users, processes, and automation foundation are ready. Use this practical AI readiness checklist for CRM teams.

Build Your Creatio AI Skills with the 2026 AI Summer Program

Join Creatio’s free 2026 AI Summer Program to learn how to identify, build, deploy, and manage AI agents.

Creatio 10x Is Coming: Join the Webinar to Explore the Future of AI-Native CRM

Learn what's new in Creatio 10x and how AI-native CRM, agentic AI, and no-code automation are shaping the future of customer relationship management. Join the upcoming webinar and discover what's next.

Creatio Unlimited Pricing Explained: AI Packages, Costs, Pros & Cons

Learn what Creatio’s new Unlimited pricing model includes, what it doesn’t include, how AI Action packages work, and the pros and cons for businesses evaluating Creatio.

Using the AI Prompt to Create Dynamic Folders in Creatio

Using Creatio's AI prompt to create the filter for a dynamic folder works best if you specifically ask for that, and if you are as clear and direct as possible about the filtering, conditions.

Related Articles

AI Readiness Checklist for CRM Teams: Is Your CRM System Ready for AI?

Before using AI in Creatio, make sure your CRM data, users, processes, and automation foundation are ready. Use this practical AI readiness checklist for CRM teams.

Build Your Creatio AI Skills with the 2026 AI Summer Program

Join Creatio’s free 2026 AI Summer Program to learn how to identify, build, deploy, and manage AI agents.

Creatio 10x Is Coming: Join the Webinar to Explore the Future of AI-Native CRM

Learn what's new in Creatio 10x and how AI-native CRM, agentic AI, and no-code automation are shaping the future of customer relationship management. Join the upcoming webinar and discover what's next.

Creatio Unlimited Pricing Explained: AI Packages, Costs, Pros & Cons

Learn what Creatio’s new Unlimited pricing model includes, what it doesn’t include, how AI Action packages work, and the pros and cons for businesses evaluating Creatio.

Related Videos