Skip to content

Fix M2A display templates from the native picker (#60)#64

Merged
smartlabsAT merged 5 commits into
mainfrom
fix/issue-60-m2a-template-syntax
Jun 1, 2026
Merged

Fix M2A display templates from the native picker (#60)#64
smartlabsAT merged 5 commits into
mainfrom
fix/issue-60-m2a-template-syntax

Conversation

@smartlabsAT
Copy link
Copy Markdown
Owner

Summary

Completes Many-to-Any (M2A) column-display support so templates built with Directus's native display-template picker actually render. Follow-up to the reopened #60 — v0.5.0 shipped M2A but the picker output never resolved.

Root cause

The picker is rooted at the parent collection, so it emits field-key-prefixed tokens — {{treatment.collection}}, {{treatment.item:service.name}} — not the hand-written {{item:...}} form the renderer expected. Confirmed live against Directus 11.11.0 by reading the picker's emitted data-field attributes.

Changes

  • Picker tokens resolve. A shared stripM2AFieldPrefix helper normalises the picker (treatment.item:…) and hand-written (item:…) forms on both the query and render sides, so the two cannot drift.
  • Discriminator-only templates no longer blank the cell. A template without an item: token made the API omit the junction item; the segment guard then treated the resulting undefined like a permission denial and skipped every row. The guard now distinguishes not-fetched (undefined) from genuinely absent (null).
  • Parent-row and junction-level fields. Bare {{code}} resolves against the parent row and {{treatment.sort}} against the junction row, alongside the discriminator and per-collection item: values. On a name clash the most specific (deepest) token wins. Every token is validated against its target before the request, so an unknown token is dropped instead of 403'ing.
  • Refactor. M2A segment building extracted into a pure, unit-tested buildM2ASegments; resolveM2ARelation now reports the junction collection; the collection discriminator token is shared by the query and render sides.
  • README and the column-display editor hint corrected; CHANGELOG updated.

Verification

  • 267 unit tests, type-check, ESLint, Prettier, build — all green.
  • Live (Directus 11.11.0): full native-picker round-trip (pick → save → reload → render) renders correctly; all token scopes resolve; no 403 (a mixed M2A + M2M query returns 200).
  • Regression smoke across collections (translations, M2O, file, M2M, datetime) — unchanged.

Refs #60.

An M2A column display built with the native display-template picker rendered
an empty cell. The picker emits per-collection tokens prefixed with the parent
field name (e.g. `{{treatment:partners_catalog.name}}`), but only the
hand-written `{{item:collection.field}}` form was recognised, so the picker
tokens were dropped on both the query and render sides.

- Add a shared isM2APrefix helper in displayHeuristics that accepts the parent
  field name, the junction item field, and the literal `item` as valid M2A
  token prefixes — matching Directus core's render-template behaviour.
- Use it in expandTokensThroughRelation (query) and renderM2ATemplate (render);
  renderM2ATemplate now takes the field name to resolve the prefix.
- Both token formats now produce identical field paths and output.
The native display-template picker is rooted at the parent collection, so it
emits field-key-prefixed tokens (treatment.collection,
treatment.item:service.name) that the renderer did not recognise, leaving M2A
cells blank. A shared stripM2AFieldPrefix helper normalises the picker and
hand-written forms on both the query and render sides.

Also fixes templates without an item token (e.g. {{collection}}), which blanked
the whole cell: the segment guard now distinguishes a not-fetched item
(undefined) from a permission-denied/dangling one (null). Parent-row ({{code}})
and junction-level ({{treatment.sort}}) fields are now resolvable, the deepest
scope wins on a name clash, and every token is validated before the request to
avoid a 403. M2A segment building is extracted into a unit-tested
buildM2ASegments helper.
Document that the native field picker now works directly, the resolvable token
scopes (item, discriminator, junction-prefixed, bare parent field) and that the
deepest match wins. Removes the now-false "bare token is dropped" and "do not
prefix the field name" notes.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Quality Check Results

TypeScript Type Check

Passed - No type errors found

ESLint

Passed - No linting errors

Prettier Format Check

Passed - Code is properly formatted

Build

Passed - Extension builds successfully


Updated: 2026-06-01T22:54:45.316Z

@smartlabsAT smartlabsAT linked an issue Jun 1, 2026 that may be closed by this pull request
@smartlabsAT smartlabsAT merged commit e24a6e9 into main Jun 1, 2026
18 checks passed
@smartlabsAT smartlabsAT self-assigned this Jun 1, 2026
@smartlabsAT smartlabsAT added the bug Something isn't working label Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M2A relationship is not displayed correctly

1 participant