Bind a Dropdown widget by setting OptionsList to a record list from an Aggregate, OptionsText to the display attribute, OptionsValue to the entity identifier, and Variable to your local variable. For dependent dropdowns, filter the second Aggregate by the first selection and retrigger it with Fetch On Demand when the parent selection changes.
Dropdowns and Dependent Dropdowns in OutSystems
The Dropdown widget is one of the most commonly used input controls in OutSystems apps. Getting it right means understanding four properties (OptionsList, OptionsText, OptionsValue, Variable) and how Aggregates feed data into them. This tutorial builds a Country / City cascading dropdown — a common pattern in address forms, filtering UIs, and lookup screens. You will also see the OutSystems UI Dropdown ServerSide pattern, which adds server-side search to handle large datasets.
Prerequisites
- A Reactive Web App with Service Studio open
- Two entities: Country (with Id, Name attributes) and City (with Id, Name, CountryId attributes) created in the Data tab
- Some sample records in both entities so the dropdowns populate during testing
Step-by-step guide
Create the Country Aggregate on your screen
Create the Country Aggregate on your screen
In the Interface tab, double-click your screen to open the Screen Editor. Right-click the screen name in the top of the Widget Tree → 'Fetch data from Database'. In the Aggregate editor that opens, drag the Country entity from the Data tab → Entities → Database into the Sources area. Leave Filters and Sorts empty. Close the Aggregate (click outside or press Escape). Service Studio names it 'GetCountries' and places it as a screen data-fetch. In the Properties Panel for GetCountries, verify Max Records is left blank (returns all countries) and Fetch is set to 'At Start'.
Expected result: A GetCountries Aggregate is listed under your screen in the Widget Tree data section and loads on screen open.
Add and bind the Country Dropdown widget
Add and bind the Country Dropdown widget
In the Toolbox (left panel), find the Dropdown widget and drag it onto the canvas inside a Form widget. In the Properties Panel: - Variable: create a new Local Variable 'SelectedCountryId' of type Country Identifier (select Country from the entity identifier types list) - OptionsList: click the expression editor → select GetCountries.List - OptionsText: Country.Name - OptionsValue: Country.Id The OptionsValue must match the data type of the Variable (Country Identifier). The Dropdown now displays country names and stores the selected country's Id.
1/* Properties Panel values */2Variable: SelectedCountryId (type: Country Identifier)3OptionsList: GetCountries.List4OptionsText: Country.Name5OptionsValue: Country.IdExpected result: The live preview shows a dropdown populated with all country names. Selecting one stores the Id in SelectedCountryId.
Create the City Aggregate with a filter on CountryId
Create the City Aggregate with a filter on CountryId
Right-click the screen again → 'Fetch data from Database'. Drag the City entity into the Sources area. In the Filters tab, click '+' to add a filter: City.CountryId = SelectedCountryId In the Properties Panel for GetCities, set Fetch to 'Only on demand'. This is the Fetch On Demand pattern — the aggregate will NOT run at screen start; instead it only runs when you call it explicitly from a Client Action. This prevents loading all cities at startup.
1/* GetCities Aggregate filter */2City.CountryId = SelectedCountryIdExpected result: A GetCities Aggregate appears on the screen. TrueChange may warn that it is never triggered — you will fix that in the next step.
Trigger the City Aggregate when Country selection changes
Trigger the City Aggregate when Country selection changes
Select the Country Dropdown widget on the canvas. In the Properties Panel, find the Events section → On Change → 'New Client Action'. Service Studio creates 'DropdownCountryOnChange' and opens the Action Flow Editor. Action flow: Start → Data Action GetCities.Fetch → End Drag the GetCities data action (from the screen's data actions list in the Toolbox) into the flow between Start and End. This calls GetCities.Fetch, which re-runs the GetCities Aggregate using the current value of SelectedCountryId (already updated by the time OnChange fires).
1/* DropdownCountryOnChange Client Action flow */2Start3 |4 v5GetCities.Fetch /* Re-runs GetCities aggregate with updated SelectedCountryId */6 |7 v8EndExpected result: When the user selects a country, GetCities re-runs and the city dropdown (added in the next step) updates automatically.
Add and bind the City Dropdown widget
Add and bind the City Dropdown widget
Drag a second Dropdown widget below the Country Dropdown. In the Properties Panel: - Variable: create Local Variable 'SelectedCityId' of type City Identifier - OptionsList: GetCities.List - OptionsText: City.Name - OptionsValue: City.Id Also set the City Dropdown's Enabled property to: SelectedCountryId <> NullIdentifier() This disables the city dropdown until a country is selected, preventing invalid states.
1/* City Dropdown Enabled expression */2SelectedCountryId <> NullIdentifier()34/* Properties */5Variable: SelectedCityId (type: City Identifier)6OptionsList: GetCities.List7OptionsText: City.Name8OptionsValue: City.IdExpected result: The City dropdown is greyed out until a country is chosen. Selecting a country enables the city dropdown and populates it with the matching cities.
Use Dropdown ServerSide for large datasets with search
Use Dropdown ServerSide for large datasets with search
For entities with hundreds or thousands of records (e.g., postal codes, products), replace the standard Dropdown with the OutSystems UI Dropdown ServerSide pattern. In the Interface tab, verify OutSystems UI is referenced (it is included by default). On the canvas, from the Toolbox search for 'Dropdown ServerSide' (under Interaction patterns) and drag it onto the screen. Dropdown ServerSide has different bindings: - OptionsList: bind to GetCities.List (same aggregate) - OptionsText: City.Name - OptionsValue: City.Id - Variable: SelectedCityId - Search enabled by default — it fires an OnSearch event with the search term In the OnSearch event, update a Local Variable 'SearchTerm', add a filter to GetCities: Index(City.Name, SearchTerm, 0, False, True) >= 0, then call GetCities.Fetch.
1/* GetCities filter for search */2City.CountryId = SelectedCountryId3and Index(City.Name, SearchTerm, 0, False, True) >= 0Expected result: The Dropdown ServerSide shows a search box. Typing filters cities server-side without loading all records into the browser.
Complete working example
1/* Screen: AddressForm2 Local Variables:3 SelectedCountryId : Country Identifier = NullIdentifier()4 SelectedCityId : City Identifier = NullIdentifier()5 SearchTerm : Text = ""67 Screen Aggregates:8 GetCountries (Fetch: At Start)9 Source: Country10 Filter: (none)1112 GetCities (Fetch: Only on demand)13 Source: City14 Filter: City.CountryId = SelectedCountryId15 and Index(City.Name, SearchTerm, 0, False, True) >= 016*/1718/* Widget tree (simplified) */19Form20 |21 +-- Dropdown (Country)22 | Variable: SelectedCountryId23 | OptionsList: GetCountries.List24 | OptionsText: Country.Name25 | OptionsValue: Country.Id26 | OnChange: DropdownCountryOnChange27 |28 +-- Dropdown (City)29 Variable: SelectedCityId30 OptionsList: GetCities.List31 OptionsText: City.Name32 OptionsValue: City.Id33 Enabled: SelectedCountryId <> NullIdentifier()3435/* Client Action: DropdownCountryOnChange */36Start37 --> Assign: SelectedCityId = NullIdentifier()38 --> GetCities.Fetch39 --> EndCommon mistakes
Why it's a problem: Setting OptionsValue to Country.Name (Text) but Variable is Country Identifier
How to avoid: OptionsValue must match the data type of Variable. Use Country.Id (Country Identifier) for OptionsValue when Variable is Country Identifier. Type mismatch causes TrueChange error 'Invalid data type'.
Why it's a problem: Forgetting to set Fetch to 'Only on demand' for the dependent aggregate
How to avoid: If GetCities is set to 'At Start', it runs with SelectedCountryId = NullIdentifier() and returns nothing. Set Fetch to 'Only on demand' and trigger it manually in the OnChange Client Action.
Why it's a problem: Calling GetCities.Fetch in a Server Action instead of a Client Action
How to avoid: Screen data-action Fetch calls must be made from Client Actions. Server Actions cannot trigger screen-level aggregate refreshes. Keep the fetch trigger in the OnChange Client Action.
Why it's a problem: Not clearing SelectedCityId when the country changes
How to avoid: If the user selects France (with Paris), then changes to Germany, SelectedCityId still holds Paris's Id. Add Assign: SelectedCityId = NullIdentifier() at the start of DropdownCountryOnChange before calling GetCities.Fetch.
Best practices
- Always set Fetch to 'Only on demand' for dependent aggregates — loading all cities at startup wastes a server round-trip.
- Reset the child selection (SelectedCityId = NullIdentifier()) in the parent's OnChange action to prevent stale values.
- Use the City Identifier type (not Integer) for SelectedCityId — this provides type-safe references and better TrueChange warnings.
- For lookup tables under 200 rows, a standard Dropdown with At Start fetch is fine. Over 500 rows, switch to Dropdown ServerSide.
- Add an empty option label ('Select a country...') by setting the Dropdown's NoSelectionValue and NoSelectionText properties.
- Validate that SelectedCountryId <> NullIdentifier() in your form submit Client Action before calling the Server Action.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a cascading dropdown in OutSystems 11 Reactive Web with Country and City entities. Country has Id and Name. City has Id, Name, and CountryId. Explain step by step how to create dependent dropdowns where selecting a country reloads the city list. Use OutSystems aggregate syntax, Fetch On Demand, and the exact Properties Panel field names (OptionsList, OptionsText, OptionsValue, Variable).
Create a dependent dropdown on my OutSystems screen. I have Country (Id, Name) and City (Id, Name, CountryId) entities. Build: (1) GetCountries aggregate loaded at start, (2) GetCities aggregate with filter City.CountryId = SelectedCountryId and Fetch On Demand, (3) Country Dropdown bound to GetCountries, (4) City Dropdown bound to GetCities, (5) DropdownCountryOnChange Client Action that resets SelectedCityId and calls GetCities.Fetch.
Frequently asked questions
How do I add a 'Please select...' placeholder to a Dropdown?
In the Dropdown Properties Panel, set the NoSelectionValue property to NullIdentifier() (for entity identifiers) and NoSelectionText to 'Please select...'. This adds a blank first option. Combine with validation (SelectedCountryId <> NullIdentifier()) on submit.
Can I pre-select a value in the dropdown when the screen opens?
Yes. Assign a default value to the Local Variable when you create it (e.g., SelectedCountryId = 1) or set it in the screen's OnInitialize Client Action. The Dropdown reads the Variable value on render and pre-selects the matching option.
How do I bind a Dropdown to a Static Entity (like an enum)?
Create a Screen Aggregate with the Static Entity as the source. Bind OptionsList to the aggregate list, OptionsText to the Label attribute, and OptionsValue to the Id attribute. Static Entities work identically to regular entities in Dropdown bindings.
My city dropdown shows nothing even after I select a country. What's wrong?
The most common cause is that GetCities is set to 'At Start' instead of 'Only on demand', which means it ran before SelectedCountryId was set and cached zero results. Change GetCities Fetch to 'Only on demand' and verify the OnChange Client Action calls GetCities.Fetch after updating SelectedCountryId.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation