Skip to content

Repository files navigation

DependentChoice - PCF Control for Power Apps

A Power Apps Component Framework (PCF) control that provides dynamic filtering for dependent choice/option set fields in Dynamics 365 and Power Apps. Built with React 16.14 and Fluent UI v9.

🌟 Features

  • Dynamic Filtering: Automatically filters dependent choice options based on parent choice selection
  • Flexible Configuration: JSON-based mapping configuration for easy customization
  • Multi-Select Support: Works with both single-select and multi-select option sets
  • Fluent UI Integration: Modern UI with theme support and accessibility
  • Metadata Service: WebAPI-based metadata retrieval with 5-minute caching
  • Auto-Cleanup: Automatically removes invalid selections when parent changes
  • Configuration Validation: Validates JSON configuration on load with user-friendly error dialog
  • Multi-Language Support: Fully localized in 14 languages
  • TypeScript: Fully typed for better development experience
  • Performance Optimized: Uses React hooks and memoization for efficient rendering

🌍 Supported Languages

The control is fully localized and supports the following languages:

  • us English (1033) - English
  • cz Czech (1029) - Čeština
  • dk Danish (1030) - Dansk
  • de German (1031) - Deutsch
  • gr Greek (1032) - Ελληνικά
  • fr French (1036) - Français
  • hu Hungarian (1038) - Magyar
  • it Italian (1040) - Italiano
  • jp Japanese (1041) - 日本語
  • kr Korean (1042) - 한국어
  • pl Polish (1045) - Polski
  • ru Russian (1049) - Русский
  • sk Slovak (1051) - Slovenčina
  • se Swedish (1053) - Svenska
  • ua Ukrainian (1058) - Українська
  • vn Vietnamese (1066) - Tiếng Việt
  • pt Portuguese - Portugal (2070) - Português
  • es Spanish - Spain (3082) - Español

The control automatically displays in the user's language based on their Dynamics 365/Power Apps language settings.

📋 Use Cases

Perfect for scenarios like:

  • Geographic Relationships: Continent → Country → State → City
  • Product Hierarchies: Category → Subcategory → Product
  • Organizational Structures: Department → Team → Role
  • Classification Systems: Industry → Sector → Subsector

🚀 Getting Started

Prerequisites

  • Node.js (v14 or higher)
  • npm or yarn
  • Power Apps CLI (pac)
  • Visual Studio Code (recommended)

Installation

  1. Clone the repository

    git clone https://github.com/aidevme/dependent-choice.git
    cd dependent-choice
  2. Install dependencies

    npm install
  3. Build the control

    npm run build
  4. Run in watch mode (for development)

    npm start watch

🔧 Configuration

Manifest Properties

The control defines three properties in ControlManifest.Input.xml:

  1. dependentChoice (Required, Bound)

    • The dependent choice field that will be filtered
    • Supports: OptionSet and MultiSelectOptionSet
  2. parentChoice (Required, Bound)

    • The parent choice field that drives the filtering
    • Supports: OptionSet and MultiSelectOptionSet
  3. configurationParameters (Input)

    • JSON configuration for mapping parent-to-dependent values
    • Type: Multiple (multi-line text)

Configuration JSON Format

The configurationParameters property expects a JSON string with the following structure:

{
  "mappings": [
    {
      "parentValue": 1,
      "dependentValues": [100, 101, 102, 103]
    },
    {
      "parentValue": 2,
      "dependentValues": [200, 201, 202]
    },
    {
      "parentValue": 3,
      "dependentValues": []
    }
  ]
}

Important: The JSON must be minified (single line) when entering into Power Apps.

Example: Continent-Country Mapping

The repository includes a complete example in DependentChoice/statics/configurationParameters.json that maps 7 continents to 195 countries:

{
  "mappings": [
    {
      "parentValue": 1,
      "dependentValues": [100000002, 100000004, 100000018, ...]
    },
    {
      "parentValue": 2,
      "dependentValues": []
    }
  ]
}

To use this configuration, minify the JSON:

# Use any JSON minifier or paste into Power Apps as a single line
{"mappings":[{"parentValue":1,"dependentValues":[100000002,100000004,...]},{"parentValue":2,"dependentValues":[]}]}

📦 Deployment

Option 1: Deploy to Dataverse

  1. Build the solution

    npm run build
  2. Create solution package

    cd Solution/DependentChoiceSolution
    msbuild /t:restore
    msbuild /p:Configuration=Release
  3. Import to Dataverse

    • Navigate to your Power Apps environment
    • Go to Solutions → Import
    • Select the generated .zip file from Solution/DependentChoiceSolution/bin/Release

