6666 :field =" actualFieldKey"
6767 :alignment =" align"
6868 />
69- <!-- Use custom RelationalCell for relational fields -->
70- <RelationalCell
71- v-else-if =" isRelationalInterface && !getInterfaceType()?.includes('select')"
72- :value =" value"
73- :field =" actualFieldKey"
74- :item =" item"
75- />
76- <!-- Use custom StatusCell for status field -->
77- <StatusCell
78- v-else-if =" actualFieldKey === 'status' && getInterfaceType() === 'select-dropdown'"
79- :value =" value"
80- :options =" interfaceOptions"
81- :field =" actualFieldKey"
82- :edit-mode =" props.editMode"
83- :align =" props.align"
84- />
85- <!-- Use custom SelectCell for other select-dropdown interfaces -->
86- <SelectCell
87- v-else-if =" getInterfaceType() === 'select-dropdown'"
88- :value =" value"
89- :options =" interfaceOptions"
90- :field =" actualFieldKey"
91- />
92- <!-- Use render-display for other types -->
69+ <!-- ABSOLUTE PRIORITY: User-configured display templates (ALL field types) -->
9370 <render-display
94- v-else
71+ v-if = " field?.display "
9572 :value =" value"
9673 :display =" field?.display"
9774 :options =" field?.displayOptions"
10178 :collection =" field?.collection"
10279 :field =" field?.field"
10380 />
81+ <!-- FALLBACK 1: Custom SelectCell for select-dropdown fields WITHOUT display template -->
82+ <SelectCell
83+ v-else-if =" getInterfaceType() === 'select-dropdown'"
84+ :value =" value"
85+ :options =" interfaceOptions"
86+ :field =" actualFieldKey"
87+ />
88+ <!-- FALLBACK 2: Custom RelationalCell for relational fields WITHOUT display template -->
89+ <RelationalCell
90+ v-else-if =" isRelationalInterface"
91+ :value =" value"
92+ :field =" actualFieldKey"
93+ :item =" item"
94+ />
95+ <!-- FINAL FALLBACK: Raw value display for fields without any special handling -->
96+ <span v-else class =" raw-value" >
97+ {{ value != null ? String(value) : '—' }}
98+ </span >
10499 </template >
105100 </InlineEditPopover >
106101
107- <!-- Display only for relational fields -->
102+ <!-- ABSOLUTE PRIORITY: Display templates for relational fields -->
103+ <div
104+ v-else-if =" field?.display"
105+ class =" editable-cell relational"
106+ :style =" { textAlign: props.align || 'left' }"
107+ >
108+ <!-- Direct Display Value (already rendered in computed) -->
109+ <span class =" template-display" >{{ displayValue }}</span >
110+ </div >
111+
112+ <!-- FALLBACK: Display only for relational fields without display templates -->
108113 <div v-else class =" editable-cell relational" :style =" { textAlign: props.align || 'left' }" >
109- <render-display
110- :value =" displayValue"
111- :display =" field?.display"
112- :options =" field?.displayOptions"
113- :interface =" field?.interface"
114- :interface-options =" field?.interfaceOptions"
115- :type =" field?.type"
116- :collection =" field?.collection"
117- :field =" field?.field"
118- />
114+ <span class =" raw-value" >
115+ {{ displayValue != null ? String(displayValue) : '—' }}
116+ </span >
119117 </div >
120118</template >
121119
122120<script lang="ts" setup>
123- import { computed } from ' vue' ;
121+ import { computed , onBeforeMount , markRaw , ref } from ' vue' ;
124122import type { Field , Item } from ' @directus/types' ;
125123import InlineEditPopover from ' ./InlineEditPopover.vue' ;
126124import BooleanToggleCell from ' ./CellRenderers/BooleanToggleCell.vue' ;
127125import SelectCell from ' ./CellRenderers/SelectCell.vue' ;
128- import StatusCell from ' ./CellRenderers/StatusCell.vue' ;
129126import ImageCell from ' ./CellRenderers/ImageCell.vue' ;
130127import RelationalCell from ' ./CellRenderers/RelationalCell.vue' ;
131128import ColorCell from ' ./CellRenderers/ColorCell.vue' ;
@@ -151,6 +148,21 @@ const emit = defineEmits<{
151148 ' navigate-prev' : [];
152149}>();
153150
151+ // Simple cache for relational objects - only cache on mount to avoid corruption
152+ const relationalCache = ref <Record <string , any >>({});
153+
154+ onBeforeMount (() => {
155+ // Cache relational objects once on mount
156+ if (props .item ) {
157+ Object .keys (props .item ).forEach ((key ) => {
158+ const value = props .item [key ];
159+ if (value && typeof value === ' object' && value !== null ) {
160+ relationalCache .value [key ] = markRaw (value );
161+ }
162+ });
163+ }
164+ });
165+
154166// Computed
155167const primaryKeyField = computed (() => {
156168 return Object .keys (props .item ).find ((key ) => key === ' id' || key .endsWith (' _id' )) || ' id' ;
@@ -206,6 +218,28 @@ const displayValue = computed(() => {
206218 return null ;
207219 }
208220
221+ // Handle relational fields with display templates
222+ const template =
223+ props .field ?.displayOptions ?.template || props .field ?.meta ?.display_options ?.template ;
224+
225+ if (template && props .field ?.display ) {
226+ const relationalValue = props .item [props .fieldKey ];
227+
228+ // If we have an object, use it
229+ if (relationalValue && typeof relationalValue === ' object' ) {
230+ return renderTemplate (relationalValue , template );
231+ }
232+
233+ // If corrupted (primitive value), try cache fallback
234+ const cachedValue = relationalCache .value [props .fieldKey ];
235+ if (cachedValue ) {
236+ return renderTemplate (cachedValue , template );
237+ }
238+
239+ // No data available
240+ return ' —' ;
241+ }
242+
209243 // For other relational fields, use the aliased getter if provided
210244 if (props .getDisplayValue ) {
211245 return props .getDisplayValue (props .item , props .fieldKey );
@@ -335,6 +369,35 @@ function getInterfaceType() {
335369 return props .field ?.interface || props .field ?.meta ?.interface ;
336370}
337371
372+ // Manual Template Rendering - Production Fix for render-display issue
373+ function renderTemplate(value : any , template : string ): string {
374+ if (! template || template === null || template === undefined ) {
375+ // No template - return formatted value
376+ return value != null ? String (value ) : ' —' ;
377+ }
378+
379+ if (! value ) {
380+ return ' —' ;
381+ }
382+
383+ // Handle object values for related fields
384+ if (typeof value === ' object' && value !== null ) {
385+ let result = template ;
386+
387+ // Replace template variables with actual values
388+ Object .keys (value ).forEach ((key ) => {
389+ const regex = new RegExp (` \\ {\\ {\\ s*${key }\\ s*\\ }\\ }` , ' g' );
390+ const fieldValue = value [key ];
391+ result = result .replace (regex , fieldValue != null ? String (fieldValue ) : ' ' );
392+ });
393+
394+ return result ;
395+ }
396+
397+ // Handle simple values - replace all template vars with the same value
398+ return template .replace (/ \{\{ . *? \}\} / g , String (value ));
399+ }
400+
338401function handleUpdate(value : any ) {
339402 const primaryKey = Object .keys (props .item ).find ((key ) => key === ' id' || key .endsWith (' _id' ));
340403
0 commit comments