Ivandt logo

Dropdown Field

Searchable dropdown with type-ahead suggestions from static or remote sources

The dropdown field provides type-ahead suggestions as users type. It supports static options, DataSource queries, and remote API endpoints.

Basic Usage

{
  type: 'dropdown',
  label: 'Product',
  key: 'product',
  order: 0,
  options: [
    { label: 'iPhone 15', value: 'iphone-15' },
    { label: 'Samsung Galaxy S24', value: 'galaxy-s24' }
  ]
}

Properties

Required

  • options - Can be:
    • Static string array: string[]
    • Static array: [{label: string, value: string}]
    • DataSource query: { query: string }
    • Remote API config: IRemoteOptionsConfig

Optional

  • acceptNewValueIfNoOptions (boolean) - Allow custom values when options unavailable (default: true)

Plus all common field properties.

Options Types

Static string options

{
  type: 'dropdown',
  label: 'City',
  key: 'city',
  order: 0,
  options: [
    'New York',
    'Los Angeles',
    'Chicago'
  ]
}

Static object options

{
  type: 'dropdown',
  label: 'City',
  key: 'city',
  order: 0,
  options: [
    { label: 'New York', value: 'ny' },
    { label: 'Los Angeles', value: 'la' },
    { label: 'Chicago', value: 'chi' }
  ]
}

DataSource Query

{
  type: 'dropdown',
  label: 'Product',
  key: 'product',
  order: 0,
  options: {
    query: '.products | map({label:.name, value:.id})'
  }
}

Remote API

{
  type: 'dropdown',
  label: 'Company',
  key: 'company',
  order: 0,
  options: {
    url: '/api/companies/search',
    method: 'GET',
    queryParam: 'q',
    labelField: 'name',
    minQueryLength: 2,
    debounceMs: 300
  }
}

Remote Options Configuration

When using remote options, the dropdown fetches data from your API as users type. This provides type-ahead functionality for large datasets.

Basic Remote Options

{
  type: 'dropdown',
  label: 'Customer',
  key: 'customer',
  order: 0,
  options: {
    url: '/api/customers/search',
    labelField: 'name'
  }
}

Full Configuration

{
  type: 'dropdown',
  label: 'Customer',
  key: 'customer',
  order: 0,
  options: {
    // Required
    url: '/api/customers/search',  // API endpoint (absolute or relative)

    // HTTP Configuration
    method: 'POST',  // 'GET' or 'POST' (default: 'GET')
    queryParam: 'term',  // Query parameter name (default: 'q')
    requestHeaders: {
      'X-Tenant': 'acme'
    },
    requestBodyTemplate: {  // For POST requests only
      includeArchived: false
    },

    // Response Configuration
    responseOptionsPath: 'data.results',  // Path to array in response (default: root)
    labelField: 'fullName',  // Field to use as label (default: 'label')

    // Performance
    minQueryLength: 2,  // Min characters before fetching (default: 2)
    debounceMs: 200,  // Debounce delay in ms (default: 200)
    cacheTtlMs: 300000,  // Cache duration in ms (default: 300000 = 5 min)
    negativeCacheTtlMs: 60000,  // Error cache duration (default: 60000 = 1 min)
    cacheMaxEntries: 300,  // Max cache entries (default: 300)
    maxOptions: 100,  // Max options per response (default: 100)

    // Advanced
    normalizeQueryBeforeSent: 'lowercase',  // 'default', 'lowercase', or 'uppercase'
    timeoutMs: 10000,  // Request timeout (default: 10000)
    retry: 2,  // Retry count for GET requests (default: 2)
    headersAffectingKey: ['Accept-Language', 'X-Tenant'],  // Headers for cache key
    existenceConcurrency: 12  // Parallel validation requests (default: 12)
  }
}

Remote Options Properties

Required

  • url (string) - API endpoint to query for suggestions. Accepts absolute or relative URLs.

