Overview
The Dropdown component provides a Material Design dropdown for selecting one or multiple options from a list. It supports static options, promise-based loading, and observable-based dynamic data.
When to Use
Use the Dropdown component when you need to:
Let users select from a predefined list of options
Load options from an API or async data source
Enable multi-select for choosing multiple values
Present a large list of choices in a compact space
For searchable dropdowns, consider using AutoComplete . For small sets of mutually exclusive options, use Radio Group .
Basic Usage
import { Dropdown , OptionChild } from 'mat-dynamic-form' ;
const countryDropdown = new Dropdown (
'country' ,
'Country' ,
[
new OptionChild ( 'United States' , 'us' ),
new OptionChild ( 'United Kingdom' , 'uk' ),
new OptionChild ( 'Canada' , 'ca' )
]
);
Common Patterns
Dropdown with Pre-Selected Value
const countryDropdown = new Dropdown (
'country' ,
'Select Country' ,
[
new OptionChild ( 'United States' , 'us' ),
new OptionChild ( 'United Kingdom' , 'uk' ),
new OptionChild ( 'Canada' , 'ca' ),
new OptionChild ( 'Australia' , 'au' )
],
'us' // Pre-select United States
). apply ({
icon: 'public' ,
validator: Validators . required
});
Multi-Select Dropdown
import { Validators } from '@angular/forms' ;
const languagesDropdown = new Dropdown (
'languages' ,
'Languages' ,
[
new OptionChild ( 'English' , 'en' ),
new OptionChild ( 'Spanish' , 'es' ),
new OptionChild ( 'French' , 'fr' ),
new OptionChild ( 'German' , 'de' ),
new OptionChild ( 'Chinese' , 'zh' )
],
null ,
true // Enable multi-select
). apply ({
icon: 'language' ,
hint: 'Select all languages you speak'
});
Loading Options from Promise
const cityDropdown = new Dropdown (
'city' ,
'City' ,
fetch ( '/api/cities' ). then ( response => response . json ())
). apply ({
icon: 'location_city' ,
validator: Validators . required
});
Loading Options from Observable
import { HttpClient } from '@angular/common/http' ;
import { map } from 'rxjs/operators' ;
// In your component:
constructor ( private http : HttpClient ) {
const countryDropdown = new Dropdown (
'country' ,
'Country' ,
this . http . get < any []>( 'https://restcountries.com/v3.1/all' ). pipe (
map ( countries =>
countries . map ( c => new OptionChild ( c . name . common , c . cca2 ))
)
)
). apply ({
icon: 'public' ,
validator: Validators . required
});
}
Dropdown with Value Change Handler
const categoryDropdown = new Dropdown (
'category' ,
'Category' ,
[
new OptionChild ( 'Electronics' , 'electronics' ),
new OptionChild ( 'Clothing' , 'clothing' ),
new OptionChild ( 'Books' , 'books' )
]
). apply ({
action: {
type: 'valueChange' ,
onEvent : ( param ) => {
console . log ( 'Selected category:' , param . event );
// Load subcategories based on selection
this . loadSubcategories ( param . event );
}
}
});
Properties
Unique identifier for the dropdown.
Placeholder text shown when no option is selected.
value
OptionChild[] | Promise<OptionChild[]> | Observable<OptionChild[]>
required
The list of options to display. Can be static array, Promise, or Observable.
The initially selected value (must match an option’s value).
Whether to allow selecting multiple options.
Material icon name to display alongside the dropdown.
validator
ValidatorFn | ValidatorFn[]
Angular validators to apply.
Custom error message shown when validation fails.
Helper text displayed below the dropdown.
Whether the dropdown is disabled.
Whether the dropdown takes up a full row in the form grid.
Methods
getOptions()
Returns the current list of options.
const dropdown = formStructure . getNodeById < Dropdown >( 'country' );
const options = dropdown . getOptions ();
console . log ( options ); // Array of OptionChild
apply()
Applies multiple properties at once.
dropdown . apply ({
icon: 'public' ,
validator: Validators . required ,
hint: 'Select your country'
});
OptionChild Structure
Options are defined using the OptionChild class:
import { OptionChild } from 'mat-dynamic-form' ;
const option = new OptionChild (
'Display Text' , // title - shown to user
'value' // value - stored in form data
);
Example with Descriptive Options
const priorityDropdown = new Dropdown (
'priority' ,
'Priority Level' ,
[
new OptionChild ( 'Low - Can wait' , 'low' ),
new OptionChild ( 'Medium - Normal timeline' , 'medium' ),
new OptionChild ( 'High - Urgent' , 'high' ),
new OptionChild ( 'Critical - Immediate action' , 'critical' )
]
);
Validation Examples
Required Selection
import { Validators } from '@angular/forms' ;
const dropdown = new Dropdown ( 'country' , 'Country' , options ). apply ({
validator: Validators . required ,
errorMessage: 'Please select a country'
});
Custom Validator
import { AbstractControl , ValidationErrors } from '@angular/forms' ;
function notUSValidator ( control : AbstractControl ) : ValidationErrors | null {
if ( control . value === 'us' ) {
return { notAllowed: true };
}
return null ;
}
const dropdown = new Dropdown ( 'country' , 'Country' , options ). apply ({
validator: [ Validators . required , notUSValidator ],
errorMessage: 'US is not available for this service'
});
Dynamic Options Example
Cascading Dropdowns
import { Component } from '@angular/core' ;
import { FormStructure , Dropdown , OptionChild } from 'mat-dynamic-form' ;
@ Component ({
selector: 'app-location-form' ,
template: '<mat-dynamic-form [structure]="formStructure"></mat-dynamic-form>'
})
export class LocationFormComponent {
formStructure : FormStructure ;
constructor () {
this . formStructure = new FormStructure ( 'Select Location' );
this . formStructure . nodeGrid = 2 ;
this . formStructure . nodes = [
new Dropdown (
'country' ,
'Country' ,
[
new OptionChild ( 'United States' , 'us' ),
new OptionChild ( 'Canada' , 'ca' ),
new OptionChild ( 'Mexico' , 'mx' )
]
). apply ({
validator: Validators . required ,
action: {
type: 'valueChange' ,
onEvent : ( param ) => this . onCountryChange ( param . event )
}
}),
new Dropdown ( 'state' , 'State/Province' , []). apply ({
validator: Validators . required ,
disabled: true // Initially disabled
})
];
}
onCountryChange ( countryCode : string ) {
const stateDropdown = this . formStructure . getNodeById < Dropdown >( 'state' );
if ( ! stateDropdown ) return ;
// Load states based on country
const states = this . getStatesForCountry ( countryCode );
stateDropdown . value = states ;
stateDropdown . disabled = false ;
// Reset the state selection
this . formStructure . getControlById ( 'state' )?. setValue ( null );
}
getStatesForCountry ( countryCode : string ) : OptionChild [] {
const statesMap : Record < string , OptionChild []> = {
'us' : [
new OptionChild ( 'California' , 'ca' ),
new OptionChild ( 'New York' , 'ny' ),
new OptionChild ( 'Texas' , 'tx' )
],
'ca' : [
new OptionChild ( 'Ontario' , 'on' ),
new OptionChild ( 'Quebec' , 'qc' ),
new OptionChild ( 'British Columbia' , 'bc' )
],
'mx' : [
new OptionChild ( 'Mexico City' , 'cdmx' ),
new OptionChild ( 'Jalisco' , 'jal' ),
new OptionChild ( 'Nuevo León' , 'nl' )
]
};
return statesMap [ countryCode ] || [];
}
}
Best Practices
Use descriptive option titles - Make it clear what each option represents. Users should understand the choice without additional context.
Keep option lists manageable - For lists with more than 10-15 items, consider using AutoComplete for better user experience.
Handle loading states - When loading options from an API, the dropdown will be empty until data arrives. Consider showing a loading indicator or placeholder.
Use multi-select sparingly - Multi-select dropdowns are harder to use on mobile. If users typically select many options, consider checkboxes instead.
Pre-select sensible defaults - If one option is most common, pre-select it to save users time:new Dropdown ( 'country' , 'Country' , options , 'us' )
Complete Example
import { Component } from '@angular/core' ;
import { Validators } from '@angular/forms' ;
import { HttpClient } from '@angular/common/http' ;
import { FormStructure , Dropdown , OptionChild , Button } from 'mat-dynamic-form' ;
import { map } from 'rxjs/operators' ;
@ Component ({
selector: 'app-registration-form' ,
template: '<mat-dynamic-form [structure]="formStructure"></mat-dynamic-form>'
})
export class RegistrationFormComponent {
formStructure : FormStructure ;
constructor ( private http : HttpClient ) {
this . formStructure = new FormStructure ( 'User Registration' );
this . formStructure . appearance = 'outline' ;
this . formStructure . nodeGrid = 2 ;
this . formStructure . nodes = [
// Static options
new Dropdown (
'role' ,
'Role' ,
[
new OptionChild ( 'Developer' , 'developer' ),
new OptionChild ( 'Designer' , 'designer' ),
new OptionChild ( 'Manager' , 'manager' ),
new OptionChild ( 'Other' , 'other' )
]
). apply ({
icon: 'work' ,
validator: Validators . required ,
singleLine: false
}),
// Dynamic options from API
new Dropdown (
'country' ,
'Country' ,
this . http . get < any []>( 'https://restcountries.com/v3.1/all' ). pipe (
map ( countries =>
countries
. map ( c => new OptionChild ( c . name . common , c . cca2 ))
. sort (( a , b ) => a . title . localeCompare ( b . title ))
)
)
). apply ({
icon: 'public' ,
validator: Validators . required ,
singleLine: false
}),
// Multi-select
new Dropdown (
'skills' ,
'Skills' ,
[
new OptionChild ( 'JavaScript' , 'js' ),
new OptionChild ( 'TypeScript' , 'ts' ),
new OptionChild ( 'Python' , 'python' ),
new OptionChild ( 'Java' , 'java' ),
new OptionChild ( 'C#' , 'csharp' ),
new OptionChild ( 'PHP' , 'php' )
],
null ,
true // multi-select
). apply ({
icon: 'code' ,
hint: 'Select all that apply' ,
singleLine: true
})
];
this . formStructure . validateActions = [
new Button ( 'submit' , 'Submit' , {
style: 'primary' ,
onEvent : ( param ) => this . onSubmit ( param . structure )
}). apply ({
validateForm: true ,
icon: 'check'
})
];
}
onSubmit ( structure : FormStructure ) {
const data = structure . getValue ();
console . log ( 'Form data:' , data );
// data.skills will be an array of selected values
}
}
Radio Group For 3-5 mutually exclusive options
AutoComplete Searchable dropdown with filtering
Checkbox For simple yes/no or on/off toggles
See Also