---
title: Render a linked (chained) dropdown
summary: Emit a `<select>` whose visible options are filtered client-side by a parent field's value.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-09-17
library: SAIF.Framework.Web.Mvc
reference_type: package
operation_id: OP-21
trigger: Razor view calling the helper
---

# Render a linked (chained) dropdown

Emit a `<select>` whose visible options are filtered client-side by a parent field's value. Trigger: Razor view calling the helper.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-21 |
| **Trigger** | Razor view calling the helper |
| **Inputs** | Field name or lambda expression, parent field id, `IEnumerable<LinkedSelectListItem>`, `removeDefault`, html attributes |
| **Outputs** | `MvcHtmlString` `<select>` markup |
| **Primary components** | `LinkedDropdownHelper.LinkedDropdownList` and `LinkedDropdownListFor` |
| **Entry point** | `src/Web.Mvc/UI/Extensions/LinkedDropdownHelper.cs` |

## Steps

1. `LinkedDropdownListFor` converts the lambda to a property name through `ExpressionHelper.GetExpressionText` and delegates to `LinkedDropdownList`.
2. Compute the element `id` by replacing `.` with `_` in the name ([BR-47](../business-rules.md#br-47-generated-element-ids-sanitize-dots-to-underscores)).
3. Build `<select>` with `id`, `name`, and `data-parent-id="{parent}"`, then merge caller html attributes through `ObjectExtensions.MergeAttributes`, which concatenates rather than overwrites by default ([BR-48](../business-rules.md#br-48-caller-html-attributes-merge-by-concatenation)).
4. Add CSS classes `{parent}` and `js-bo-ddl-chained` — the client-side filter hook.*
5. For each item, skip it when `removeDefault` is true and `Value == "-1"` (the conventional "please select" entry); otherwise emit an `<option>` whose CSS class is the item's `LinkValue`, with `value`, inner text, and `selected` when `Selected` is set ([BR-49](../business-rules.md#br-49-linked-dropdown-option-carries-parent-value-as-css-class), [BR-50](../business-rules.md#br-50-default-option-suppressed-when-requested)).
6. `LinkedSelectList.GetListItems` builds those items: it guards `items` non-null through `Enforce`, builds a case-insensitive `HashSet` of selected values, and for each source object evaluates `DataValueField`, `DataTextField`, and `DataLinkedValueField` through `DataBinder.Eval` (returning the object itself when the field expression is empty), marking `Selected` when the value is in the selected set ([BR-51](../business-rules.md#br-51-selected-value-matching-is-case-insensitive)).

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([Razor view calls LinkedDropdownList])
	Id[Compute sanitized element id]
	Build[Build select with data-parent-id]
	Merge[Merge caller html attributes]
	Items[Evaluate GetListItems]
	Filter{removeDefault and value -1?}
	Skip([Skip option])
	Emit[Emit option element]
	EndOk([Return select markup])
	Start --> Id
	Id --> Build
	Build --> Merge
	Merge --> Items
	Items --> Filter
	Filter -->|Yes| Skip
	Filter -->|No| Emit
	Emit --> EndOk
	Skip --> EndOk
```

## Failure and edge-case behavior

- No unexpected failure paths beyond argument-guard exceptions and delegated dependency behavior were identified for this operation.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts. Statements marked with `**` could not be confirmed; treat
> as unknown until verified.


## Sources
- `src/Web.Mvc/UI/Extensions/LinkedDropdownHelper.cs`