HTTP Configuration

  • method ('GET' | 'POST') - HTTP method. For GET, query is sent via URL params. For POST, query is in the JSON body. Default: 'GET'
  • queryParam (string) - Name of the query parameter carrying the search term. For GET: ?{queryParam}={term}. For POST: { [queryParam]: term }. Default: 'q'
  • requestHeaders (object) - Headers included with every request. Include only headers that affect results (e.g., Accept-Language, X-Tenant). Don't place secrets here in browser contexts.
  • requestBodyTemplate (object) - Template object merged into POST request body. The query term is injected under queryParam. Ignored for GET requests.

Response Configuration

  • responseOptionsPath (string) - Path to the array of items in the JSON response using JSON query language. Examples: 'items'response.items, 'data.results'response.data.results. The path is automatically prefixed with . if omitted. If invalid or doesn't resolve to an array, result is treated as empty.
  • labelField (string) - Field name within each item to use as the label. If items are primitives (e.g., string[]), the primitive value is used directly. Empty/whitespace-only labels are filtered out. Default: 'label'

Performance Options

  • minQueryLength (number) - Minimum query length to trigger a remote fetch. Queries shorter than this return [] immediately. Default: 2
  • debounceMs (number) - Debounce interval in milliseconds for type-ahead. Default: 200
  • cacheTtlMs (number) - Time-to-live in milliseconds for positive cache entries. Within this window, identical queries are served from memory. Default: 300000 (5 minutes)
  • negativeCacheTtlMs (number) - Time-to-live in milliseconds for negative cache entries (network/server errors). Prevents request storms on repeated failures. Not applied for AbortError. Default: 60000 (1 minute)
  • cacheMaxEntries (number) - Maximum number of distinct cache keys kept in memory (LRU). Evicts least-recently-used entry when capacity is exceeded. Default: 300
  • maxOptions (number) - Maximum number of options extracted from a single response. Protects UI and memory if server returns large arrays. Default: 100

Advanced Options

  • normalizeQueryBeforeSent ('default' | 'lowercase' | 'uppercase') - Normalization policy applied to query before sending and when comparing labels. The normalized text participates in the cache key to avoid duplicate fetches. Default: 'default'
  • timeoutMs (number) - Request timeout in milliseconds. Default: 10000
  • retry (number) - Retry count for transient failures. Applies to GET requests only. Default: 2
  • headersAffectingKey (string[]) - Header names that affect the shape or contents of results and should be included in the cache key fingerprint (e.g., 'Accept-Language', 'X-Tenant'). Don't include authorization headers.
  • existenceConcurrency (number) - Maximum number of parallel network requests used by the existence-checker. Higher values may increase throughput but can overwhelm network or server. Default: 12

Use Cases

  • Product search with large catalogs
  • Customer/user lookup
  • Location search (cities, addresses)
  • Tag/category selection
  • Any field with many options or remote data

Example

import type { IvtSchema } from '@ivandt/importer';

const schema: IvtSchema = {
  title: 'Order Import',
  publicKey: 'pk_live_xxx',
  sessionToken: 'session_xxx',
  fields: [
    {
      type: 'dropdown',
      label: 'Customer',
      key: 'customer',
      order: 0,
      options: {
        url: '/api/customers/search',
        method: 'GET',
        labelField: 'name',
        minQueryLength: 2
      },
      validators: [
        { type: 'required' }
      ]
    },
    {
      type: 'dropdown',
      label: 'Product',
      key: 'product',
      order: 1,
      options: {
        query: '.products | map({label:.name, value:.sku})'
      }
    }
  ]
};

Remote API Features

  • Debouncing: Prevents excessive API calls as user types
  • Caching: Stores results in memory (LRU + TTL)
  • Deduplication: Prevents duplicate requests for same query
  • Concurrency Control: Limits parallel requests
  • Error Handling: Graceful fallback on API failures

Accept New Values

{
  type: 'dropdown',
  label: 'Tag',
  key: 'tag',
  order: 0,
  options: {
    url: '/api/tags/search',
    labelField: 'name'
  },
  acceptNewValueIfNoOptions: true  // Allow custom values as options
}

When true, users can enter values not in the options list.