Option 2: Use PAC CLI

# Authenticate
pac auth create --url https://your-org.crm.dynamics.com

# Push control
pac pcf push --publisher-prefix aidevme

🎨 Usage in Power Apps

1. Add Fields to Form

  1. Open your Dataverse table form in the form designer
  2. Add the parent choice field (e.g., Continent)
  3. Add the dependent choice field (e.g., Country)

2. Configure the Control

  1. Select the dependent choice field
  2. Click + Component in the properties panel
  3. Select DependentChoice from the list
  4. Configure the properties:
    • dependentChoice: Bind to your dependent field (e.g., Country)
    • parentChoice: Bind to your parent field (e.g., Continent)
    • configurationParameters: Paste your minified JSON configuration

3. Test the Filtering

  1. Save and publish the form
  2. Open a record
  3. Select a value in the parent choice field
  4. Observe the dependent choice field automatically filter

🏗️ Project Structure

dependent-choice/
├── DependentChoice/                                  # Main control directory
│   ├── components/
│   │   ├── DependentChoice.tsx                       # Main React component
│   │   └── DependentChoiceConfigurationErrorDialog.tsx # Error dialog component
│   ├── generated/
│   │   └── ManifestTypes.d.ts                        # Auto-generated TypeScript types
│   ├── hooks/
│   │   └── useTranslation.ts                         # Translation hook for components
│   ├── services/
│   │   ├── index.ts                                  # Service exports
│   │   ├── DependencyMappingService/
│   │   │   └── DependencyMappingService.ts           # Filtering logic
│   │   ├── MetadataService/
│   │   │   └── MetadataService.ts                    # Dataverse metadata retrieval
│   │   └── PcfContextService/
│   │       ├── PcfContext.tsx                        # React context provider
│   │       └── PcfContextService.ts                  # PCF context wrapper
│   ├── statics/
│   │   ├── configurationParameters.json              # Example configuration
│   ├── strings/                                      # Localization files (18 languages)
│   │   ├── DependentChoice.1029.resx                 # Czech
│   │   ├── DependentChoice.1030.resx                 # Danish
│   │   ├── DependentChoice.1031.resx                 # German
│   │   ├── DependentChoice.1032.resx                 # Greek
│   │   ├── DependentChoice.1033.resx                 # English
│   │   ├── DependentChoice.1036.resx                 # French
│   │   ├── DependentChoice.1038.resx                 # Hungarian
│   │   ├── DependentChoice.1040.resx                 # Italian
│   │   ├── DependentChoice.1041.resx                 # Japanese
│   │   ├── DependentChoice.1042.resx                 # Korean
│   │   ├── DependentChoice.1045.resx                 # Polish
│   │   ├── DependentChoice.1049.resx                 # Russian
│   │   ├── DependentChoice.1051.resx                 # Slovak
│   │   ├── DependentChoice.1053.resx                 # Swedish
│   │   ├── DependentChoice.1058.resx                 # Ukrainian
│   │   ├── DependentChoice.1066.resx                 # Vietnamese
│   │   ├── DependentChoice.2070.resx                 # Portuguese (Portugal)
│   │   └── DependentChoice.3082.resx                 # Spanish (Spain)
│   ├── styles/
│   │   └── Styles.ts                                 # Fluent UI styles & hooks
│   ├── tools/
│   │   ├── ConfigurationValidator.ts                 # JSON configuration validator
│   │   └── index.ts                                  # Tool exports
│   ├── ControlManifest.Input.xml                     # PCF manifest
│   ├── DependentChoice.pcfproj                       # MSBuild project file
│   ├── DependentChoiceApp.tsx                        # React app wrapper
│   └── index.ts                                      # PCF control entry point
├── Solution/                                         # Dataverse solution
│   └── DependentChoiceSolution/
│       ├── src/                                      # Solution files
│       │   └── Other/
│       │       ├── Customizations.xml
│       │       ├── Relationships.xml
│       │       └── Solution.xml
│       └── DependentChoiceSolution.cdsproj           # CDS solution project
├── docs/                                             # Documentation
│   ├── api/                                          # API documentation (TypeDoc)
│   ├── blog/                                         # Development blog posts
│   └── todos/                                        # Research & planning docs
├── .github/
│   ├── ISSUE_TEMPLATE/                               # GitHub issue templates
│   ├── prompts/                                      # AI prompts & documentation
│   ├── workflows/                                    # GitHub Actions CI/CD
│   └── copilot-instructions.md                       # Development guidelines
├── out/                                              # Build output directory
├── obj/                                              # Intermediate build files
├── dependent-choice.pcfproj                          # Root MSBuild project
├── eslint.config.mjs                                 # ESLint flat configuration
├── featureconfig.json                                # PCF feature configuration
├── package.json                                      # npm dependencies
├── pcfconfig.json                                    # PCF build configuration
├── tsconfig.json                                     # TypeScript configuration
├── typedoc.json                                      # TypeDoc documentation config
├── CONTRIBUTING.md                                   # Contribution guidelines
├── LICENSE                                           # MIT License
├── README.md                                         # This file
└── SECURITY.md                                       # Security policy

