Filtering a lookup field by a choice field value (model-driven forms)
Praxisis D365 Wiki · Public reference · Client-specific details redacted
Source: Claude.ai chat (original: lookup-field-filtering-by-choice-field__4b8a9ac1.md)
Added: 2026-10-02
Summary
- The no-code "Filter by related records" option in the form designer works lookup-to-lookup only — not choice-to-lookup.
- For choice-filtered lookups, write a small JS web resource using
addPreSearch+addCustomFilter.addPreSearchruns on every lookup open (always reads current choice value);addCustomFilterfilters within existing views (does NOT replace them — useaddCustomViewfor fully separate view/columns). - No-code alternative: replace the choice field with a lookup to a small Category config table + give the target table the same Category lookup + enable "Filter by related records" declaratively. Trades a field type for a model change — only worth it early in a build.
Key takeaways
- Clear the lookup on choice change (
setValue(null)) to avoid saving a mismatched pair. - If the target table's choice is a global choice shared with the form's field, integer values match directly. Otherwise map them in code.
- Register handlers with "Pass execution context as first parameter" ticked on both.
Learnings / how to apply
- Codify this as a reusable JS web-resource pattern (per-form namespace,
applyFilter+onLoad+onChoiceChange) — standard Praxisis Dynamics UX pattern. - When scoping new forms, prefer the no-code lookup-to-lookup pattern unless the choice is truly immutable (small, closed vocabulary).
Bonus (related in same chat): Volunteer / Event Signup Data Model (Shift / Shift Location / Shift Application)
- Minimum tables: Shift (period/duration template) → Shift Location (junction, carries capacity + status + current signup count) → Shift Application (signup, lookup to Location + Shift Location + applicant details).
- Location/Community must be a table, not a choice — the cascading filter needs lookup-to-lookup.
- A denormalised Location lookup on Shift Application is redundant but needed for the cascading filter. Have a Power Automate flow overwrite it from the parent so it can never drift.
- Don't use a rollup for Signup Count — 12-hr default recalc leaves the slot open long after it's full. Instead use a flow on Shift Application create / status change: count applications on parent with counted statuses → update Signup Count → deactivate at capacity → reactivate on cancellation.
- Concurrency gap: two anonymous submits within the same second both pass. If overbooking by one is unacceptable → add a Pre-Operation plugin on Shift Application create that re-checks count vs. max and throws (Power Pages surfaces the error on the form).
- Anonymous web role table permissions (global scope): Shift Application = Create only, Shift Location = Read, Location = Read. Keep Web API disabled for these tables — anonymous Read + Web API = anyone enumerates everything incl. admin fields.
- Dedupe: alternate key on
(email + shiftlocationid)so a second submit fails, OR a flow that cancels duplicates. - Alternative UX worth considering: 3-page flow (list of Locations → list of Shift Locations for that Location → create form with Shift Location + Location pre-filled from query string). Removes the lookup modal entirely (modals are the weakest part of anonymous forms on mobile), shows availability before commit, needs no filtering config.
Learnings / how to apply
- "Capacity management via flow, not rollup" is the Praxisis default pattern for any real-time inventory / signup / booking.
- Codify the 3-page alternative in a Praxisis Power Pages "Anonymous Signup Flow" starter template — eliminates most of the mobile-UX pain in one design decision.
Need this applied to your own tenant?
Praxisis Sustain Self-Serve builds a private AI Wiki trained on your documents and tickets, with free Tier-2 support when the AI cannot answer. Book a 30-min overview.