🔍 How It Works

Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│ PCF Control (index.ts)                                      │
│  ├── Reads parent choice value                              │
│  ├── Reads configuration parameters                         │
│  └── Passes props to React app                              │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ DependentChoiceApp.tsx                                      │
│  ├── Creates PcfContextService                              │
│  │   └── Initializes DependencyMappingService               │
│  ├── Provides theme and context to components               │
│  └── Renders DependentChoice component                      │
└──────────────────────┬──────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ DependentChoice.tsx                                         │
│  ├── Monitors parent choice value changes                   │
│  ├── Uses DependencyMappingService to get allowed values    │
│  ├── Filters options based on mapping                       │
│  └── Renders Fluent UI Dropdown with filtered options       │
└─────────────────────────────────────────────────────────────┘

Filtering Logic

  1. Parent Value Change: User selects a value in the parent choice field
  2. Get Mapping: DependencyMappingService.getDependentValues(parentValue) retrieves allowed values
  3. Filter Options: Only options with values in the allowed list are shown
  4. Special Cases:
    • No mapping found → Show all options (fail-safe)
    • Empty array mapping → Show no options (explicit restriction)
    • Currently selected value → Always kept visible even if not in mapping

🛠️ Development

Prerequisites

# Install ESLint (required by pcf-scripts)
npm install

Build Commands

  • npm run build - Full build (validates manifest, runs ESLint, compiles TypeScript, bundles)
  • npm run refreshTypes - Regenerate ManifestTypes.d.ts after editing manifest
  • npm start watch - Development mode with auto-rebuild
  • npm run lint:fix - Auto-fix ESLint issues

Code Style

  • PascalCase: Components, interfaces, classes (DependentChoice, IInputs)
  • camelCase: Variables, functions, properties (handleChange, parentValue)
  • TSDoc: All public APIs must be documented

Debugging

The control includes extensive console logging for troubleshooting:

// Check console for these logs:
"DependencyMappingService: initialize called with:" // Configuration received
"DependencyMappingService: Configuration loaded successfully" // Parsing succeeded
"DependentChoice: Filtering options" // Filter triggered
"DependentChoice: Parent values to check:" // Parent value detected
"DependentChoice: Got dependent values for parent X:" // Allowed values retrieved
"DependentChoice: Filtered options count:" // Final result

📝 Customization

Creating Your Own Mappings

  1. Identify Your Option Values

    // Use browser console in Power Apps form
    // Check the option set values for your fields
  2. Create Configuration

    {
      "mappings": [
        {
          "parentValue": <your_parent_value>,
          "dependentValues": [<dependent_value_1>, <dependent_value_2>, ...]
        }
      ]
    }
  3. Minify JSON

    • Remove all whitespace and newlines
    • Paste as single line into configurationParameters property
  4. Test

    • Save form
    • Test parent-dependent filtering behavior

Extending the Control

To add custom features:

  1. Modify the Component - Edit DependentChoice/components/DependentChoice.tsx
  2. Add New Services - Create in DependentChoice/services/
  3. Update Manifest - Add properties in ControlManifest.Input.xml
  4. Regenerate Types - Run npm run refreshTypes
  5. Rebuild - Run npm run build

🐛 Troubleshooting

Control Not Filtering

Check Console Logs:

  1. Open browser developer console (F12)
  2. Look for initialization logs
  3. Verify configuration is loaded
  4. Check parent value detection

Common Issues:

  • Configuration not provided or malformed JSON
  • Parent value doesn't match mapping keys
  • Option values don't match mapping values

Build Errors

ESLint Not Found:

npm install

Type Errors:

npm run refreshTypes

Cache Issues:

# Clear build artifacts
Remove-Item -Recurse -Force obj, out
npm run build

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

📞 Support

🔗 Related Resources


Made with ❤️ for the Power Platform Community

About

A Power Apps Component Framework (PCF) control that enables cascading (dependent) choice and lookup fields in Dataverse. Dynamically filters available options based on parent field selections, supporting multi-level dependencies, configurable rules, and Fluent UI rendering for model-driven .

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages